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

问题描述 投票:240回答:12

我是reading about the getattr() function。问题是我仍然无法掌握它的用法。我对getattr()唯一了解的是getattr(li, "pop")与调用li.pop相同。

我不明白这本书何时提到你如何使用它来获取函数的引用而不知道它的名字直到运行时。也许这就是我在编程方面的一般菜鸟。任何人都可以对这个问题有所了解吗?我何时以及如何使用它?

python getattr
12个回答
60
投票

您可以在此处查看完整示例:

内省可以用于不同的目的,“Dive Into Python”中提供的内容只是一种在应用程序中动态添加功能(插件)的方法。

通过动态我的意思是不在核心应用程序中进行修改以添加新功能。

以'Dive Into Python'为例 - 一个从不同文件的文件中提取属性的简单应用程序 - 您可以添加新文件格式的处理,而无需修改原始应用程序。

我建议你完成这本书。当你阅读时,一切都会变得越来越清晰。


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

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

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:'Person'对象没有属性'sex'


265
投票

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

您可以使用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

实际用途是找到名称以testcall them开头的所有方法。

getattr类似,有setattr,它允许您设置具有其名称的对象的属性:

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

89
投票

对我来说,getattr最容易解释这种方式:

它允许您根据字符串的内容调用方法,而不是键入方法名称。

例如,你不能这样做:

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

因为x不是“builtin”类型,而是“str”类型。但是,你可以这样做:

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

它允许您根据输入动态连接对象。我发现它在处理自定义对象和模块时很有用。


43
投票

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

例如,在像Django或Pylons这样的Web框架中,getattr可以直接将Web请求的URL映射到将要处理它的函数。例如,如果你看看Pylons的路由引擎,你会看到(默认情况下,至少)它会删除一个请求的URL,例如:

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

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

一旦掌握了这个想法,扩展Web应用程序的功能变得非常容易:只需向控制器类添加新方法,然后在页面中创建使用这些方法的相应URL的链接。所有这一切都是由getattr实现的。


12
投票

下面是一个快速而又脏的示例,说明类如何根据使用getattr()执行的操作系统来触发不同版本的save方法。

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

6
投票

除了这里所有令人惊讶的答案之外,还有一种方法可以使用getattr来保存丰富的代码并使其保持舒适。这个想法是在代码的可怕代表之后发生的,而代码有时可能是必要的。

脚本

假设您的目录结构如下:

- superheroes.py
- properties.py

并且,您可以在Thor获取有关Iron 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来编写更清晰的代码。


4
投票

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

主要有两种情况,getattr可能有用。

  • 你不能写object.x,因为你事先不知道你想要哪个属性(例如:它来自一个字符串)
  • 您想提供默认值。如果没有object.yAttributeError将筹集y。但getattr(object, 'y', 5)将返回5

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__(..)方法的分心,理想情况下应该是关于概念状态的class,而不是由于技术原因(例如优化)仅由函数的某个方法使用的某个实用程序计数器,并且与对象的含义无关。


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

© www.soinside.com 2019 - 2024. All rights reserved.