What I would like to do there is declaring class variables, but actually use them as vars of the instance. I have a class Field
and a class Thing
, like this:
class Field(object):def __set__(self, instance, value):for key, v in vars(instance.__class__).items():if v is self:instance.__dict__.update({key: value})def __get__(self, instance, owner):for key, v in vars(instance.__class__).items():if v is self:try:return instance.__dict__[key]except:return Noneclass Thing(object):foo = Field()
So when I instantiate a thing and set attribute foo, it will be added to the instance, not the class, the class variable is never actually re-set.
new = Thing()
new.foo = 'bar'
# (foo : 'bar') is stored in new.__dict__
This works so far, but the above code for Field is rather awkward. It has too look for the Field object instance in the classes props, otherwise there seems no way of knowing the name of the property (foo
) in __set__
and __get__
. Is there another, more straight forward way to accomplish this?
Every instance of Field (effectively) has a name. Its name is the attribute name (or key) which references it in Thing
. Instead of having to look up the key dynamically, you could instantiate Field
s with the name at the time the class attribute is set in Thing
:
class Field(object):def __init__(self, name):self.name = namedef __set__(self, instance, value):instance.__dict__.update({self.name: value})def __get__(self, instance, owner):if instance is None:return selftry:return instance.__dict__[self.name]except KeyError:return Nonedef make_field(*args):def wrapper(cls):for arg in args:setattr(cls, arg, Field(arg))return clsreturn wrapper@make_field('foo')
class Thing(object):pass
And it can be used like this:
new = Thing()
Before new.foo
is set, new.foo
returns None:
print(new.foo)
# None
After new.foo
is set, 'foo'
is an instance attribute of new
:
new.foo = 'bar'
print(new.__dict__)
# {'foo': 'bar'}
You can access the descriptor (the Field
instance itself) with Thing.foo
:
print(Thing.foo)
# <__main__.Field object at 0xb76cedec>
PS. I'm assuming you have a good reason why
class Thing(object):foo = None
does not suffice.