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

我最近阅读有关getattr()函数的内容。问题是我仍然不能理解它的用法。我对getattr()的唯一理解是,getattr(li, "pop")与调用li.pop相同。

我不理解书中提到如何使用它来获取函数的引用,而直到运行时才知道它的名称。也许这就是我在编程方面的新手。谁能解释一下这个问题?我何时以及如何使用它?

338735 次浏览

下面是一个快速而简单的例子,说明一个类如何根据使用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()

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

例如,在Django或Pylons这样的web框架中,getattr可以直接将web请求的URL映射到将要处理它的函数。例如,如果你看一下Pylons的路由,你会发现(至少在默认情况下)它分割了一个请求的URL,比如:

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

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

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

Python中的对象可以具有属性——数据属性和使用这些属性的函数(方法)。实际上,每个对象都有内置属性(在Python控制台中尝试dir(None)dir(True)dir(...)dir(dir))。

例如,你有一个对象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

它的一个实际用途是找到所有名称以test打电话给他们开头的方法。

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

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

对我来说,getattr是最容易解释的方式:

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

例如,你不能这样做:

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

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

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

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

# 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')

我有时使用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__(..)方法造成干扰,理想情况下,__init__(..)方法应该是关于类的概念状态,而不是一些仅由函数的一个方法出于技术原因(如优化)使用的实用计数器,与对象的含义无关。

相当频繁地,当我从存储在类中的数据创建XML文件时,如果属性不存在或类型为None,我经常会收到错误。在本例中,我的问题不是不知道属性名是什么,而是存储在该属性中的数据。

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

如果我使用hasattr来做到这一点,即使属性值是None类型,它也会返回True,这将导致我的ElementTree set命令失败。

hasattr(temp, 'hair')
>>True

如果属性值的类型是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

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

除了这里所有令人惊讶的答案之外,还有一种方法可以使用getattr来节省大量的代码行,并保持它的舒适。这种想法源于代码的可怕表示,有时可能是必要的。

场景

假设你的目录结构如下:

- superheroes.py
- properties.py

并且,你有函数在superheroes.py中获取关于ThorIron ManDoctor Strange的信息。你非常聪明地在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来编写更清晰的代码。

它还从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'

getattr(object, 'x') 是完全等价的object.x

getattr可能有用的地方有只有两件

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

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)

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

我认为这个例子是不言自明的。它运行第一个参数的方法,其名称在第二个参数中给出。

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