How to define properties in __init__

I want to define properties in a class from a member function. Below is an example of test code showing how I want this to work. However, I do not get the expected behavior.

class Basket(object): def __init__(self): # add all the properties for p in self.PropNames(): setattr(self, p, property(lambda : p) ) def PropNames(self): # The names of all the properties return ['Apple', 'Pear'] # normal property Air = property(lambda s : "Air") if __name__ == "__main__": b = Basket() print b.Air # outputs: "Air" print b.Apple # outputs: <property object at 0x...> print b.Pear # outputs: <property object at 0x...> 

How can I make this work?

+4
source share
3 answers

You need to set the properties of the class (i.e. self.__class__ ), and not on the object (i.e. self ). For instance:

 class Basket(object): def __init__(self): # add all the properties setattr(self.__class__, 'Apple', property(lambda s : 'Apple') ) setattr(self.__class__, 'Pear', property(lambda s : 'Pear') ) # normal property Air = property(lambda s : "Air") if __name__ == "__main__": b = Basket() print b.Air # outputs: "Air" print b.Apple # outputs: "Apple" print b.Pear # outputs: "Pear" 

What is the use of p when creating lamdas in a loop does not give the behavior you expect. Because the p value changes as the loop passes, the two properties specified in the loop return the same value: the last p value.

+10
source

This does what you wanted:

 class Basket(object): def __init__(self): # add all the properties def make_prop( name ): def getter( self ): return "I'm a " + name return property(getter) for p in self.PropNames(): setattr(Basket, p, make_prop(p) ) def PropNames(self): # The names of all the properties return ['Apple', 'Pear', 'Bread'] # normal property Air = property(lambda s : "I'm Air") if __name__ == "__main__": b = Basket() print b.Air print b.Apple print b.Pear 

Another way to do this would be a metaclass ... but they confuse a lot of people ^^.

Because I am bored:

 class WithProperties(type): """ Converts `__props__` names to actual properties """ def __new__(cls, name, bases, attrs): props = set( attrs.get('__props__', () ) ) for base in bases: props |= set( getattr( base, '__props__', () ) ) def make_prop( name ): def getter( self ): return "I'm a " + name return property( getter ) for prop in props: attrs[ prop ] = make_prop( prop ) return super(WithProperties, cls).__new__(cls, name, bases, attrs) class Basket(object): __metaclass__ = WithProperties __props__ = ['Apple', 'Pear'] Air = property(lambda s : "I'm Air") class OtherBasket(Basket): __props__ = ['Fish', 'Bread'] if __name__ == "__main__": b = Basket() print b.Air print b.Apple print b.Pear c = OtherBasket() print c.Air print c.Apple print c.Pear print c.Fish print c.Bread 
+3
source

Why are you defining properties in __init__ time? This is confusing and smarter, so you have a really good reason. The loop problem that Stef pointed out is just one example of why this should be avoided.

If you need to clarify which properties the subclass has, you can simply do del self.<property name> in the __init__ subclass or define new properties in the subclass.

In addition, some nitpicks styles:

  • Indent to 4 spaces, not 2
  • Do not use quote types unnecessarily.
  • Use underscores instead of the camel case for method names. PropNamesprop_names
  • PropNames does not have to be a method
+1
source