How to add property to a class dynamically?
I suppose I should expand this answer, now that I'm older and wiser and know what's going on. Better late than never.
You can add a property to a class dynamically. But that's the catch: you have to add it to the class.
class Foo(object): passfoo = Foo() foo.a = 3Foo.b = property(lambda self: self.a + 1) foo.b4
property is actually a simple implementation of a thing called a descriptor. It's an object that provides custom handling for a given attribute, on a given class. Kinda like a way to factor a huge
if tree out of
When I ask for
foo.b in the example above, Python sees that the
b defined on the class implements the descriptor protocol—which just means it's an object with a
__delete__ method. The descriptor claims responsibility for handling that attribute, so Python calls
Foo.b.__get__(foo, Foo), and the return value is passed back to you as the value of the attribute. In the case of
property, each of these methods just calls the
fdel you passed to the
Descriptors are really Python's way of exposing the plumbing of its entire OO implementation. In fact, there's another type of descriptor even more common than
class Foo(object): def bar(self): passFoo().bar<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>> Foo().bar.__get__<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>
The humble method is just another kind of descriptor. Its
__get__ tacks on the calling instance as the first argument; in effect, it does this:
def __get__(self, instance, owner): return functools.partial(self.function, instance)
Anyway, I suspect this is why descriptors only work on classes: they're a formalization of the stuff that powers classes in the first place. They're even the exception to the rule: you can obviously assign descriptors to a class, and classes are themselves instances of
type! In fact, trying to read
Foo.bar still calls
property.__get__; it's just idiomatic for descriptors to return themselves when accessed as class attributes.
I think it's pretty cool that virtually all of Python's OO system can be expressed in Python. :)
Oh, and I wrote a wordy blog post about descriptors a while back if you're interested.
It seems you could solve this problem much more simply with a
namedtuple, since you know the entire list of fields ahead of time.
from collections import namedtupleFoo = namedtuple('Foo', ['bar', 'quux'])foo = Foo(bar=13, quux=74)print foo.bar, foo.quuxfoo2 = Foo() # error
If you absolutely need to write your own setter, you'll have to do the metaprogramming at the class level;
property() doesn't work on instances.