到底什么是getattr(),我该如何使用它?


295

我最近阅读了有关该getattr()功能的信息。问题是我仍然无法理解其用法。据我所知的唯一的事情getattr()getattr(li, "pop")相同调用li.pop

当书中提到您是如何使用它来获得对函数的引用时,直到运行时才知道它的名称,我不明白。总的来说,也许这是我在编程方面的菜鸟。有人可以阐明这个话题吗?我什么时候以及如何使用它?


您在哪部分遇到问题?属性为字符串?一流的功能?
伊格纳西奥·巴斯克斯

1
我认为我的问题是了解getattr()的概念。我仍然不明白它的目的。
特伦斯·庞塞

@Terence我的回答是否使事情更清楚?
阿洛伊斯·科查德

@Alois,您的回答肯定消除了我的一些疑问,但我仍然无法完全理解getattr()的用途。
Terence Ponce 2010年

6
@ S.Lott,我做到了。该文档仅具有定义,因此我对其用法感到困惑。在阅读了更多内容之后,我现在了解了getattr。
Terence Ponce 2010年

Answers:


87

getattr(object, 'x') 完全等同object.x

只有两种情况,其中getattr可能是有用的。

  • 您无法编写object.x,因为您事先不知道想要哪个属性(它来自字符串)。对于元编程非常有用。
  • 您想要提供一个默认值。如果没有object.y则将引发一个。但是会回来的。AttributeErrorygetattr(object, 'y', 5)5

2
我认为这应该是公认的答案。很清楚,很明确。
yuqli

290

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

getattrAttributeError如果对象中不存在具有给定名称的属性,则将引发:

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

您可以使用getattrdir来迭代所有属性名称并获取它们的值:

>>> 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(..)应该在两种情况下使用:1.当属性名称是变量内部的值(例如getattr(person, some_attr))时; 2.当我们需要将第三个位置参数用作默认值时(例如getattr(person, 'age', 24))。如果我看到这样的场景,getattr(person, 'age')那就好像它完全相同,person.age这使我认为这person.age更像Python。那是对的吗?
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
这是一个非常简单而准确的答案。
user6037143

43

一个很常见的用例getattr是将数据映射到函数。

例如,在Django或Pylons之类的Web框架中,getattr可以很容易地将Web请求的URL映射到将要处理它的函数。例如,如果您看一下Pylons路由的内幕,您会发现(至少默认情况下)它会截取请求的URL,例如:

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

分为“客户”和“列表”。然后,它搜索名为的控制器类CustomerController。假设找到该类,则创建该类的实例,然后用于getattr获取其list方法。然后,它将调用该方法,并将请求作为参数传递给该方法。

一旦掌握了这个想法,就可以轻松扩展Web应用程序的功能:只需将新方法添加到控制器类中,然后在页面中创建链接,并为这些方法使用适当的URL。所有这些都可以通过来实现getattr


13

这是一个简单而又肮脏的示例,它说明了一个类如何根据使用的操作系统来触发不同版本的save方法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

而且,你有功能,让有关的信息ThorIron ManDoctor Strangesuperheroes.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 2015年

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

如果属性值为type Nonegetattr还将返回它,这将导致我的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()在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]))

我喜欢这个答案,但请修复小错别字

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.