Getattr () chính xác là gì và làm thế nào để tôi sử dụng nó?


295

Gần đây tôi đã đọc về getattr()chức năng . Vấn đề là tôi vẫn không thể nắm bắt được ý tưởng sử dụng nó. Điều duy nhất tôi hiểu về getattr()getattr(li, "pop")cũng giống như gọi li.pop.

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


Phần nào bạn gặp khó khăn với? Thuộc tính như chuỗi? Chức năng hạng nhất?
Ignacio Vazquez-Abrams

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

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

@Alois, câu trả lời của bạn chắc chắn đã xóa tan một số nghi ngờ của tôi, nhưng tôi vẫn không thể hiểu đầy đủ những gì getattr () dành cho.
Terence Ponce

6
@ S.Lott, tôi đã làm. Các tài liệu chỉ có định nghĩa nên tôi hơi bối rối về cách sử dụng nó. Tôi hiểu getattr bây giờ sau khi đọc thêm về nó mặc dù.
Terence Ponce

Câu trả lời:


88

getattr(object, 'x') là hoàn toàn tương đương với object.x.

chỉ có hai trường hợpgetattrcó thể hữu ích.

  • bạn không thể viết object.x, vì bạn không biết trước thuộc tính nào bạn muốn (nó xuất phát từ một chuỗi). Rất hữu ích cho lập trình meta.
  • bạn muốn cung cấp một giá trị mặc định. object.ysẽ tăng AttributeErrornếu không có y. Nhưng getattr(object, 'y', 5)sẽ trở lại 5.

2
Tôi cảm thấy đây nên là câu trả lời được chấp nhận. Rất rõ ràng và cho điểm.
yuqli

290

Các đối tượng trong Python có thể có các thuộc tính - các thuộc tính dữ liệu và các hàm để làm việc với các (các phương thức) đó. Trên thực tế, mọi đối tượng đều có các thuộc tính tích hợp.

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

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 được gọi 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

getattrsẽ tăng AttributeErrornếu thuộc tính với tên đã cho không tồn tại trong đố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ể chuyển một giá trị mặc định làm đối số thứ ba, sẽ được trả về nếu thuộc tính đó không tồn tại:

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

Bạn có thể sử dụng getattrcùng với dirđể lặp lại tất cả các tên thuộc tính và nhận các giá trị của chúng:

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

Một cách sử dụng thực tế cho việc này sẽ là tìm tất cả các phương thức có tên bắt đầu testgọi chúng .

Tương tự như getattrsetattrđó cho phép bạn đặ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'
>>>

9
Vì vậy, có vẻ như tôi getattr(..)nên 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 một biến (ví dụ getattr(person, some_attr)) và 2. khi chúng ta cần sử dụng đối số vị trí thứ ba cho giá trị mặc định (ví dụ getattr(person, 'age', 24)). Nếu tôi thấy một kịch bản giống getattr(person, 'age')như đối với tôi thì nó giống hệt với person.agenó khiến tôi nghĩ rằng đó person.agelà Pythonic hơn. Đúng không?
wpcarro

102

Cho tôi, getattr dễ nhất là 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 một 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()

bởi vì x không phải là loại builtin, 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 tự động kết nối với các đối tượng dựa trên đầu vào của bạn. Tôi đã thấy nó hữu ích khi xử lý các đối tượng và mô-đun tùy chỉnh.


2
Đây là một câu trả lời khá thẳng về phía trước và chính xác.
dùng6037143

43

Một trường hợp sử dụng khá phổ biến cho getattr ánh xạ dữ liệu tới các hàm.

Chẳng hạn, trong một khung web như Django hoặc Pylons, getattrlàm cho việc ánh xạ URL của yêu cầu web đến chức năng sẽ xử lý nó trở nên đơn giản. Chẳng hạn, nếu bạn nhìn vào phần định tuyến của Pylons, bạn sẽ thấy rằng (ít nhất là theo mặc định), nó sẽ chọn URL của yêu cầu, như:

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

vào "khách hàng" và "danh sách". Sau đó, nó tìm kiếm một lớp điều khiển có tên 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để lấy nólist phương thức . Sau đó, nó gọi phương thức đó, truyền yêu cầu đó làm đối số.

Khi bạn nắm bắt được ý tưởng này, việc mở rộng chức năng của ứng dụng web trở nên thực sự dễ dàng: chỉ cần thêm các phương thức mới vào các lớp trình điều khiển và sau đó tạo liên kết trong các trang của bạn sử dụng 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.


13

Đây là một ví dụ nhanh và bẩn về cách một 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 mà nó được thực thi 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ờ 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()

7

Khác với tất cả các câu trả lời tuyệt vời ở đây, có một cách để sử dụng getattr để lưu các dòng mã phong phú và giữ cho nó vừa khít. Suy nghĩ này xuất hiện sau sự đại diện đáng sợ của mã mà đôi khi có thể là một điều cần thiết.

Kịch bản

Giả sử cấu trúc thư mục của bạn như sau:

- superheroes.py
- properties.py

Và, bạn có chức năng để nhận thông tin về Thor, Iron Man, Doctor Strangetrong superheroes.py. Bạn rất thông minh viết ra các thuộc tính của tất cả chúng properties.pytrong một bản rút gọn dictvà sau đó truy cập chúng.

properties.py

thor = {
    'about': 'Asgardian god of thunder',
    'weapon': 'Mjolnir',
    'powers': ['invulnerability', 'keen senses', 'vortex breath'], # and many more
}
iron_man = {
    'about': 'A wealthy American business magnate, playboy, and ingenious scientist',
    'weapon': 'Armor',
    'powers': ['intellect', 'armor suit', 'interface with wireless connections', 'money'],
}
doctor_strange = {
    'about': ' primary protector of Earth against magical and mystical threats',
    'weapon': 'Magic',
    'powers': ['magic', 'intellect', 'martial arts'],
}

Bây giờ, giả sử bạn muốn trả lại khả năng của từng người trong số họ theo yêu cầu superheroes.py. Vì vậy, có các chức năng như

from .properties import thor, iron_man, doctor_strange


def get_thor_weapon():
    return thor['weapon']


def get_iron_man_bio():
    return iron_man['about']


def get_thor_powers():
    return thor['powers']

... và nhiều chức năng trả về các giá trị khác nhau dựa trên các phím và siêu anh hùng.

Với sự giúp đỡ của getattr, bạn có thể làm một cái gì đó như:

from . import properties


def get_superhero_weapon(hero):
    superhero = getattr(properties, hero)
    return superhero['weapon']


def get_superhero_powers(hero):
    superhero = getattr(properties, hero)
    return superhero['powers']

Bạn đã giảm đáng kể số lượng dòng mã, chức năng và sự lặp lại!

Ồ và tất nhiên, nếu bạn có tên xấu như properties_of_thorbiến, chúng có thể được tạo và truy cập bằng cách thực hiện đơn giản

def get_superhero_weapon(hero):
    superhero = 'properties_of_{}'.format(hero)
    all_properties = getattr(properties, superhero)
    return all_properties['weapon']

LƯU Ý: Đối với vấn đề cụ thể này, có thể có những cách thông minh hơn để xử lý tình huống, nhưng ý tưởng là đưa ra một cái nhìn sâu sắc về việc sử dụng getattrđúng nơi để viết mã sạch hơn.


3
# 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 bạn 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(..)để khởi tạo một cách lười biếng các thuộc tính có tầm quan trọng thứ cấp ngay trước khi chúng được sử dụng trong mã.

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

Về điều 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_plotchỉ xuất hiện xung quanh vị trí trong mã được sử dụng. Điều này tốt cho khả năng đọc, bởi vì (1) bạn có thể thấy ngay giá trị của nó bắt đầu khi đọc cách sử dụng, (2) nó không đưa ra sự phân tâm vào __init__(..)phương thức, lý tưởng nhất là về trạng thái khái niệm của lớp , thay vì một số bộ đếm tiện ích chỉ được sử dụng bởi một trong các phương thức của hàm vì 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

Rất thường xuyên khi tôi tạo một tệp XML từ dữ liệu được lưu trữ trong một lớp, tôi sẽ thường xuyên gặp 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à là dữ liệu đã từng được lưu trữ trong thuộc tính đó.

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

Nếu tôi đã từng hasattrlàm điều này, nó sẽ trả về Truengay cả khi giá trị thuộc tính là loại Nonevà điều này sẽ khiến setlệnh ElementTree của tôi thất bại.

hasattr(temp, 'hair')
>>True

Nếu giá trị thuộc tính là loại None, getattrcũng sẽ trả về nó sẽ khiến setlệnh ElementTree của tôi bị lỗ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 này:

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 và cách tôi sử dụng getattr.


3

Một cách sử dụng khác của getattr () trong việc thực hiện câu lệnh switch trong Python. Nó sử dụng cả hai phản xạ để 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]))

Tôi thích câu trả lời này nhưng vui lòng sửa lỗi chính tả nhỏ
có thể

2

setattr ()

Chúng tôi sử dụng setattr để thêm một thuộc tính vào thể hiện của lớp. Chúng ta truyền ví dụ lớp, tên thuộc tính và giá trị.

getattr ()

Với getattr, chúng tôi lấy lại các giá trị này

Ví dụ

Employee = type("Employee", (object,), dict())

employee = Employee()

# Set salary to 1000
setattr(employee,"salary", 1000 )

# Get the Salary
value = getattr(employee, "salary")

print(value)

1

Tôi nghĩ ví dụ này là tự giải thích. Nó chạy phương thức của tham số đầu tiên, có tên được đưa ra trong tham số thứ hai.

class MyClass:
   def __init__(self):
      pass
   def MyMethod(self):
      print("Method ran")

# Create an object
object = MyClass()
# Get all the methods of a class
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
# You can use any of the methods in method_list
# "MyMethod" is the one we want to use right now

# This is the same as running "object.MyMethod()"
getattr(object,'MyMethod')()

0

Nó cũng được làm rõ từ https://www.programiz.com/python-programming/methods/built-in/getattr

class Person:
    age = 23
    name = "Adam"

person = Person()
print('The age is:', getattr(person, "age"))
print('The age is:', person.age)

Độ tuổi là: 23

Độ tuổi là: 23

class Person:
    age = 23
    name = "Adam"

person = Person()

# when default value is provided
print('The sex is:', getattr(person, 'sex', 'Male'))

# when no default value is provided
print('The sex is:', getattr(person, 'sex'))

Giới tính là: Nam

AttributionError: Đối tượng 'Person' không có thuộc tính 'sex'


0

Tôi đã thử trong Python2.7.17

Một số đồng bào đã trả lời. Tuy nhiên, tôi đã cố gắng gọi getattr (obj, 'set_value') và điều này đã không thực thi phương thức set_value, vì vậy tôi đã thay đổi thành getattr (obj, 'set_value') () -> Điều này giúp gọi tương tự.

Mã ví dụ:

Ví dụ 1:

    class GETATT_VERIFY():
       name = "siva"
       def __init__(self):
           print "Ok"
       def set_value(self):
           self.value = "myself"
           print "oooh"
    obj = GETATT_VERIFY()
    print getattr(GETATT_VERIFY, 'name')
    getattr(obj, 'set_value')()
    print obj.value
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.