Kir*_*ser 9 python magic-methods getattr
我有接受dicts或其他对象的方法以及从这些对象中获取的"fields"的名称.如果对象是dict,则该方法用于__getitem__检索命名键,或者getattr用于检索命名属性.这在网络模板语言中非常常见.例如,在变色龙模板中,您可能具有:
<p tal:content="foo.keyname">Stuff goes here</p>
Run Code Online (Sandbox Code Playgroud)
如果你foo像dict一样传入{'keyname':'bar'},然后foo.keyname获取'keyname'键以获得'bar'.如果foo是类的实例,如:
class Foo(object):
keyname = 'baz'
Run Code Online (Sandbox Code Playgroud)
然后foo.keyname从keyname属性中获取值.变色龙本身实现了这个功能(在chameleon.py26模块中),如下所示:
def lookup_attr(obj, key):
try:
return getattr(obj, key)
except AttributeError as exc:
try:
get = obj.__getitem__
except AttributeError:
raise exc
try:
return get(key)
except KeyError:
raise exc
Run Code Online (Sandbox Code Playgroud)
我在自己的包中实现了它,如:
try:
value = obj[attribute]
except (KeyError, TypeError):
value = getattr(obj, attribute)
Run Code Online (Sandbox Code Playgroud)
问题是,这是一种非常常见的模式.我已经在很多模块中看到过这种方法或者与它非常类似的方法.那么为什么在语言的核心,或者至少在一个核心模块中不是这样的东西呢?如果做不到这一点,是那里怎么会以明确的方式应该写?
sen*_*rle 15
我有点半读你的问题,写下面的内容,然后重新阅读你的问题并意识到我已经回答了一个微妙的不同问题.但我认为以下实际上仍然提供了一个答案.如果你不这么认为,那么假装你曾经问过这个更普遍的问题,我认为这个问题包含在你的子问题中:
"为什么Python不提供任何内置的方法来将属性和项目视为可互换的?"
我对这个问题给予了相当多的思考,我认为答案非常简单.创建容器类型时,区分属性和项目非常重要.任何合理完善的容器类型都有许多属性 - 通常并不总是方法 - 使它能够以优雅的方式管理其内容.因此,例如,一个dict有items,values,keys,iterkeys等.这些属性都使用.表示法访问.另一方面,项目使用[]表示法访问.所以不会发生碰撞.
使用.表示法启用项目访问时会发生什么?突然间你有重叠的命名空间.你现在如何处理碰撞?如果您将dict子类化并赋予它此功能,则要么不能像items规则那样使用键,要么必须创建某种命名空间层次结构.第一个选项会创建一个繁琐,难以遵循且难以执行的规则.第二个选项会产生令人讨厌的复杂性,而不会完全解决碰撞问题,因为您仍需要使用备用接口来指定是否需要items项目或items属性.
现在,对于某些非常原始的类型,这是可以接受的.例如,这可能就是namedtuple标准库中存在的原因.(但是请注意,namedtuple受这些非常的问题,这可能是为什么它被作为一个工厂函数实现的(防止继承),并使用像怪异,私有方法的名称_asdict.)
创建一个object没有(公共)属性并setattr在其上使用的子类也非常非常容易.它甚至很容易覆盖__getitem__,__setitem__以及__delitem__调用__getattribute__,__setattr__并且__delattr__,使项目的访问刚刚成为语法糖getattr(),setattr()等等(虽然这是一个有点更值得怀疑,因为它创造多少有些意外的行为.)
但是对于任何一种你想要能够扩展和继承的完善的容器类,添加新的有用属性__getattr__ + __getitem__,坦率地说,混合体将是一个巨大的PITA.
您可以很容易地编写自己的dict子类,该子类本身就以这种方式运行。一个最小的实现,我喜欢称之为属性“堆”,如下所示:
class Pile(dict):
# raise AttributeError for missing key here to fulfill API
def __getattr__(self, key):
if key in self:
return self[key]
else:
raise AttributeError(key)
def __setattr__(self, key, value):
self[key] = value
Run Code Online (Sandbox Code Playgroud)
不幸的是,如果您需要能够处理传递给您的字典或包含属性的对象,而不是从一开始就控制该对象,那么这将无济于事。
在你的情况下,我可能会使用与你所拥有的非常相似的东西,除了将其分解为一个函数,这样我就不必一直重复它。
python标准库中最接近的是namedtuple(),http://docs.python.org/dev/library/collections.html#collections.namedtuple
Foo = namedtuple('Foo', ['key', 'attribute'])
foo = Foo(5, attribute=13)
print foo[1]
print foo.key
Run Code Online (Sandbox Code Playgroud)
或者您可以轻松定义自己的类型,它总是实际存储到它的dict中,但允许属性设置和获取的外观:
class MyDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
d = MyDict()
d.a = 3
d[3] = 'a'
print(d['a']) # 3
print(d[3]) # 'a'
print(d['b']) # Returns a keyerror
Run Code Online (Sandbox Code Playgroud)
但不要这样做,d.3因为这是一个语法错误.当然有更复杂的方法来制作这样的混合存储类型,在网上搜索许多例子.
至于如何检查两者,变色龙的方式看起来很彻底.当谈到'为什么没有办法在标准库中同时做'时,这是因为歧义是不好的.是的,我们在python中有类型和所有其他类型的伪装,并且类无论如何都只是字典,但是在某些时候我们想要一个像dict或list这样的容器的不同功能,而不是我们想要的类,它的方法解析顺序,压倒等