Dynamic property names on class

B

Bryan

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?
 
D

Diez B. Roggisch

Bryan said:
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
 
B

Bryan

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.
 
D

Diez B. Roggisch

Bryan said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top