Python:如何实现 __getattr__()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16237659/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python: how to implement __getattr__()?
提问by TieDad
My class has a dict, for example:
我的班级有一个字典,例如:
class MyClass(object):
def __init__(self):
self.data = {'a': 'v1', 'b': 'v2'}
Then I want to use the dict's key with MyClass instance to access the dict, for example:
然后我想将字典的键与 MyClass 实例一起使用来访问字典,例如:
ob = MyClass()
v = ob.a # Here I expect ob.a returns 'v1'
I know this should be implemented by __getattr__, but I'm new to Python, I don't exactly know how to implement it.
我知道这应该由 __getattr__ 实现,但我是 Python 新手,我不知道如何实现它。
采纳答案by jamylak
class MyClass(object):
def __init__(self):
self.data = {'a': 'v1', 'b': 'v2'}
def __getattr__(self, attr):
return self.data[attr]
>>> ob = MyClass()
>>> v = ob.a
>>> v
'v1'
Be careful when implementing __setattr__though, you will need to make a few modifications:
__setattr__但是在实施时要小心,您需要进行一些修改:
class MyClass(object):
def __init__(self):
# prevents infinite recursion from self.data = {'a': 'v1', 'b': 'v2'}
# as now we have __setattr__, which will call __getattr__ when the line
# self.data[k] tries to access self.data, won't find it in the instance
# dictionary and return self.data[k] will in turn call __getattr__
# for the same reason and so on.... so we manually set data initially
super(MyClass, self).__setattr__('data', {'a': 'v1', 'b': 'v2'})
def __setattr__(self, k, v):
self.data[k] = v
def __getattr__(self, k):
# we don't need a special call to super here because getattr is only
# called when an attribute is NOT found in the instance's dictionary
try:
return self.data[k]
except KeyError:
raise AttributeError
>>> ob = MyClass()
>>> ob.c = 1
>>> ob.c
1
If you don't need to set attributes just use a namedtuple eg.
如果您不需要设置属性,只需使用命名元组,例如。
>>> from collections import namedtuple
>>> MyClass = namedtuple("MyClass", ["a", "b"])
>>> ob = MyClass(a=1, b=2)
>>> ob.a
1
If you want the default arguments you can just write a wrapper class around it:
如果你想要默认参数,你可以围绕它编写一个包装类:
class MyClass(namedtuple("MyClass", ["a", "b"])):
def __new__(cls, a="v1", b="v2"):
return super(MyClass, cls).__new__(cls, a, b)
or maybe it looks nicer as a function:
或者它作为一个函数看起来更好:
def MyClass(a="v1", b="v2", cls=namedtuple("MyClass", ["a", "b"])):
return cls(a, b)
>>> ob = MyClass()
>>> ob.a
'v1'
回答by Adem ?zta?
class A(object):
def __init__(self):
self.data = {'a': 'v1', 'b': 'v2'}
def __getattr__(self, attr):
try:
return self.data[attr]
except:
return "not found"
>>>a = A()
>>>print a.a
v1
>>>print a.c
not found
回答by glglgl
I like to take this therefore.
因此我喜欢接受这个。
I took it from somewhere, but I don't remember where.
我从某个地方拿来的,但我不记得在哪里。
class A(dict):
def __init__(self, *a, **k):
super(A, self).__init__(*a, **k)
self.__dict__ = self
This makes the __dict__of the object the same as itself, so that attribute and item access map to the same dict:
这使得__dict__对象的 与自身相同,因此属性和项目访问映射到相同的字典:
a = A()
a['a'] = 2
a.b = 5
print a.a, a['b'] # prints 2 5
回答by Adam Haile
I figured out an extension to @glglgl's answer that handles nested dictionaries and dictionaries insides lists that are in the original dictionary:
我想出了@glglgl 答案的扩展,它处理原始字典中的嵌套字典和字典内部列表:
class d(dict):
def __init__(self, *a, **k):
super(d, self).__init__(*a, **k)
self.__dict__ = self
for k in self.__dict__:
if isinstance(self.__dict__[k], dict):
self.__dict__[k] = d(self.__dict__[k])
elif isinstance(self.__dict__[k], list):
for i in range(len(self.__dict__[k])):
if isinstance(self.__dict__[k][i], dict):
self.__dict__[k][i] = d(self.__dict__[k][i])
回答by Tjaart
You can initialize your class dictionary through the constructor:
您可以通过构造函数初始化您的类字典:
def __init__(self,**data):
And call it as follows:
并按如下方式调用它:
f = MyClass(**{'a': 'v1', 'b': 'v2'})
All of the instance attributes being accessed(read) in __setattr__, need to be declaredusing its parent (super) method, only once:
在 __setattr__中访问(读取)的所有实例属性,都需要使用其父(超级)方法声明一次:
super().__setattr__('NewVarName1', InitialValue)
Or
或者
super().__setattr__('data', dict())
Thereafter, they can be accessed or assigned to in the usual manner:
此后,可以以通常的方式访问或分配它们:
self.data = data
And instance attributes not being accessedin __setattr__, can be declared in the usual manner:
而在 __setattr__ 中没有被访问的实例属性,可以用通常的方式声明:
self.x = 1
The overridden __setattr__ method must now call the parent method inside itself, for new variables to be declared:
重写的 __setattr__ 方法现在必须在其自身内部调用父方法,以便声明新变量:
super().__setattr__(key,value)
A complete class would look as follows:
一个完整的类如下所示:
class MyClass(object):
def __init__(self, **data):
# The variable self.data is used by method __setattr__
# inside this class, so we will need to declare it
# using the parent __setattr__ method:
super().__setattr__('data', dict())
self.data = data
# These declarations will jump to
# super().__setattr__('data', dict())
# inside method __setattr__ of this class:
self.x = 1
self.y = 2
def __getattr__(self, name):
# This will callback will never be called for instance variables
# that have beed declared before being accessed.
if name in self.data:
# Return a valid dictionary item:
return self.data[name]
else:
# So when an instance variable is being accessed, and
# it has not been declared before, nor is it contained
# in dictionary 'data', an attribute exception needs to
# be raised.
raise AttributeError
def __setattr__(self, key, value):
if key in self.data:
# Assign valid dictionary items here:
self.data[key] = value
else:
# Assign anything else as an instance attribute:
super().__setattr__(key,value)
Test:
测试:
f = MyClass(**{'a': 'v1', 'b': 'v2'})
print("f.a = ", f.a)
print("f.b = ", f.b)
print("f.data = ", f.data)
f.a = 'c'
f.d = 'e'
print("f.a = ", f.a)
print("f.b = ", f.b)
print("f.data = ", f.data)
print("f.d = ", f.d)
print("f.x = ", f.x)
print("f.y = ", f.y)
# Should raise attributed Error
print("f.g = ", f.g)
Output:
输出:
f.a = v1
f.b = v2
f.data = {'a': 'v1', 'b': 'v2'}
f.a = c
f.b = v2
f.data = {'a': 'c', 'b': 'v2'}
f.d = e
f.x = 1
f.y = 2
Traceback (most recent call last):
File "MyClass.py", line 49, in <module>
print("f.g = ", f.g)
File "MyClass.py", line 25, in __getattr__
raise AttributeError
AttributeError
回答by mxl
I think this implement is cooler
我觉得这个工具更酷
class MyClass(object):
def __init__(self):
self.data = {'a': 'v1', 'b': 'v2'}
def __getattr__(self,key):
return self.data.get(key,None)
回答by marcelocra
Late to the party, but found two really good resources that explain this better (IMHO).
聚会迟到了,但发现两个非常好的资源可以更好地解释这一点(恕我直言)。
As explained here, you should use self.__dict__to access fields from within __getattr__, in order to avoid infinite recursion. The example provided is:
正如解释在这里,你应该使用self.__dict__访问域从内部__getattr__,以避免无限递归。提供的例子是:
def __getattr__(self, attrName): if not self.__dict__.has_key(attrName): value = self.fetchAttr(attrName) # computes the value self.__dict__[attrName] = value return self.__dict__[attrName]
def __getattr__(self, attrName): if not self.__dict__.has_key(attrName): value = self.fetchAttr(attrName) # computes the value self.__dict__[attrName] = value return self.__dict__[attrName]
Note: in the second line (above), a more Pythonic way would be (has_keyapparently was even removed in Python 3):
注意:在第二行(上面)中,一种更 Pythonic 的方式是(has_key显然在 Python 3 中甚至被删除了):
if attrName not in self.__dict__:
The other resourceexplains that the __getattr__is invoked only when the attribute is not found in the object, and that hasattralways returns Trueif there is an implementation for __getattr__. It provides the following example, to demonstrate:
在其他资源解释说,__getattr__只有当对象没有找到,则调用,并且hasattr总是返回True如果有一个实现__getattr__。它提供了以下示例,以进行演示:
class Test(object): def __init__(self): self.a = 'a' self.b = 'b' def __getattr__(self, name): return 123456 t = Test() print 'object variables: %r' % t.__dict__.keys() #=> object variables: ['a', 'b'] print t.a #=> a print t.b #=> b print t.c #=> 123456 print getattr(t, 'd') #=> 123456 print hasattr(t, 'x') #=> True
class Test(object): def __init__(self): self.a = 'a' self.b = 'b' def __getattr__(self, name): return 123456 t = Test() print 'object variables: %r' % t.__dict__.keys() #=> object variables: ['a', 'b'] print t.a #=> a print t.b #=> b print t.c #=> 123456 print getattr(t, 'd') #=> 123456 print hasattr(t, 'x') #=> True
回答by Todd
A simple approach to solving your __getattr__()/__setattr__()infinite recursion woes
解决您的__getattr__()/__setattr__()无限递归问题的简单方法
Implementing one or the other of these magic methods can usually be easy. But when overriding them both, it becomes trickier. This post's examples apply mostly to this more difficult case.
实现这些魔术方法中的一种或另一种通常很容易。但是当覆盖它们两者时,它变得更加棘手。这篇文章的例子主要适用于这个更困难的案例。
When implementing both these magic methods, it's not uncommon to get stuck figuring out a strategy to get around recursion in the __init__()constructor of classes. This is because variables need to be initialized for the object, but every attempt to read or write those variables go through __get/set/attr__(), which could have more unset variables in them, incurring more futile recursive calls.
在实现这两种神奇的方法时,在__init__()类的构造函数中找出一种绕过递归的策略的情况并不少见。这是因为需要为对象初始化变量,但是每次读取或写入这些变量的尝试都会通过__get/set/attr__(),这可能会在其中包含更多未设置的变量,从而导致更多徒劳的递归调用。
Up front, a key point to remember is that __getattr__()only gets called by the runtime if the attribute can't be found on the object already. The trouble is to get attributes defined without tripping these functions recursively.
在前面,要记住的一个关键点是,__getattr__()只有在对象上找不到该属性时才会被运行时调用。麻烦的是在不递归地触发这些函数的情况下获得定义的属性。
Another point is __setattr__()will get called no matter what.That's an important distinction between the two functions, which is why implementing both attribute methods can be tricky.
另一点是__setattr__()无论如何都会被调用。这是两个函数之间的一个重要区别,这就是为什么实现这两个属性方法可能会很棘手。
This is one basic pattern that solves the problem.
这是解决问题的一种基本模式。
class AnObjectProxy:
_initialized = False # *Class* variable 'constant'.
def __init__(self):
self._any_var = "Able to access instance vars like usual."
self._initialized = True # *instance* variable.
def __getattr__(self, item):
if self._initialized:
pass # Provide the caller attributes in whatever ways interest you.
else:
try:
return self.__dict__[item] # Transparent access to instance vars.
except KeyError:
raise AttributeError(item)
def __setattr__(self, key, value):
if self._initialized:
pass # Provide caller ways to set attributes in whatever ways.
else:
self.__dict__[key] = value # Transparent access.
While the class is initializing and creating it's instance vars, the code in both attribute functions permits access to the object's attributes via the __dict__dictionary transparently - your code in __init__()can create and access instance attributes normally. When the attribute methods are called, they only access self.__dict__which is already defined, thus avoiding recursive calls.
当类正在初始化和创建它的实例变量时,两个属性函数中的代码都允许__dict__透明地通过字典访问对象的属性- 您的代码__init__()可以正常创建和访问实例属性。当属性方法被调用时,它们只访问self.__dict__已经定义的,从而避免递归调用。
In the case of self._any_var, once it's assigned, __get/set/attr__()won't be called to find it again.
在 的情况下self._any_var,一旦分配,__get/set/attr__()就不会再调用查找它。
Stripped of extra code, these are the two pieces that are most important.
除去额外的代码,这是最重要的两部分。
... def __getattr__(self, item):
... try:
... return self.__dict__[item]
... except KeyError:
... raise AttributeError(item)
...
... def __setattr__(self, key, value):
... self.__dict__[key] = value
Solutions can build around these lines accessing the __dict__dictionary. To implement an object proxy, two modes were implemented: initialization and post-initialization in the code before this - a more detailed example of the same is below.
解决方案可以围绕访问__dict__字典的这些行构建。为了实现一个对象代理,在此之前的代码中实现了两种模式:初始化和后初始化——下面是一个更详细的例子。
There are other examples in answers that may have differing levels of effectiveness in dealing with allaspects of recursion. One effective approach is accessing __dict__directly in __init__()and other places that need early access to instance vars. This works but can be a little verbose. For instance,
答案中还有其他示例,它们在处理递归的所有方面时可能具有不同程度的有效性。一种有效的方法是__dict__直接访问__init__()需要提前访问实例变量的其他地方。这有效,但可能有点冗长。例如,
self.__dict__['_any_var'] = "Setting..."
would work in __init__().
会在__init__().
My posts tend to get a little long-winded.. after this point is just extra. You should already have the idea with the examples above.
我的帖子往往有点冗长……在这一点之后只是多余的。你应该已经有了上面的例子的想法。
A drawback to some other approaches can be seen with debuggers in IDE's. They can be overzealous in their use of introspection and produce warning and error recovery messages as you're stepping through code. You can see this happening even with solutions that work fine standalone. When I say allaspects of recursion, this is what I'm talking about.
使用 IDE 中的调试器可以看到一些其他方法的缺点。当您单步执行代码时,他们可能会过度使用自省并产生警告和错误恢复消息。即使使用独立运行良好的解决方案,您也可以看到这种情况发生。当我说递归的所有方面时,这就是我所谈论的。
The examples in this post only use a single class variable to support 2-modes of operation, which is very maintainable.
这篇文章中的示例仅使用单个类变量来支持 2-mode 操作,这是非常易于维护的。
But please NOTE:the proxy class required two modes of operation to set up and proxy for an internal object. You don't have to have two modes of operation.
但请注意:代理类需要两种操作模式来设置和代理内部对象。您不必有两种操作模式。
You could simply incorporate the code to access the __dict__as in these examples in whatever ways suit you.
您可以简单地合并代码,__dict__以任何适合您的方式访问这些示例中的as。
If your requirements don't include two modes of operation, you may not need to declare any class variables at all.Just take the basic pattern and customize it.
如果您的要求不包括两种操作模式,您可能根本不需要声明任何类变量。只需采用基本模式并对其进行自定义即可。
Here's a closerto real-world (but by no means complete) example of a 2-mode proxy that follows the pattern:
这是一个更接近真实世界(但绝不是完整的)的遵循模式的 2 模式代理示例:
>>> class AnObjectProxy:
... _initialized = False # This class var is important. It is always False.
... # The instances will override this with their own,
... # set to True.
... def __init__(self, obj):
... # Because __getattr__ and __setattr__ access __dict__, we can
... # Initialize instance vars without infinite recursion, and
... # refer to them normally.
... self._obj = obj
... self._foo = 123
... self._bar = 567
...
... # This instance var overrides the class var.
... self._initialized = True
...
... def __setattr__(self, key, value):
... if self._initialized:
... setattr(self._obj, key, value) # Proxying call to wrapped obj.
... else:
... # this block facilitates setting vars in __init__().
... self.__dict__[key] = value
...
... def __getattr__(self, item):
... if self._initialized:
... attr = getattr(self._obj, item) # Proxying.
... return attr
... else:
... try:
... # this block facilitates getting vars in __init__().
... return self.__dict__[item]
... except KeyError:
... raise AttributeError(item)
...
... def __call__(self, *args, **kwargs):
... return self._obj(*args, **kwargs)
...
... def __dir__(self):
... return dir(self._obj) + list(self.__dict__.keys())
The 2-mode proxy only needs a bit of "bootstrapping" to access vars in its own scope at initialization before any of its vars are set. After initialization, the proxy has no reason to create more vars for itself, so it will fare fine by deferring all attribute calls to it's wrapped object.
2-mode 代理只需要一点“引导”就可以在初始化时访问它自己范围内的变量,然后再设置它的任何变量。初始化后,代理没有理由为自己创建更多的变量,因此通过将所有属性调用推迟到它的包装对象,它会很好。
Any attribute the proxy itself owns will still be accessible to itself and other callers since the magic attribute functions only get called if an attribute can't be found immediately on the object.
代理本身拥有的任何属性仍然可以被它自己和其他调用者访问,因为只有在无法立即在对象上找到属性时才会调用魔法属性函数。
Hopefully this approach can be of benefit to anyone who appreciates a direct approach to resolving their __get/set/attr__()__init__()frustrations.
希望这种方法可以对任何欣赏直接方法来解决他们的__get/set/attr__()__init__()挫折的人有益。

