2010-11-02 108 views
173

我是reading about the getattr() function。問題是我仍然無法理解它的用法。我對getattr()唯一瞭解的是getattr(li, "pop")與致電li.pop相同。什麼是getattr(),我該如何使用它?

我不明白本書何時提及如何在運行時使用它來獲取對函數的引用而不知道它的名稱。總的來說,也許這就是我在編程方面的一個小菜鳥。任何人都可以談談這個問題嗎?何時以及如何使用這個?

+0

哪個部位有問題?屬性作爲字符串?一流的功能? – 2010-11-02 05:50:39

+0

我認爲我的問題是理解getattr()的概念。我仍然不明白它的目的。 – 2010-11-02 05:55:39

+0

@特倫斯我的回答沒有讓事情更清楚嗎? – 2010-11-02 05:57:22

回答

45

您可以查看完整的例子在這裏:

自省,可用於不同的目的,在「深入Python」中呈現的是簡單地增加功能的方法(插件)動態地在您的應用程序中。

通過動態我的意思是沒有在覈心應用程序修改添加一個新的功能。

服用「深入Python」的例子 - 一個簡單的應用程序,從不同的文件的文件中提取屬性 - 您可以添加新的文件格式的處理,而不做修改原來的應用程序。

我建議你完成這本書。當你閱讀時,一切都會變得越來越清晰。

+0

我想我能理解反思的概念。真正讓我感到厭煩的是getattr()的用法。如果我只是提前閱讀,我會更好地理解它嗎? – 2010-11-02 06:01:30

+0

是的,當然,您是否閱讀過我發送鏈接的部分?你在裏面有一個getattr()的具體用法。 http://diveintopython.org/object_oriented_framework/index.html – 2010-11-02 06:03:19

+0

我還沒有讀完它。謝謝你的提示。我會接受這個答案。 – 2010-11-02 06:08:36

11

下面是一個快速和骯髒的例子,說明一個類如何根據使用getattr()執行哪個操作系統來激發不同版本的保存方法。

import os 

class Log(object): 
    def __init__(self): 
     self.os = os.name 
    def __getattr__(self, name): 
     """ look for a 'save' attribute, or just 
      return whatever attribute was specified """ 
     if name == 'save': 
      try: 
       # try to dynamically return a save 
       # method appropriate for the user's system 
       return getattr(self, self.os) 
      except: 
       # bail and try to return 
       # a default save method 
       return getattr(self, '_save') 
     else: 
      return getattr(self, name) 

    # each of these methods could have save logic specific to 
    # the system on which the script is executed 
    def posix(self): print 'saving on a posix machine' 
    def nt(self): print 'saving on an nt machine' 
    def os2(self): print 'saving on an os2 machine' 
    def ce(self): print 'saving on a ce machine' 
    def java(self): print 'saving on a java machine' 
    def riscos(self): print 'saving on a riscos machine' 
    def _save(self): print 'saving on an unknown operating system' 

    def which_os(self): print os.name 

現在讓我們使用這個類的一個示例:

logger = Log() 

# Now you can do one of two things: 
save_func = logger.save 
# and execute it, or pass it along 
# somewhere else as 1st class: 
save_func() 

# or you can just call it directly: 
logger.save() 

# other attributes will hit the else 
# statement and still work as expected 
logger.which_os() 
38

一個很常見的用例getattr是將數據映射到功能。

例如,在像Django或Pylons這樣的Web框架中,getattr可以很簡單地將Web請求的URL映射到要處理它的函數。如果你看看塔的路由的引擎蓋下,例如,你會看到(默認情況下,至少)它扒了一個請求的URL,如:

http://www.example.com/customers/list 

成「客戶」和「列表」。然後它搜索名爲CustomerController的控制器類。假設它找到該類,它將創建該類的一個實例,然後使用getattr來獲取其list方法。然後它調用該方法,將請求作爲參數傳遞給它。

一旦掌握了這個想法,擴展Web應用程序的功能就變得非常簡單:只需向控制器類中添加新的方法,然後在頁面中爲這些方法使用適當的URL創建鏈接。所有這些都可以通過getattr來實現。

+4

+1指出「映射數據到函數」 – Philip007 2012-10-13 18:53:48

194

Python中的對象可以具有屬性(實際上,每個對象都具有內置屬性 - 數據屬性和方法(函數是值,即對象)以使用這些屬性)。

例如你有一個對象person,有幾個屬性:namegender

你訪問這些屬性(無論是方法或數據對象),通常寫作:person.nameperson.genderperson.the_method(),等等。

但是,如果您在編寫程序時不知道該屬性的名稱,該怎麼辦?例如,您將屬性名稱存儲在名爲attr_name的變量中。

如果

attr_name = 'gender' 

然後,而不是寫

gender = person.gender 

你可以寫

gender = getattr(person, attr_name) 

一些做法:

Python 3.4.0 (default, Apr 11 2014, 13:05:11) 

>>> class Person(): 
...  name = 'Victor' 
...  def say(self, what): 
...   print(self.name, what) 
... 
>>> getattr(Person, 'name') 
'Victor' 
>>> attr_name = 'name' 
>>> person = Person() 
>>> getattr(person, attr_name) 
'Victor' 
>>> getattr(person, 'say')('Hello') 
Victor Hello 

getattr將提高AttributeError如果與給定的名稱屬性不會在對象存在:

>>> getattr(person, 'age') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'Person' object has no attribute 'age' 

但是你可以傳遞一個默認值作爲第三個參數,如果這樣的屬性不存在,這將返回:

>>> getattr(person, 'age', 0) 
0 

您可以使用dir一起getattr遍歷所有的屬性名,並得到他們的價值觀:

>>> dir(1000) 
['__abs__', '__add__', ..., '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes'] 

>>> obj = 1000 
>>> for attr_name in dir(obj): 
...  attr_value = getattr(obj, attr_name) 
...  print(attr_name, attr_value, callable(attr_value)) 
... 
__abs__ <method-wrapper '__abs__' of int object at 0x7f4e927c2f90> True 
... 
bit_length <built-in method bit_length of int object at 0x7f4e927c2f90> True 
... 

>>> getattr(1000, 'bit_length')() 
10 

對此的實際應用是查找名稱以testcall them開頭的所有方法。

類似getattrsetattr它允許你設置一個對象的屬性有其名稱:

>>> setattr(person, 'name', 'Andrew') 
>>> person.name # accessing instance attribute 
'Andrew' 
>>> Person.name # accessing class attribute 
'Victor' 
>>> 
+1

所以在我看來在以下兩種情況下應該使用'getattr(..)':1.當屬性名稱是變量內部的值時(例如'getattr(person,some_attr)')和2.當我們需要使用第三個位置參數爲默認值(例如'getattr(person,'age',24)')。如果我看到像getattr(person,'age')這樣的場景,我認爲它與'person.age'完全相同,這使我認爲'person.age'更加Pythonic。那是對的嗎? – wpcarro 2016-10-24 22:01:33

+0

另外,這將是很好的指出,有像'setattr()' – 2016-10-27 15:46:29

+1

@BłażejMichalik謝謝,將添加此信息 – warvariuc 2016-10-27 20:04:30

67

對於我來說,GETATTR是最簡單的解釋是這樣的:

它可以讓你根據字符串的內容調用方法,而不是輸入方法名稱。因爲x是類型的不是 「內置」,而是 「STR」

obj = MyObject() 
for x in ['foo', 'bar']: 
    obj.x() 

例如,你不能做到這一點。但是,您可以這樣做:

obj = MyObject() 
for x in ['foo', 'bar']: 
    getattr(obj, x)() 

它允許您根據輸入動態連接對象。在處理自定義對象和模塊時我發現它很有用。

2
# getattr 

class hithere(): 

    def french(self): 
     print 'bonjour' 

    def english(self): 
     print 'hello' 

    def german(self): 
     print 'hallo' 

    def czech(self): 
     print 'ahoj' 

    def noidea(self): 
     print 'unknown language' 


def dispatch(language): 
    try: 
     getattr(hithere(),language)() 
    except: 
     getattr(hithere(),'noidea')() 
     # note, do better error handling than this 

dispatch('french') 
dispatch('english') 
dispatch('german') 
dispatch('czech') 
dispatch('spanish') 
+2

您是否可以詳細說明您的答案,並添加關於您提供的解決方案的更多描述? – abarisone 2015-03-26 13:30:39

3

我有時用getattr(..)懶洋洋地初始化它們在代碼中使用之前的次要屬性。

比較如下:

class Graph(object): 
    def __init__(self): 
     self.n_calls_to_plot = 0 

    #... 
    #A lot of code here 
    #... 

    def plot(self): 
     self.n_calls_to_plot += 1 

要這樣:

class Graph(object): 
    def plot(self): 
     self.n_calls_to_plot = 1 + getattr(self, "n_calls_to_plot", 0) 

的第二種方式的優點是n_calls_to_plot只出現圍繞它是用來在代碼的地方。這對於可讀性是有益的,因爲(1)在閱讀它的使用方式時,可以立即看到它開始的價值,(2)它不會將注意力引入__init__(..)方法,理想情況下應該是關於類,而不是一些實用計數器,由於技術原因(例如優化)僅由函數方法之一使用,並且與對象的含義無關。

3

當我從存儲在類中的數據創建XML文件時,經常會遇到錯誤,如果該屬性不存在或者類型爲None。在這種情況下,我的問題並不知道屬性名稱是什麼,正如您的問題所述,而是存儲在該屬性中的數據。

class Pet: 
    def __init__(self): 
     self.hair = None 
     self.color = None 

如果我以前hasattr要做到這一點,它會返回True即使該屬性值是None型的,這將導致我的ElementTree set命令失敗。

hasattr(temp, 'hair') 
>>True 

如果屬性值是None型,getattr也將返回它,這將導致我的ElementTree set命令失敗。

c = getattr(temp, 'hair') 
type(c) 
>> NoneType 

我用下面的方法,現在照顧這些情況:

def getRealAttr(class_obj, class_attr, default = ''): 
    temp = getattr(class_obj, class_attr, default) 
    if temp is None: 
     temp = default 
    elif type(temp) != str: 
     temp = str(temp) 
    return temp 

這是什麼時候,怎麼我用getattr

1

getattr()在Python中實現switch語句的另一種用法。它使用兩種反射來獲取案例類型。

import sys 

class SwitchStatement(object): 
    """ a class to implement switch statement and a way to show how to use gettattr in Pythion""" 

    def case_1(self): 
     return "value for case_1" 

    def case_2(self): 
     return "value for case_2" 

    def case_3(self): 
     return "value for case_3" 

    def case_4(self): 
     return "value for case_4" 

    def case_value(self, case_type=1): 
     """This is the main dispatchmethod, that uses gettattr""" 
     case_method = 'case_' + str(case_type) 
     # fetch the relevant method name 
     # Get the method from 'self'. Default to a lambda. 
     method = getattr(self, case_method, lambda: "Invalid case type") 
     # Call the method as we return it 
     return method() 

def main(_): 
    switch = SwitchStatement() 
    print swtich.case_value(_) 

if __name__ == '__main__': 
    main(int(sys.argv[1])) 
相關問題