getattr () คืออะไรและฉันจะใช้อย่างไร


295

ผมเพิ่งอ่านเกี่ยวกับgetattr()ฟังก์ชั่น ปัญหาคือฉันยังไม่เข้าใจความคิดของการใช้งาน สิ่งเดียวที่ฉันเข้าใจเกี่ยวกับgetattr()ว่าเป็นเช่นเดียวกับการเรียกgetattr(li, "pop")li.pop

ฉันไม่เข้าใจเมื่อหนังสือกล่าวถึงวิธีที่คุณใช้เพื่อรับการอ้างอิงไปยังฟังก์ชั่นโดยไม่ทราบชื่อจนกระทั่งถึงเวลาใช้งาน บางทีนี่อาจเป็นฉันในการเขียนโปรแกรมโดยทั่วไป มีใครบ้างไหมที่ให้ความสว่างกับตัวแบบ? ฉันควรใช้สิ่งนี้เมื่อใดและอย่างไร


ส่วนไหนที่คุณกำลังมีปัญหากับ? คุณสมบัติเป็นสตริง? ฟังก์ชั่นชั้นหนึ่ง?
Ignacio Vazquez-Abrams

1
ฉันคิดว่าปัญหาของฉันคือการเข้าใจแนวคิดของ getattr () ฉันยังไม่เข้าใจวัตถุประสงค์ของมัน
Terence Ponce

@ คำตอบของฉันไม่ได้ทำให้สิ่งต่าง ๆ ชัดเจนขึ้น?
Alois Cochard

@Alois คำตอบของคุณลบข้อสงสัยของฉันออกไปบางส่วน แต่ฉันก็ยังไม่เข้าใจว่า getattr () นั้นเหมาะกับอะไร
Terence Ponce

6
@ S.Lott ฉันทำ เอกสารมีเพียงคำจำกัดความดังนั้นฉันสับสนเกี่ยวกับการใช้งาน ฉันเข้าใจ getattr ทันทีหลังจากอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้
Terence Ponce

คำตอบ:


88

getattr(object, 'x') เป็นสมบูรณ์เทียบเท่าobject.xการ

มีเพียงสองกรณีเท่านั้นที่getattrสามารถเป็นประโยชน์ได้

  • คุณไม่สามารถเขียนได้object.xเพราะคุณไม่ทราบล่วงหน้าว่าคุณต้องการคุณลักษณะใด (มาจากสตริง) มีประโยชน์มากสำหรับการเขียนโปรแกรมเมตา
  • คุณต้องการระบุค่าเริ่มต้น object.yจะยกถ้าไม่มีAttributeError yแต่จะกลับมาgetattr(object, 'y', 5)5

2
ฉันรู้สึกว่านี่ควรเป็นคำตอบที่ยอมรับได้ ชัดเจนมากและตรงประเด็น
yuqli

290

วัตถุใน Python สามารถมีคุณลักษณะได้ - คุณลักษณะของข้อมูลและฟังก์ชั่นในการทำงานกับ (วิธีการ) ที่จริงแล้วทุกวัตถุมีคุณสมบัติในตัว

ตัวอย่างเช่นคุณมีวัตถุpersonที่มีคุณลักษณะหลายname, genderฯลฯ

คุณสามารถเข้าถึงคุณลักษณะเหล่านี้ (ไม่ว่าจะเป็นวิธีการหรือวัตถุข้อมูล) มักเขียน: person.name, person.gender, person.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

คุณสามารถใช้getattrร่วมกับdirเพื่อวนซ้ำชื่อแอตทริบิวต์ทั้งหมดและรับค่า:

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

การใช้งานจริงสำหรับสิ่งนี้คือการค้นหาวิธีการทั้งหมดที่มีชื่อขึ้นต้นด้วยtestและเรียกมันว่า

คล้ายกับgetattrมีsetattrซึ่งจะช่วยให้คุณสามารถตั้งค่าแอตทริบิวต์ของวัตถุที่มีชื่อของมัน:

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

9
ดังนั้นสำหรับฉันดูเหมือนว่าgetattr(..)ควรใช้ใน 2 สถานการณ์: 1. เมื่อชื่อคุณลักษณะเป็นค่าภายในตัวแปร (เช่นgetattr(person, some_attr)) และ 2 เมื่อเราจำเป็นต้องใช้อาร์กิวเมนต์ตำแหน่งที่สามสำหรับค่าเริ่มต้น (เช่นgetattr(person, 'age', 24)) ถ้าฉันเห็นสถานการณ์แบบgetattr(person, 'age')นี้ดูเหมือนว่าฉันจะเหมือนกันperson.ageซึ่งทำให้ฉันคิดว่าperson.ageเป็น Pythonic มากกว่า ถูกต้องหรือไม่
wpcarro

102

สำหรับฉันgetattrง่ายที่สุดที่จะอธิบายด้วยวิธีนี้:

จะช่วยให้คุณสามารถเรียกวิธีการตามเนื้อหาของสตริงแทนการพิมพ์ชื่อวิธีการ

ตัวอย่างเช่นคุณไม่สามารถทำสิ่งนี้:

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

เพราะ x ไม่ได้เป็นชนิดที่แต่builtin strอย่างไรก็ตามคุณสามารถทำได้:

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

ช่วยให้คุณสามารถเชื่อมต่อกับวัตถุตามอินพุตของคุณแบบไดนามิก ฉันพบว่ามีประโยชน์เมื่อจัดการกับวัตถุและโมดูลที่กำหนดเอง


2
นี่เป็นคำตอบที่ตรงไปตรงมาและแม่นยำ
6037143

43

กรณีการใช้งานที่ค่อนข้างธรรมดาgetattrคือการจับคู่ข้อมูลกับฟังก์ชัน

ตัวอย่างเช่นในกรอบงานเว็บเช่น Django หรือ Pylons getattrทำให้ตรงไปตรงมาเพื่อจับคู่ URL ของคำขอเว็บกับฟังก์ชั่นที่จะจัดการ หากคุณดูภายใต้ประทุนของการกำหนดเส้นทางของ Pylons คุณจะเห็นว่า (โดยค่าเริ่มต้นอย่างน้อย) จะตัด URL ของคำขอเช่น:

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

ลงใน "ลูกค้า" และ "รายการ" จากนั้นจะค้นหาคลาสคอนโทรลเลอร์ที่ชื่อCustomerControllerจากนั้นก็จะค้นหาชั้นชื่อตัวควบคุมเป็นสมมติว่ามันหาคลาสมันสร้างอินสแตนซ์ของคลาสแล้วใช้getattrเพื่อรับlistเมธอด จากนั้นเรียกเมธอดนั้นส่งผ่านคำร้องขอเป็นอาร์กิวเมนต์

เมื่อคุณเข้าใจความคิดนี้จะเป็นการขยายฟังก์ชั่นการใช้งานของเว็บแอปพลิเคชันได้อย่างง่ายดายเพียงเพิ่มวิธีการใหม่ลงในคลาสตัวควบคุมแล้วสร้างลิงก์ในหน้าเว็บของคุณที่ใช้ URL ที่เหมาะสมสำหรับวิธีการเหล่านั้น getattrทั้งหมดนี้ทำไปได้โดย


13

นี่เป็นตัวอย่างรวดเร็วและสกปรกของวิธีการในชั้นเรียนสามารถยิงรุ่นต่าง ๆ 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()

7

นอกเหนือจากคำตอบที่น่าอัศจรรย์ทั้งหมดที่นี่ยังมีวิธีใช้getattrในการบันทึกบรรทัดโค้ดจำนวนมากและทำให้มันสบาย ความคิดนี้เกิดขึ้นหลังจากการแสดงรหัสที่น่ากลัวซึ่งบางครั้งอาจมีความจำเป็น

สถานการณ์

สมมติว่าโครงสร้างไดเรกทอรีของคุณเป็นดังนี้:

- superheroes.py
- properties.py

และคุณจะมีฟังก์ชั่นสำหรับการรับข้อมูลเกี่ยวกับThor, Iron Man, ในDoctor Strange superheroes.pyคุณเขียนคุณสมบัติของพวกมันทั้งหมดไว้ในproperties.pyคอมแพคdictแล้วก็เข้าถึงมันอย่างชาญฉลาด

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'],
}

superheroes.pyตอนนี้ขอบอกว่าคุณต้องการที่จะกลับขีดความสามารถของแต่ละของพวกเขาเกี่ยวกับความต้องการใน ดังนั้นมีฟังก์ชั่นดังนี้

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

... และฟังก์ชั่นอื่น ๆ ที่คืนค่าที่แตกต่างกันตามคีย์และซูเปอร์ฮีโร่

ด้วยความช่วยเหลือของgetattrคุณสามารถทำสิ่งที่ชอบ:

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

คุณลดจำนวนบรรทัดรหัสฟังก์ชันและการทำซ้ำอย่างมาก!

โอ้และแน่นอนถ้าคุณมีชื่อไม่ดีเช่นproperties_of_thorสำหรับตัวแปรพวกเขาสามารถสร้างและเข้าถึงได้โดยเพียงแค่ทำ

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

หมายเหตุ: สำหรับปัญหานี้โดยเฉพาะอาจมีวิธีที่ชาญฉลาดในการจัดการกับสถานการณ์ แต่ความคิดคือการให้ข้อมูลเชิงลึกเกี่ยวกับการใช้getattrในสถานที่ที่เหมาะสมในการเขียนรหัสทำความสะอาด


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
คุณช่วยอธิบายคำตอบของคุณให้ละเอียดมากขึ้นโดยเพิ่มคำอธิบายเพิ่มเติมเล็กน้อยเกี่ยวกับโซลูชันที่คุณให้ไว้ได้หรือไม่?
abarisone

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และสิ่งนี้จะทำให้setคำสั่งElementTree ของฉันล้มเหลว

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นี่คือเวลาและวิธีการที่ผมใช้


3

การใช้ getattr () อื่นในการนำคำสั่ง switch ไปใช้ใน Python ใช้การสะท้อนทั้งสองเพื่อรับชนิดเคส

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

ฉันชอบคำตอบนี้ แต่โปรดแก้ไขความผิดพลาดเล็ก ๆ
อาจ

2

setattr ()

เราใช้setattrเพื่อเพิ่มคุณสมบัติให้กับอินสแตนซ์ชั้นเรียนของเรา เราผ่านอินสแตนซ์ของคลาสชื่อแอตทริบิวต์และค่า

getattr ()

ด้วยgetattrเราจะเรียกคืนค่าเหล่านี้

ตัวอย่างเช่น

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

ฉันคิดว่าตัวอย่างนี้อธิบายได้ด้วยตนเอง มันรันวิธีการของพารามิเตอร์แรกที่มีชื่อจะได้รับในพารามิเตอร์ที่สอง

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

นอกจากนี้ยังชี้แจงจาก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)

อายุคือ 23

อายุคือ 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'))

เพศคือ: ชาย

AttributeError: วัตถุ 'บุคคล' ไม่มีแอตทริบิวต์ 'เพศ'


0

ฉันได้ลองใน Python2.7.17 แล้ว

เพื่อนบางคนตอบแล้ว อย่างไรก็ตามฉันได้ลองเรียกใช้ getattr (obj, 'set_value') และสิ่งนี้ไม่ได้ดำเนินการเมธอด set_value ดังนั้นฉันจึงเปลี่ยนเป็น getattr (obj, 'set_value') () -> สิ่งนี้ช่วยให้เรียกสิ่งเดียวกันได้

รหัสตัวอย่าง:

ตัวอย่างที่ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.