changing local namespace of a function

Nick Coghlan ncoghlan at iinet.net.au
Sat Feb 5 03:56:48 EST 2005


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   |   ncoghlan at email.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://boredomandlaziness.skystorm.net



More information about the Python-list mailing list