How to add property to a class dynamically?
The goal is to create a mock class which behaves like a db resultset.
So for example, if a database query returns, using a dict expression, {'ab':100, 'cd':200}
, then I would like to see:
>>> dummy.ab
100
At first I thought maybe I could do it this way:
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
but c.ab
returns a property object instead.
Replacing the setattr
line with k = property(lambda x: vs[i])
is of no use at all.
So what is the right way to create an instance property at runtime?
P.S. I am aware of an alternative presented in How is the __getattribute__
method used?
Solution 1:
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):
... pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4
A 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 __getattribute__
.
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 __get__
, __set__
, or __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 fget
, fset
, or fdel
you passed to the property
constructor.
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 property
.
>>> class Foo(object):
... def bar(self):
... pass
...
>>> Foo().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.
Solution 2:
The goal is to create a mock class which behaves like a db resultset.
So what you want is a dictionary where you can spell a['b'] as a.b?
That's easy:
class atdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
Solution 3:
You don't need to use a property for that. Just override __setattr__
to make them read only.
class C(object):
def __init__(self, keys, values):
for (key, value) in zip(keys, values):
self.__dict__[key] = value
def __setattr__(self, name, value):
raise Exception("It is read only!")
Tada.
>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
Exception: It is read only!
Solution 4:
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 namedtuple
Foo = namedtuple('Foo', ['bar', 'quux'])
foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux
foo2 = 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.
Solution 5:
How to add property to a python class dynamically?
Say you have an object that you want to add a property to. Typically, I want to use properties when I need to begin managing access to an attribute in code that has downstream usage, so that I can maintain a consistent API. Now I will typically add them to the source code where the object is defined, but let's assume you don't have that access, or you need to truly dynamically choose your functions programmatically.
Create a class
Using an example based on the documentation for property
, let's create a class of object with a "hidden" attribute and create an instance of it:
class C(object):
'''basic class'''
_x = None
o = C()
In Python, we expect there to be one obvious way of doing things. However, in this case, I'm going to show two ways: with decorator notation, and without. First, without decorator notation. This may be more useful for the dynamic assignment of getters, setters, or deleters.
Dynamic (a.k.a. Monkey Patching)
Let's create some for our class:
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
And now we assign these to the property. Note that we could choose our functions programmatically here, answering the dynamic question:
C.x = property(getx, setx, delx, "I'm the 'x' property.")
And usage:
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:
I'm the 'x' property.
Decorators
We could do the same as we did above with decorator notation, but in this case, we must name the methods all the same name (and I'd recommend keeping it the same as the attribute), so programmatic assignment is not so trivial as it is using the above method:
@property
def x(self):
'''I'm the 'x' property.'''
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
And assign the property object with its provisioned setters and deleters to the class:
C.x = x
And usage:
>>> help(C.x)
Help on property:
I'm the 'x' property.
>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None