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\'
The best way to achieve is by defining __slots__
. That way your instances can't have new attributes.
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
__slots__ = []
def __init__(self, ks, vs): self.update(zip(ks, vs))
def __getattr__(self, key): return self[key]
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
That prints 12
c.ab = 33
That gives: AttributeError: 'C' object has no attribute 'ab'
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.
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!
Here is a solution that:
After the class has been defined, you just do this to add a property to it dynamically:
setattr(SomeClass, 'propertyName', property(getter, setter))
Here is a complete example, tested in Python 3:
#!/usr/bin/env python3
class Foo():
pass
def get_x(self):
return 3
def set_x(self, value):
print("set x on %s to %d" % (self, value))
setattr(Foo, 'x', property(get_x, set_x))
foo1 = Foo()
foo1.x = 12
print(foo1.x)
For those coming from search engines, here are the two things I was looking for when talking about dynamic properties:
class Foo:
def __init__(self):
# we can dynamically have access to the properties dict using __dict__
self.__dict__['foo'] = 'bar'
assert Foo().foo == 'bar'
# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
def __init__(self):
self._data = {}
def __getattr__(self, key):
return self._data[key]
def __setattr__(self, key, value):
self._data[key] = value
bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'
__dict__
is good if you want to put dynamically created properties. __getattr__
is good to only do something when the value is needed, like query a database. The set/get combo is good to simplify the access to data stored in the class (like in the example above).
If you only want one dynamic property, have a look at the property() built-in function.
You cannot add a new property()
to an instance at runtime, because properties are data descriptors. Instead you must dynamically create a new class, or overload __getattribute__
in order to process data descriptors on instances.