how to add property "dynamically"?

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Sun Aug 17 06:09:43 EDT 2008


akonsu a écrit :
> hello,
> 
> i need to add properties to instances  dynamically during run time.
> this is because their names are determined by the database contents.
> so far i found a way to add methods on demand:
> 
> class A(object) :
>     def __getattr__(self, name) :
>         if name == 'test' :
>             def f() : return 'test'
>             setattr(self, name, f)
>             return f
>         else :
>             raise AttributeError("'%s' object has no attribute '%s'" %
> (self.__class__.__name__, name))
 > this seems to work and i can invoke method test() on an object.

Nope. This adds per-instance *function* attributes - not *methods*.

class A(object) :
     def __getattr__(self, name) :
         if name == 'test' :
             def f(self) :
                 return "%s.test" % self
             setattr(self, name, f)
             return f
         else :
             raise AttributeError(
                 "'%s' object has no attribute '%s'" \
                 % (self.__class__.__name__, name)
                 )


a = A()
a.test()
=> Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: f() takes exactly 1 argument (0 given)


To add methods on a per-instance basis, you have to manually invoke the 
descriptor protocol's implementation of function objects:

class A(object) :
     def __getattr__(self, name) :
         if name == 'test' :
             def f(self) :
                 return "%s.test" % self
             m = f.__get__(self, type(self))
             setattr(self, name, m)
             return m
         else :
             raise AttributeError(
                 "'%s' object has no attribute '%s'" \
                 % (self.__class__.__name__, name)
                 )


> it
> would be nice to have it as property though.   so i tried:
> 
> class A(object) :
>     def __getattr__(self, name) :
>         if name == 'test' :
>             def f() : return 'test'
>             setattr(self, name, property(f))
>             return f
>         else :
>             raise AttributeError("'%s' object has no attribute '%s'" %
> (self.__class__.__name__, name))
> 
> but this does not work, instance.test returns a callable but does not
> call it.

Properties must be class attributes. The only way (the only way I know) 
to get them to work as instance-attributes is to overload 
__getattribute__, which is tricky and may have pretty bad impact on 
lookup perfs - and ruins the whole point of using properties FWIW.

> i am not an expert in python, would someone please tell me what i am
> doing wrong?

Wrong solution to your problem, I'd say. Let's start again:

"""
 > i need to add properties to instances dynamically during run time.
 > this is because their names are determined by the database contents.
"""

Care to elaborate ? I may be wrong, but I suspect you're trying to roll 
your own python/database mapper. If so, there are quite a couple Python 
ORMs around. Else, please tell us more.



More information about the Python-list mailing list