2017-09-13 73 views
3

標題可能不是我想要做的最好的描述,但我不知道該怎麼稱呼它。我遇到了各種看起來相關的概念,名稱分別是「裝飾器」,「描述符」和「元類」,但我不知道我應該進一步調查哪些方法(如果有的話)。動態定義派生自類屬性名稱的方法?

考慮以下類:

class AnimalGreeter(object): 

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

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

class MyGreeter(AnimalGreeter): 

    animals = ['dog', 'parrot'] 

我希望能夠使用的MyGreeter一個實例是這樣的:

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

實際上,我想它充當如果我已定義MyGreeter而不是:

class MyGreeter(AnimalGreeter): 

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

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

In換句話說,我想動態定義從類(MyGreeter)的屬性(animals)派生的名稱的方法(dog()等)。

這樣做的最好方法是什麼?非常感謝!

+2

只實現'__getattr __(自我,動物)'。 – AChampion

+0

不能幫助,但我對你的用例感興趣。你爲什麼需要這種行爲? – FabienP

+1

@FabienP,在我的案例中'AnimalGreeter'是一個Django模型,'MyGreeter'是一個自定義混合,它可以處理具有可配置文件名的圖像。我想要訪問Django模板中的圖像url,但Django的模板系統不允許將實例方法傳遞給參數。所以我不能在模板中做相當於'model.image_url('large')'。但是我可以訪問模板中的實例屬性,例如'model.large_image_url'。 – tino

回答

4

一個簡單的方法來做到這一點也只是實現__getattr__(),例如:

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

謝謝!這是我遇到的其中一個建議,但不確定我是否應該關注開銷。 'AnimalGreeter'有很多其他屬性可以經常訪問。每次訪問任何實例屬性時都會執行此操作嗎? – tino

+2

@tino否,只有在訪問「不存在」的屬性時才調用__getattr__',即在拋出AttributeError時調用該屬性。 [閱讀文檔](https://docs.python.org/3/reference/datamodel.html#object.__getattr__) –

+0

@Rawing,啊,這很有道理。感謝您的鏈接和解釋! – tino