2009-09-21 9 views
4

Je souhaite définir les propriétés d'une classe à partir d'une fonction membre. Voici un code de test montrant comment je voudrais que cela fonctionne. Cependant, je n'obtiens pas le comportement attendu.Comment définir les propriétés dans __init__

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

Comment puis-je faire en sorte que cela fonctionne?

Répondre

10

Vous devez définir les propriétés de la classe (par exemple: self.__class__), pas sur l'objet (par exemple: self). Par exemple:

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" 

Pour ce que ça vaut, votre utilisation de p lors de la création lamdas dans la boucle, ne donne pas le comportement que vous attendez. Puisque la valeur de p est modifiée en passant par la boucle, les deux propriétés définies dans la boucle renvoient toutes les deux la même valeur: la dernière valeur de p.

3

Ce fait ce que vous vouliez:

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 

Une autre façon de le faire serait une métaclasse ... mais ils confondez beaucoup de gens ^^.

Parce que je me ennuie:

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

Je suis en désaccord que la bonne façon de le faire serait une métaclasse. Les métaclasses sont trop puissantes pour cela, car cela peut être accompli en utilisant des moyens moins puissants. –

+0

Merci pour la mise à jour. En fait, je ne suis jamais allé aussi loin que Metaclasses en Python. Peut-être que c'est une bonne raison de commencer à lire à leur sujet. – pkit

1

Pourquoi êtes-vous à la définition des propriétés __init__ temps? C'est déroutant et intelligent, alors vous feriez mieux d'avoir une très bonne raison. Le problème de boucle signalé par Stef n'est qu'un exemple des raisons pour lesquelles cela devrait être évité.

Si vous devez redéfinir les propriétés d'une sous-classe, vous pouvez simplement faire del self.<property name> dans la méthode sous-classe __init__ ou définir de nouvelles propriétés dans la sous-classe.

En outre, un certain style nitpicks:

  • à 4 places Indentation, pas 2
  • Ne pas mélanger les types de devis inutilement
  • Utiliser au lieu de cas souligne de chameau pour les noms de méthode. PropNames ->prop_names
  • PropNames n'a pas vraiment besoin d'être une méthode
+0

La raison principale était que je pouvais hériter de Basket et redéfinir PropNames. P.S. Le code d'exemple original était réellement indenten 4 espaces mais quand je l'ai copié à SO il a perdu l'indentation ainsi j'ai re-indenté sur 2 espaces. – pkit

+0

@pkit: réponse éditée pour parler de sous-classe. –

Questions connexes