How to define properties in __init__

前端 未结 3 1018

I whish to define properties in a class from a member function. Below is some test code showing how I would like this to work. However I don\'t get the expected behaviour.

相关标签:
3条回答
  • 2021-01-05 20:39

    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 it would be a metaclass ... but they confuse a lot of people ^^.

    Because I'm 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 
    
    0 讨论(0)
  • 2021-01-05 20:57

    You need to set the properties on the class (ie: self.__class__), not on the object (ie: self). For example:

    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"
    

    For what it's worth, your usage of p when creating lamdas in the loop, doesn't give the behavior that you would expect. Since the value of p is changed while going through the loop, the two properties set in the loop both return the same value: the last value of p.

    0 讨论(0)
  • 2021-01-05 20:58

    Why are you defining properties at __init__ time? It's confusing and clever, so you better 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 redifine which properties a subclass has, you can just do del self.<property name> in the subclass __init__ method, or define new properties in the subclass.

    Also, some style nitpicks:

    • Indent to 4 spaces, not 2
    • Don't mix quote types unnecessarily
    • Use underscores instead of camel case for method names. PropNames -> prop_names
    • PropNames doesn't really need to be a method
    0 讨论(0)
提交回复
热议问题