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

Ter*_*nce 264 python getattr

我正在阅读有关该getattr()功能的内容.问题是我仍然无法掌握它的用法.我唯一理解的getattr()getattr(li, "pop")和调用一样li.pop.

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

war*_*iuc 276

Python中的对象可以具有属性 - 数据属性和函数以使用这些属性和方法(方法).实际上,每个对象都有内置属性.

例如你有一个对象person,一个具有多个属性:name,gender,等.

您可以访问这些属性(无论是方法或数据对象),通常写作:person.name,person.gender,person.the_method()等.

但是如果你在编写程序时不知道属性的名称怎么办?例如,您将属性的名称存储在名为的变量中attr_name.

如果

attr_name = 'gender'
Run Code Online (Sandbox Code Playgroud)

然后,而不是写作

gender = person.gender
Run Code Online (Sandbox Code Playgroud)

你可以写

gender = getattr(person, attr_name)
Run Code Online (Sandbox Code Playgroud)

一些做法:

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
Run Code Online (Sandbox Code Playgroud)

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

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

但是你可以传递一个默认值作为第三个参数,如果这个属性不存在,它将被返回:

>>> getattr(person, 'age', 0)
0
Run Code Online (Sandbox Code Playgroud)

您可以使用getattrwith with 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
Run Code Online (Sandbox Code Playgroud)

这样做的一个实际用途是找到名称以其开头test调用它们的所有方法.

类似getattr还有setattr它允许你设定有其名称的对象的属性:

>>> setattr(person, 'name', 'Andrew')
>>> person.name  # accessing instance attribute
'Andrew'
>>> Person.name  # accessing class attribute
'Victor'
>>>
Run Code Online (Sandbox Code Playgroud)

  • 所以在我看来,`getattr(..)`应该在2个场景中使用:1.当属性名称是变量内部的值时(例如`getattr(person,some_attr)`)和2.当我们需要时使用第三个位置参数作为默认值(例如`getattr(person,'age',24)`).如果我看到像'getattr(person,'age')这样的场景,我觉得它与`person.age`相同,这让我觉得`person.age`更像是Pythonic.那是对的吗? (9认同)
  • “可读性很重要”。当然 `person.age` 比 `getattr(person, "age")` 更好。当变量中有属性名称时,我使用“getattr”是有意义的。 (4认同)

Nuc*_*eon 95

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

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

例如,你不能这样做:

obj = MyObject()
for x in ['foo', 'bar']:
    obj.x()
Run Code Online (Sandbox Code Playgroud)

因为x不是"builtin"类型,而是"str"类型.但是,你可以这样做:

obj = MyObject()
for x in ['foo', 'bar']:
    getattr(obj, x)()
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个非常直接和准确的答案。 (3认同)

Rob*_*ney 43

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

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

http://www.example.com/customers/list
Run Code Online (Sandbox Code Playgroud)

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

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


blu*_*ote 28

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

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

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

  • 要复活死灵,另一个用例是标识符包含非法字符,如“.”或“-”(正如我现在正在处理的)。`getattr(obj, 'some.val')` 将在 obj.some.val 不起作用的地方起作用。 (5认同)
  • @skoh:好吧,实际上,开头语句提到了带有两个参数的“getattr”(这是等效的),第二个项目符号提到了带有 3 个参数的 getattr。即使它不一致,我也可能会放弃它,强调更重要。 (3认同)
  • @UlfGjerdingen:想想 javascript。`ox` 等价于 `o['x']`。但是第二个表达式可以与任何可以在运行时决定的“o[some_string]”一起使用(例如,根据用户输入或对象检查),而在第一个表达式中,“x”是固定的。 (2认同)

Jos*_*osh 12

下面是一个快速而又脏的示例,说明类如何根据正在使用的操作系统触发不同版本的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
Run Code Online (Sandbox Code Playgroud)

现在让我们在一个例子中使用这个类:

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()
Run Code Online (Sandbox Code Playgroud)


uni*_*xia 6

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

脚本

假设您的目录结构如下:

- superheroes.py
- properties.py
Run Code Online (Sandbox Code Playgroud)

而且,你有功能,让有关的信息Thor,Iron Man,Doctor 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'],
}
Run Code Online (Sandbox Code Playgroud)

现在,假设您希望按需返回每个功能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']
Run Code Online (Sandbox Code Playgroud)

...以及更多基于键和超级英雄返回不同值的函数.

借助于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']
Run Code Online (Sandbox Code Playgroud)

您大大减少了代码行数,功能和重复次数!

哦,当然,如果你有像properties_of_thor变量这样的坏名字,可以通过简单的方式制作和访问它们

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

注意:对于这个特殊问题,可以采用更智能的方法来处理这种情况,但我们的想法是提供有关getattr在正确的位置使用来编写更清晰代码的见解.