2017-09-13 1 views
3

Le titre n'est peut-être pas la meilleure description de ce que j'essaie de faire mais je ne sais pas trop comment appeler ça. Je suis tombé sur divers concepts apparemment apparentés avec des noms comme "décorateurs", "descripteurs", et "métaclasses" mais je ne sais pas laquelle de ces approches (le cas échéant) je devrais enquêter plus loin!Définition dynamique de méthodes avec des noms dérivés d'un attribut de classe?

Compte tenu des classes suivantes:

class AnimalGreeter(object): 

    def __init__(self, greeting): 
     self.greeting = greeting 

    def greet(self, animal): 
     print(self.greeting + ", " + animal + "!") 

class MyGreeter(AnimalGreeter): 

    animals = ['dog', 'parrot'] 

Je voudrais être en mesure d'utiliser une instance de MyGreeter comme ceci:

greeter = MyGreeter("What's up") 
greeter.parrot 
# "What's up, parrot!" 

En effet, je voudrais qu'il fonctionne comme si je avait défini MyGreeter comme ceci:

class MyGreeter(AnimalGreeter): 

    @property 
    def dog(self): 
     self.greet('dog') 

    @property 
    def parrot(self): 
     self.greet('parrot') 

En En d'autres termes, je veux définir dynamiquement des méthodes (dog(), etc.) avec des noms dérivés d'un attribut (animals) de la classe (MyGreeter).

Quelle est la meilleure façon de faire cela? Merci beaucoup!

+2

Tout mettre en œuvre '__getattr __ (self, animal)'. – AChampion

+0

Cant aide ici, mais je suis intéressé par votre cas d'utilisation. Pourquoi avez-vous besoin de ce comportement? – FabienP

+1

@FabienP, Dans mon cas 'AnimalGreeter' est un modèle Django et' MyGreeter' est un mixin personnalisé qui gère les images avec des noms de fichiers configurables. Je veux avoir accès aux urls de l'image dans les templates de Django mais le système de templates de Django ne permet pas de passer des arguments aux méthodes d'instance. Donc je ne peux pas faire l'équivalent de 'model.image_url ('large')' dans le template. Mais je peux accéder aux propriétés des instances dans les modèles, par exemple 'model.large_image_url'. – tino

Répondre

4

Un moyen facile de faire cela serait juste de mettre en œuvre __getattr__(), .: par exemple

class MyGreeter(AnimalGreeter): 
    animals = {'dog', 'parrot'} 

    def __getattr__(self, animal): 
     if animal in self.animals: 
      return self.greet(animal) 
     raise AttributeError("'{}' object unknown animal '{}'".format(type(self).__name__, animal)) 

>>> greeter = MyGreeter("What's up") 
>>> greeter.parrot 
What's up, parrot! 
>>> greeter.cat 
... 
AttributeError: 'MyGreeter' object unknown animal 'cat' 
+0

Merci! C'était l'une des suggestions que j'ai rencontrées, mais je ne savais pas si je devais être préoccupé par les frais généraux. 'AnimalGreeter' a beaucoup d'autres attributs auxquels on accède fréquemment. Est-ce que cela est exécuté chaque fois que j'accède à un attribut d'instance? – tino

+2

@tino Non, '__getattr__' n'est appelé que si vous accédez à un attribut qui" n'existe pas ", c'est-à-dire qu'il est appelé lorsqu'une AttributeError est lancée. [Lire les docs.] (Https://docs.python.org/3/reference/datamodel.html#object.__getattr__) –

+0

@Rawing, Ah, c'est tout à fait logique. Merci pour le lien et l'explication! – tino