how to add property "dynamically"?

Rafe rafesacks at gmail.com
Tue Aug 19 14:02:29 EDT 2008


On Aug 17, 5:09 pm, Bruno Desthuilliers
<bdesth.quelquech... at free.quelquepart.fr> wrote:
> 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.

I posted this to another thread, but...


You can dynamically add properties (or anything else) to a CLASS just
before returning the
instance using __new__():

class AClass(object):
    def __new__(cls):
        setattr(cls,"propName", property(fget = ...,
                                         fset = ...,
                                         fdel = ...,
                                         doc  = ...) )

        obj = super(AClass, cls).__new__(cls)
        return obj


- Rafe



More information about the Python-list mailing list