Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Python (http://www.velocityreviews.com/forums/f43-python.html)
-   -   Dynamic property names on class (http://www.velocityreviews.com/forums/t705179-dynamic-property-names-on-class.html)

Bryan 11-13-2009 04:59 PM

Dynamic property names on class
 
I have several properties on a class that have very similar behavior.
If one of the properties is set, all the other properties need to be
set to None. So I wanted to create these properties in a loop like:

class Test(object):
for prop in ['foo', 'bar', 'spam']:
# Attribute that data is actually stored in
field = '_' + prop
# Create getter/setter
def _get(self):
return getattr(self, field)
def _set(self, val):
setattr(self, field, val)
for otherProp in prop:
if otherProp != prop: setattr(self, '_' + otherProp, None)
# Assign property to class
setattr(Test, prop, property(_get, _set))

t = Test()
t.foo = 1
assert t.bar == t.spam == None

But the class Test is not defined yet, so I can't set a property on
it. How can I do this?

Diez B. Roggisch 11-13-2009 05:34 PM

Re: Dynamic property names on class
 
Bryan schrieb:
> I have several properties on a class that have very similar behavior.
> If one of the properties is set, all the other properties need to be
> set to None. So I wanted to create these properties in a loop like:
>
> class Test(object):
> for prop in ['foo', 'bar', 'spam']:
> # Attribute that data is actually stored in
> field = '_' + prop
> # Create getter/setter
> def _get(self):
> return getattr(self, field)
> def _set(self, val):
> setattr(self, field, val)
> for otherProp in prop:
> if otherProp != prop: setattr(self, '_' + otherProp, None)
> # Assign property to class
> setattr(Test, prop, property(_get, _set))
>
> t = Test()
> t.foo = 1
> assert t.bar == t.spam == None
>
> But the class Test is not defined yet, so I can't set a property on
> it. How can I do this?


With a metaclass, or a post-class-creation function. Which is a
metaclass without being fancy.

Just put your above code into a function with the class in question as
argument, and invoke it after Test is defined.

Diez

Bryan 11-13-2009 06:20 PM

Re: Dynamic property names on class
 
On Nov 13, 9:34*am, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
> Bryan schrieb:
>
>
>
> > I have several properties on a class that have very similar behavior.
> > If one of the properties is set, all the other properties need to be
> > set to None. *So I wanted to create these properties in a loop like:

>
> > class Test(object):
> > * *for prop in ['foo', 'bar', 'spam']:
> > * * * * * *# Attribute that data is actually stored in
> > * * * * * *field = '_' + prop
> > * * * * * *# Create getter/setter
> > * * * * * *def _get(self):
> > * * * * * * * * * *return getattr(self, field)
> > * * * * * *def _set(self, val):
> > * * * * * * * * * *setattr(self, field, val)
> > * * * * * * * * * *for otherProp in prop:
> > * * * * * * * * * * * * * *if otherProp != prop: setattr(self, '_' + otherProp, None)
> > * * * * * *# Assign property to class
> > * * * * * *setattr(Test, prop, property(_get, _set))

>
> > t = Test()
> > t.foo = 1
> > assert t.bar == t.spam == None

>
> > But the class Test is not defined yet, so I can't set a property on
> > it. *How can I do this?

>
> With a metaclass, or a post-class-creation function. Which is a
> metaclass without being fancy.
>
> Just put your above code into a function with the class in question as
> argument, and invoke it after Test is defined.
>
> Diez


I think there are some closure issues with this as I am getting very
strange results. I think all properties have the getter/setters of
whatever the last item in the list was.
t.foo = 'settingFoo' actually sets t.spam, as 'spam' was the last
property generated.

Diez B. Roggisch 11-13-2009 07:46 PM

Re: Dynamic property names on class
 
Bryan schrieb:
> On Nov 13, 9:34 am, "Diez B. Roggisch" <de...@nospam.web.de> wrote:
>> Bryan schrieb:
>>
>>
>>
>>> I have several properties on a class that have very similar behavior.
>>> If one of the properties is set, all the other properties need to be
>>> set to None. So I wanted to create these properties in a loop like:
>>> class Test(object):
>>> for prop in ['foo', 'bar', 'spam']:
>>> # Attribute that data is actually stored in
>>> field = '_' + prop
>>> # Create getter/setter
>>> def _get(self):
>>> return getattr(self, field)
>>> def _set(self, val):
>>> setattr(self, field, val)
>>> for otherProp in prop:
>>> if otherProp != prop: setattr(self, '_' + otherProp, None)
>>> # Assign property to class
>>> setattr(Test, prop, property(_get, _set))
>>> t = Test()
>>> t.foo = 1
>>> assert t.bar == t.spam == None
>>> But the class Test is not defined yet, so I can't set a property on
>>> it. How can I do this?

>> With a metaclass, or a post-class-creation function. Which is a
>> metaclass without being fancy.
>>
>> Just put your above code into a function with the class in question as
>> argument, and invoke it after Test is defined.
>>
>> Diez

>
> I think there are some closure issues with this as I am getting very
> strange results. I think all properties have the getter/setters of
> whatever the last item in the list was.
> t.foo = 'settingFoo' actually sets t.spam, as 'spam' was the last
> property generated.


That's a FAQ. Closures capture the *names*, not the values. There are
various options to remedy this, e.g. by something like this:


def gen_property(prop):

def _get(...) # your code


return property(_get, _set)

setattr(Test, prop, gen_property(prop))


Diez


All times are GMT. The time now is 10:51 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.