Bo Peng wrote:
I guess I will go with solution 3. It is evil but it is most close to my original intention. It leads to most readable code (except for the first line to do the magic and the last line to return result) and fastest performance.

Thousands of programs use Python's class attribute access syntax day-in and day-out and find the performance to be acceptable.


Premature optimization is the root of much evil - the exec + locals() hack if very dependent on the exact Python version you are using. Expect some hard to find bugs if you actually use the hack.

And code which relies on an evil hack in order to have the desired effect doesn't count as readable in my book - I would be very surprised if many people reading your code knew that updates to locals() can sometimes be made to work by performing them inside a bare exec statement.

Thank again for everyone's help. I have learned a lot from the posts, especially the wrapdict class.

It sounds like the dictionaries you are working with actually have some meaningful content that you want to manipulate.


I'd seriously suggest creating a class which is able to extract the relevant data from the dictionaries, supports the desired manipulations, and then be instructed to update the original dictionaries.

For example, an approach based on Michael's dictionary wrapper class

class DataAccessor(object):
  # Just a different name for Michael's "wrapbigdict"
  def __init__(self, data):
    object.__setattr__(self, "_data", data)
  def __getattr__(self, attrname):
    return self._data[attrname]
  def __setattr__(self, attrname, value):
    self._data[attrname] = value

class DataManipulator(DataAccessor):
  def __init__(self, data):
    DataAccessor.__init__(self, data)
  def calc_z(self):
    self.z = self.x + self.y

In action:

Py> data = DataManipulator(dict(x=1, y=2))
Py> data.z
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 6, in __getattr__
KeyError: 'z'
Py> data.calc_z()
Py> data.z
3
Py> data._data
{'y': 2, 'x': 1, 'z': 3}

The class based approach also gives you access to properties, which can be used to make that first call to 'z' automatically calculate the desired result instead of giving a KeyError:

class EnhancedDataManipulator(DataAccessor):
  def __init__(self, data):
    DataAccessor.__init__(self, data)

  class _utils(object):

    @staticmethod
    def make_prop(name, calculator):
      def get(self, _name=name, _calculator=calculator):
        try:
          return self._data[_name]
        except KeyError:
          val = _calculator(self)
          self._data[_name] = val
          return val
      def set(self, val, _name=name):
        self._data[_name] = val
      def delete(self, _name=name):
        del self._data[_name]
      return property(get, set, delete)

    @staticmethod
    def calc_z(self):
      return self.x + self.y

  z = _utils.make_prop('z', _utils.calc_z)

Note the nested _utils class isn't strictly necessary. I just like to use it to separate out the information which the class uses to construct itself from those which are provided to clients of the class.

Anyway, the enhanced version in action:

Py> data = EnhancedDataManipulator(dict(x=1,y=2))
Py> data.x
1
Py> data.y
2
Py> data.z
3
Py> data.x = 30
Py> data.z
3
Py> data.z = 10
Py> data.z
10
Py> del data.z
Py> data.z
32

If you want to add more calculated properties to the data manipulator, simply define additional calculator methods, and define the attribute with make_prop.

Use a class. Giving meaningful form to a collection of attributes is what they're for, and Python provides many features designed to make that process easier.

Trying to fake it with function namespaces is just going to make trouble for you in the future.

Cheers,
Nick.

--
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---------------------------------------------------------------
            http://boredomandlaziness.skystorm.net
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to