2010-11-02 12 views
173

Tôi là reading about the getattr() function. Vấn đề là tôi vẫn không thể hiểu được ý tưởng sử dụng của nó. Điều duy nhất tôi hiểu về getattr()getattr(li, "pop") giống như gọi số li.pop.Getattr() chính xác là gì và tôi sử dụng nó như thế nào?

Tôi không hiểu khi nào cuốn sách đề cập đến cách bạn sử dụng nó để tham chiếu đến một hàm mà không biết tên của nó cho đến khi chạy. Có lẽ đây là tôi là một noob trong lập trình, nói chung. Có ai có thể làm sáng tỏ chủ đề không? Khi nào và làm thế nào để tôi sử dụng chính xác?

+0

Bạn đang gặp sự cố với phần nào? Thuộc tính dưới dạng chuỗi? Các hàm hạng nhất? –

+0

Tôi nghĩ rằng vấn đề của tôi là hiểu được khái niệm getattr(). Tôi vẫn không hiểu mục đích của nó. –

+0

@Terence không trả lời của tôi làm cho mọi thứ rõ ràng hơn? –

Trả lời

45

Bạn có thể xem một ví dụ đầy đủ ở đây:

Mẫn có thể được sử dụng cho mục đích khác nhau, một trong những trình bày trong 'Dive Into Python' chỉ đơn giản là một cách để thêm chức năng (plug-in) động trong ứng dụng của bạn.

Bằng động Ý tôi là không sửa đổi trong ứng dụng chính để thêm tính năng mới.

Lấy ví dụ 'Dive Into Python' - một ứng dụng đơn giản để trích xuất thuộc tính từ tệp của tệp khác nhau - bạn có thể thêm xử lý định dạng tệp mới mà không sửa đổi ứng dụng gốc.

Tôi khuyên bạn nên hoàn thành cuốn sách. Khi bạn đọc, mọi thứ sẽ trở nên ngày càng rõ ràng.

+0

Tôi nghĩ rằng tôi có thể hiểu được khái niệm về nội tâm. Những gì thực sự boggles tôi là việc sử dụng getattr(). Tôi sẽ hiểu nó tốt hơn nếu tôi chỉ đọc trước? –

+0

Có chắc chắn, bạn đã đọc phần tôi gửi làm liên kết? bạn có một cách sử dụng thực sự cụ thể của getattr() bên trong. http://diveintopython.org/object_oriented_framework/index.html –

+0

Tôi chưa đọc xong. Cảm ơn vì tiền hỗ trợ. Tôi sẽ chấp nhận điều này như là câu trả lời. –

11

Dưới đây là ví dụ nhanh về cách lớp có thể kích hoạt các phiên bản khác nhau của phương thức lưu tùy thuộc vào hệ điều hành nào đang được thực hiện khi sử dụng 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 

Bây giờ chúng ta hãy sử dụng lớp này trong một ví dụ:

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

Một trường hợp sử dụng khá phổ biến cho getattr là dữ liệu bản đồ vào các chức năng. Ví dụ:

Ví dụ: trong khung web như Django hoặc giá treo, getattr giúp bạn dễ dàng ánh xạ URL của yêu cầu web đến hàm sẽ xử lý nó. Nếu bạn nhìn dưới mui xe của định tuyến giá treo của, ví dụ, bạn sẽ thấy rằng (theo mặc định, ít nhất) có thể băm nhỏ URL của yêu cầu, như:

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

thành "khách hàng" và "danh sách". Sau đó, nó tìm kiếm một lớp điều khiển tên là CustomerController. Giả sử nó tìm thấy lớp, nó tạo ra một thể hiện của lớp và sau đó sử dụng getattr để có được phương thức list của nó. Sau đó nó gọi phương thức đó, chuyển nó thành một đối số. Khi bạn nắm bắt được ý tưởng này, bạn sẽ dễ dàng mở rộng chức năng của một ứng dụng web: chỉ cần thêm các phương thức mới vào các lớp điều khiển và sau đó tạo các liên kết trong các trang sử dụng các URL thích hợp cho các phương thức đó. Tất cả điều này được thực hiện bởi getattr.

+4

+1 để chỉ ra "ánh xạ dữ liệu đến hàm" – Philip007

194

Đối tượng trong Python có thể có thuộc tính

Ví dụ, bạn có một đối tượng person, có một số thuộc tính: name, gender vv

Bạn truy cập vào các thuộc tính (có thể là phương pháp hay đối tượng dữ liệu) thường viết: person.name, person.gender, person.the_method() vv

Nhưng nếu bạn không biết tên thuộc tính tại thời điểm bạn viết chương trình thì sao? Ví dụ bạn có tên thuộc tính được lưu trữ trong một biến gọi là attr_name.

nếu

attr_name = 'gender' 

sau đó, thay vì viết

gender = person.gender 

bạn có thể viết

gender = getattr(person, attr_name) 

Một số thực hành:

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 sẽ tăng AttributeError nếu thuộc tính với tên được đặt không tồn tại trong các đối tượng:

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

Nhưng bạn có thể vượt qua một giá trị mặc định như là đối số thứ ba, mà sẽ được trả lại nếu thuộc tính như vậy không tồn tại:

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

Bạn có thể sử dụng cùng với getattrdir để lặp qua tất cả các tên thuộc tính và nhận được giá trị của họ:

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

Cách sử dụng thực tế cho việc này là tìm tất cả các phương thức có tên bắt đầu bằng testcall them.

Tương tự như getattrsetattr cho phép bạn thiết lập một thuộc tính của một đối tượng có tên của nó:

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

Vì vậy, có vẻ như với tôi 'getattr (..)' nên được sử dụng trong 2 kịch bản: 1. khi tên thuộc tính là một giá trị bên trong của một biến (ví dụ 'getattr (person, some_attr)') và 2. khi chúng ta cần sử dụng vị trí thứ ba đối số cho giá trị mặc định (ví dụ 'getattr (person, 'age', 24)'). Nếu tôi thấy một kịch bản như 'getattr (người,' tuổi ') 'có vẻ như với tôi rằng nó giống hệt' person.age' khiến tôi nghĩ rằng 'person.age' là Pythonic hơn. Đúng không? – wpcarro

+0

Ngoài ra, nó sẽ được tốt đẹp để chỉ ra rằng có một cái gì đó như 'setattr()' –

+1

@ BłażejMichalik cảm ơn, sẽ thêm thông tin này – warvariuc

67

Đối với tôi, getattr là đơn giản nhất để giải thích theo cách này:

Nó cho phép bạn để gọi các phương thức dựa trên nội dung của chuỗi thay vì nhập tên phương thức.

Ví dụ, bạn không thể làm điều này:

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

vì x không phải là loại "dựng sẵn", nhưng "str". Tuy nhiên, bạn CÓ THỂ làm điều này:

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

Nó cho phép bạn kết nối động với các đối tượng dựa trên dữ liệu đầu vào của bạn. Tôi đã thấy nó hữu ích khi giao dịch với các đối tượng và mô-đun tùy chỉnh.

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

Bạn có thể giải thích thêm câu trả lời của mình bằng cách thêm một chút mô tả về giải pháp bạn cung cấp không? – abarisone

3

Đôi khi tôi sử dụng getattr(..) để lười biếng khởi tạo thuộc tính tầm quan trọng thứ hai ngay trước khi chúng được sử dụng trong mã.

Hãy so sánh như sau:

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 

Để này:

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

Ưu điểm của cách thứ hai là n_calls_to_plot chỉ xuất hiện xung quanh nơi trong mã nơi nó được sử dụng. Điều này là tốt cho dễ đọc, bởi vì (1) bạn có thể ngay lập tức thấy giá trị của nó bắt đầu khi đọc nó được sử dụng như thế nào, (2) nó không giới thiệu một sự phân tâm vào phương pháp __init__(..), lý tưởng nhất là về trạng thái khái niệm của lớp, thay vì một số truy cập tiện ích chỉ được sử dụng bởi một trong các phương thức của hàm vì các lý do kỹ thuật, chẳng hạn như tối ưu hóa và không liên quan gì đến ý nghĩa của đối tượng.

3

Khá thường xuyên khi tôi tạo tệp XML từ dữ liệu được lưu trữ trong một lớp, tôi thường xuyên nhận được lỗi nếu thuộc tính không tồn tại hoặc thuộc loại None. Trong trường hợp này, vấn đề của tôi không biết tên thuộc tính là gì, như đã nêu trong câu hỏi của bạn, mà đúng hơn là dữ liệu được lưu trữ trong thuộc tính đó.

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

Nếu tôi sử dụng hasattr để làm điều này, nó sẽ quay trở lại True ngay cả khi giá trị thuộc tính là loại None và điều này sẽ gây ra lệnh ElementTree set của tôi thất bại.

hasattr(temp, 'hair') 
>>True 

Nếu giá trị thuộc tính là loại None, getattr cũng sẽ quay trở lại nó mà có thể gây ra lệnh ElementTree set của tôi thất bại.

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

tôi sử dụng phương pháp sau đây để chăm sóc những trường hợp bây giờ:

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 

Đây là khi nào và làm thế nào tôi sử dụng getattr.

1

Sử dụng khác getattr() trong việc triển khai câu lệnh chuyển đổi bằng Python. Nó sử dụng cả hai phản ánh để có được loại trường hợp.

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]))