标签归档:getattr

__getattr__在模块上

问题:__getattr__在模块上

如何实现等效的 __getattr__在类,模块上于a?

当调用模块的静态定义的属性中不存在的函数时,我希望在该模块中创建一个类的实例,并使用与该模块上的属性查找失败相同的名称调用该方法。

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
    return getattr(A(), name)

if __name__ == "__main__":
    # i hope here to have my __getattr__ function above invoked, since
    # salutation does not exist in the current namespace
    salutation("world")

这使:

matt@stanley:~/Desktop$ python getattrmod.py 
Traceback (most recent call last):
  File "getattrmod.py", line 9, in <module>
    salutation("world")
NameError: name 'salutation' is not defined

How can implement the equivalent of a __getattr__ on a class, on a module?

Example

When calling a function that does not exist in a module’s statically defined attributes, I wish to create an instance of a class in that module, and invoke the method on it with the same name as failed in the attribute lookup on the module.

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
    return getattr(A(), name)

if __name__ == "__main__":
    # i hope here to have my __getattr__ function above invoked, since
    # salutation does not exist in the current namespace
    salutation("world")

Which gives:

matt@stanley:~/Desktop$ python getattrmod.py 
Traceback (most recent call last):
  File "getattrmod.py", line 9, in <module>
    salutation("world")
NameError: name 'salutation' is not defined

回答 0

不久前,Guido宣布对新型类的所有特殊方法查找都绕过__getattr__and__getattribute__。Dunder方法曾经工作的模块-你可以,例如,使用一个模块作为一个上下文管理器简单地通过定义__enter____exit__,这些技巧之前爆发

最近,一些历史功能已经卷土重来,其中的一个模块已被卷土重来,__getattr__因此,sys.modules不再需要现有的hack(在导入时将一个模块替换为一个类)。

在Python 3.7+中,您仅使用一种显而易见的方法。要自定义模块上的属性访问,请__getattr__在模块级别定义一个函数,该函数应接受一个参数(属性名称),然后返回计算值或引发一个AttributeError

# my_module.py

def __getattr__(name: str) -> Any:
    ...

这也将允许钩子插入“ from”导入,即,您可以为语句(例如)返回动态生成的对象 from my_module import whatever

与此相关的是,您还可以与模块getattr一起__dir__在模块级别定义一个函数以响应dir(my_module)。有关详细信息,请参见PEP 562

A while ago, Guido declared that all special method lookups on new-style classes bypass __getattr__ and __getattribute__. Dunder methods had previously worked on modules – you could, for example, use a module as a context manager simply by defining __enter__ and __exit__, before those tricks broke.

Recently some historical features have made a comeback, the module __getattr__ among them, and so the existing hack (a module replacing itself with a class in sys.modules at import time) should be no longer necessary.

In Python 3.7+, you just use the one obvious way. To customize attribute access on a module, define a __getattr__ function at the module level which should accept one argument (name of attribute), and return the computed value or raise an AttributeError:

# my_module.py

def __getattr__(name: str) -> Any:
    ...

This will also allow hooks into “from” imports, i.e. you can return dynamically generated objects for statements such as from my_module import whatever.

On a related note, along with the module getattr you may also define a __dir__ function at module level to respond to dir(my_module). See PEP 562 for details.


回答 1

您在这里遇到两个基本问题:

  1. __xxx__ 方法只在类上查找
  2. TypeError: can't set attributes of built-in/extension type 'module'

(1)表示任何解决方案还必须跟踪正在检查的模块,否则每个模块将具有实例替换行为;(2)表示(1)甚至是不可能的……至少不是直接的。

幸运的是,sys.modules对那里发生的事情并不挑剔,因此可以使用包装器,但是只能用于模块访问(即import somemodule; somemodule.salutation('world'),对于相同模块的访问,您几乎必须从替换类中提取方法并将其添加到globals()eiher中。类上的自定义方法(我喜欢使用.export())或具有泛型函数(例如已经列出的答案)要记住的一件事:如果包装器每次都创建一个新实例,而全局解决方案不是,最终,您的行为会有所不同。哦,您不能同时使用两者-一种是另一种。


更新资料

Guido van Rossum出发:

实际上,偶尔会使用并推荐一种hack:一个模块可以用所需的功能定义一个类,然后最后,用该类的实例(如果需要,可以用该类)替换sys.modules中的自身。 ,但通常用处不大)。例如:

# module foo.py

import sys

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>

sys.modules[__name__] = Foo()

之所以可行,是因为导入机制正在积极地启用此hack,并且在加载的最后一步是将实际模块从sys.modules中拉出。(这绝非偶然。黑客是在很久以前就提出的,我们认为我们很喜欢在进口机器中提供支持。)

因此,完成所需操作的既定方法是在模块中创建一个类,并且作为模块的最后一步,sys.modules[__name__]用您的类的实例替换-现在您可以根据需要使用__getattr__/ __setattr__/ __getattribute__进行操作。


注意1:如果您使用此功能,则在进行sys.modules分配时,模块中的所有其他内容(例如全局变量,其他函数等)都会丢失-因此请确保所需的所有内容都在替换类之内。

注意2:要支持from module import *您必须__all__在类中进行定义;例如:

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>
    __all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})

根据您的Python版本,可能会省略其他名称__all__set()如果不需要Python 2兼容性,可以省略。

There are two basic problems you are running into here:

  1. __xxx__ methods are only looked up on the class
  2. TypeError: can't set attributes of built-in/extension type 'module'

(1) means any solution would have to also keep track of which module was being examined, otherwise every module would then have the instance-substitution behavior; and (2) means that (1) isn’t even possible… at least not directly.

Fortunately, sys.modules is not picky about what goes there so a wrapper will work, but only for module access (i.e. import somemodule; somemodule.salutation('world'); for same-module access you pretty much have to yank the methods from the substitution class and add them to globals() eiher with a custom method on the class (I like using .export()) or with a generic function (such as those already listed as answers). One thing to keep in mind: if the wrapper is creating a new instance each time, and the globals solution is not, you end up with subtly different behavior. Oh, and you don’t get to use both at the same time — it’s one or the other.


Update

From Guido van Rossum:

There is actually a hack that is occasionally used and recommended: a module can define a class with the desired functionality, and then at the end, replace itself in sys.modules with an instance of that class (or with the class, if you insist, but that’s generally less useful). E.g.:

# module foo.py

import sys

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>

sys.modules[__name__] = Foo()

This works because the import machinery is actively enabling this hack, and as its final step pulls the actual module out of sys.modules, after loading it. (This is no accident. The hack was proposed long ago and we decided we liked enough to support it in the import machinery.)

So the established way to accomplish what you want is to create a single class in your module, and as the last act of the module replace sys.modules[__name__] with an instance of your class — and now you can play with __getattr__/__setattr__/__getattribute__ as needed.


Note 1: If you use this functionality then anything else in the module, such as globals, other functions, etc., will be lost when the sys.modules assignment is made — so make sure everything needed is inside the replacement class.

Note 2: To support from module import * you must have __all__ defined in the class; for example:

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>
    __all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})

Depending on your Python version, there may be other names to omit from __all__. The set() can be omitted if Python 2 compatibility is not needed.


回答 2

这是一个技巧,但是您可以使用一个类包装模块:

class Wrapper(object):
  def __init__(self, wrapped):
    self.wrapped = wrapped
  def __getattr__(self, name):
    # Perform custom logic here
    try:
      return getattr(self.wrapped, name)
    except AttributeError:
      return 'default' # Some sensible default

sys.modules[__name__] = Wrapper(sys.modules[__name__])

This is a hack, but you can wrap the module with a class:

class Wrapper(object):
  def __init__(self, wrapped):
    self.wrapped = wrapped
  def __getattr__(self, name):
    # Perform custom logic here
    try:
      return getattr(self.wrapped, name)
    except AttributeError:
      return 'default' # Some sensible default

sys.modules[__name__] = Wrapper(sys.modules[__name__])

回答 3

我们通常不那样做。

我们要做的就是这个。

class A(object):
....

# The implicit global instance
a= A()

def salutation( *arg, **kw ):
    a.salutation( *arg, **kw )

为什么?使隐式全局实例可见。

例如,查看random模块,该模块创建一个隐式全局实例,以稍微简化您需要“简单”随机数生成器的用例。

We don’t usually do it that way.

What we do is this.

class A(object):
....

# The implicit global instance
a= A()

def salutation( *arg, **kw ):
    a.salutation( *arg, **kw )

Why? So that the implicit global instance is visible.

For examples, look at the random module, which creates an implicit global instance to slightly simplify the use cases where you want a “simple” random number generator.


回答 4

与@HåvardS提出的类似,在我需要在模块上实现一些魔术的情况下(例如__getattr__),我将定义一个继承types.ModuleType并放入其中的新类sys.modules(可能替换自定义模块ModuleType定义了定义)。

请参阅Werkzeug的主__init__.py文件,以实现此功能的强大功能。

Similar to what @Håvard S proposed, in a case where I needed to implement some magic on a module (like __getattr__), I would define a new class that inherits from types.ModuleType and put that in sys.modules (probably replacing the module where my custom ModuleType was defined).

See the main __init__.py file of Werkzeug for a fairly robust implementation of this.


回答 5

这有点黑,但是…

import types

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

    def farewell(self, greeting, accusative):
         print greeting, accusative

def AddGlobalAttribute(classname, methodname):
    print "Adding " + classname + "." + methodname + "()"
    def genericFunction(*args):
        return globals()[classname]().__getattribute__(methodname)(*args)
    globals()[methodname] = genericFunction

# set up the global namespace

x = 0   # X and Y are here to add them implicitly to globals, so
y = 0   # globals does not change as we iterate over it.

toAdd = []

def isCallableMethod(classname, methodname):
    someclass = globals()[classname]()
    something = someclass.__getattribute__(methodname)
    return callable(something)


for x in globals():
    print "Looking at", x
    if isinstance(globals()[x], (types.ClassType, type)):
        print "Found Class:", x
        for y in dir(globals()[x]):
            if y.find("__") == -1: # hack to ignore default methods
                if isCallableMethod(x,y):
                    if y not in globals(): # don't override existing global names
                        toAdd.append((x,y))


for x in toAdd:
    AddGlobalAttribute(*x)


if __name__ == "__main__":
    salutation("world")
    farewell("goodbye", "world")

通过遍历全局命名空间中的所有对象来工作。如果该项目是一个类,则在类属性上进行迭代。如果该属性是可调用的,则将其作为函数添加到全局命名空间中。

它忽略所有包含“ __”的属性。

我不会在生产代码中使用它,但是它应该可以帮助您入门。

This is hackish, but…

import types

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

    def farewell(self, greeting, accusative):
         print greeting, accusative

def AddGlobalAttribute(classname, methodname):
    print "Adding " + classname + "." + methodname + "()"
    def genericFunction(*args):
        return globals()[classname]().__getattribute__(methodname)(*args)
    globals()[methodname] = genericFunction

# set up the global namespace

x = 0   # X and Y are here to add them implicitly to globals, so
y = 0   # globals does not change as we iterate over it.

toAdd = []

def isCallableMethod(classname, methodname):
    someclass = globals()[classname]()
    something = someclass.__getattribute__(methodname)
    return callable(something)


for x in globals():
    print "Looking at", x
    if isinstance(globals()[x], (types.ClassType, type)):
        print "Found Class:", x
        for y in dir(globals()[x]):
            if y.find("__") == -1: # hack to ignore default methods
                if isCallableMethod(x,y):
                    if y not in globals(): # don't override existing global names
                        toAdd.append((x,y))


for x in toAdd:
    AddGlobalAttribute(*x)


if __name__ == "__main__":
    salutation("world")
    farewell("goodbye", "world")

This works by iterating over the all the objects in the global namespace. If the item is a class, it iterates over the class attributes. If the attribute is callable it adds it to the global namespace as a function.

It ignore all attributes which contain “__”.

I wouldn’t use this in production code, but it should get you started.


回答 6

这是我自己的不起眼的贡献-@HåvardS的高度评价的答案略有修饰,但略显一点(因此@ S.Lott可以接受,尽管可能对OP不够好):

import sys

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

class Wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        try:
            return getattr(self.wrapped, name)
        except AttributeError:
            return getattr(A(), name)

_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])

if __name__ == "__main__":
    _globals.salutation("world")

Here’s my own humble contribution — a slight embellishment of @Håvard S’s highly rated answer, but a bit more explicit (so it might be acceptable to @S.Lott, even though probably not good enough for the OP):

import sys

class A(object):
    def salutation(self, accusative):
        print "hello", accusative

class Wrapper(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def __getattr__(self, name):
        try:
            return getattr(self.wrapped, name)
        except AttributeError:
            return getattr(A(), name)

_globals = sys.modules[__name__] = Wrapper(sys.modules[__name__])

if __name__ == "__main__":
    _globals.salutation("world")

回答 7

创建包含您的类的模块文件。导入模块。getattr在刚导入的模块上运行。您可以使用以下方式进行动态导入__import__ sys.modules中的模块。

这是您的模块some_module.py

class Foo(object):
    pass

class Bar(object):
    pass

在另一个模块中:

import some_module

Foo = getattr(some_module, 'Foo')

动态地执行此操作:

import sys

__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')

Create your module file that has your classes. Import the module. Run getattr on the module you just imported. You can do a dynamic import using __import__ and pull the module from sys.modules.

Here’s your module some_module.py:

class Foo(object):
    pass

class Bar(object):
    pass

And in another module:

import some_module

Foo = getattr(some_module, 'Foo')

Doing this dynamically:

import sys

__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')

如何实现__getattribute__而没有无限递归错误?

问题:如何实现__getattribute__而没有无限递归错误?

我想覆盖对类中一个变量的访问,但通常返回所有其他变量。我该怎么做__getattribute__呢?

我尝试了以下操作(它也应说明我要执行的操作),但是出现了递归错误:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

I want to override access to one variable in a class, but return all others normally. How do I accomplish this with __getattribute__?

I tried the following (which should also illustrate what I’m trying to do) but I get a recursion error:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

回答 0

您收到递归错误,因为您尝试访问其中的self.__dict__属性会再次__getattribute__调用您__getattribute__。如果你使用object__getattribute__不是,它的工作原理:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return object.__getattribute__(self, name)

之所以可行,是因为object(在此示例中)是基类。通过调用您的基本版本,__getattribute__可以避免您以前遇到的递归地狱。

IPython的输出与foo.py中的代码:

In [1]: from foo import *

In [2]: d = D()

In [3]: d.test
Out[3]: 0.0

In [4]: d.test2
Out[4]: 21

更新:

在当前文档中,标题为“ 针对新样式类的更多属性访问 ”的部分中有一些内容,他们建议完全这样做以避免无限递归。

You get a recursion error because your attempt to access the self.__dict__ attribute inside __getattribute__ invokes your __getattribute__ again. If you use object‘s __getattribute__ instead, it works:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return object.__getattribute__(self, name)

This works because object (in this example) is the base class. By calling the base version of __getattribute__ you avoid the recursive hell you were in before.

Ipython output with code in foo.py:

In [1]: from foo import *

In [2]: d = D()

In [3]: d.test
Out[3]: 0.0

In [4]: d.test2
Out[4]: 21

Update:

There’s something in the section titled More attribute access for new-style classes in the current documentation, where they recommend doing exactly this to avoid the infinite recursion.


回答 1

实际上,我相信您想改用__getattr__特殊方法。

引用Python文档:

__getattr__( self, name)

当在常规位置未找到属性时调用该属性(即,它不是实例属性,也不是在自身的类树中找到该属性)。name是属性名称。此方法应返回(计算出的)属性值或引发AttributeError异常。
请注意,如果通过常规机制找到该属性,__getattr__()则不会调用该属性。(这是__getattr__()和之间的故意不对称__setattr__()。)这样做是出于效率方面的考虑,并且因为否则__setattr__()将无法访问实例的其他属性。请注意,至少对于实例变量,您可以通过不在实例属性字典中插入任何值(而是将其插入另一个对象中)来伪造总体控制。见__getattribute__() 方法,以实际获得新样式类中的总控制权。

注:对于这项工作,该实例应该不会有一个test属性,因此行self.test=20应该被删除。

Actually, I believe you want to use the __getattr__ special method instead.

Quote from the Python docs:

__getattr__( self, name)

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.
Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __setattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control in new-style classes.

Note: for this to work, the instance should not have a test attribute, so the line self.test=20 should be removed.


回答 2

Python语言参考:

为了避免此方法的无限递归,其实现应始终调用具有相同名称的基类方法以访问其所需的任何属性,例如 object.__getattribute__(self, name)

含义:

def __getattribute__(self,name):
    ...
        return self.__dict__[name]

您正在调用名为的属性__dict__。由于它是一个属性,因此__getattribute__会在搜索__dict__中调用__getattribute__哪个调用而被调用… yada yada yada

return  object.__getattribute__(self, name)

使用基类__getattribute__有助于查找真实属性。

Python language reference:

In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).

Meaning:

def __getattribute__(self,name):
    ...
        return self.__dict__[name]

You’re calling for an attribute called __dict__. Because it’s an attribute, __getattribute__ gets called in search for __dict__ which calls __getattribute__ which calls … yada yada yada

return  object.__getattribute__(self, name)

Using the base classes __getattribute__ helps finding the real attribute.


回答 3

确定要使用__getattribute__吗?您实际上想实现什么?

最简单的方法是:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    test = 0

要么:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    @property
    def test(self):
        return 0

编辑:请注意,的实例在每种情况下D将具有不同的值test。在第一种情况下d.test为20,在第二种情况下为0。我将由您自己确定原因。

Edit2:Greg指出示例2将失败,因为该属性是只读属性,并且该__init__方法尝试将其设置为20。对此的更完整示例为:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    _test = 0

    def get_test(self):
        return self._test

    def set_test(self, value):
        self._test = value

    test = property(get_test, set_test)

显然,作为一门课,这几乎是毫无用处的,但它为您提供了继续学习的想法。

Are you sure you want to use __getattribute__? What are you actually trying to achieve?

The easiest way to do what you ask is:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    test = 0

or:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    @property
    def test(self):
        return 0

Edit: Note that an instance of D would have different values of test in each case. In the first case d.test would be 20, in the second it would be 0. I’ll leave it to you to work out why.

Edit2: Greg pointed out that example 2 will fail because the property is read only and the __init__ method tried to set it to 20. A more complete example for that would be:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    _test = 0

    def get_test(self):
        return self._test

    def set_test(self, value):
        self._test = value

    test = property(get_test, set_test)

Obviously, as a class this is almost entirely useless, but it gives you an idea to move on from.


回答 4

这是一个更可靠的版本:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21
    def __getattribute__(self, name):
        if name == 'test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

它从父类调用__ getattribute __方法,最终退回到对象。__ getattribute __方法,如果其他祖先没有覆盖它。

Here is a more reliable version:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21
    def __getattribute__(self, name):
        if name == 'test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

It calls __getattribute__ method from parent class, eventually falling back to object.__getattribute__ method if other ancestors don’t override it.


回答 5

如何__getattribute__使用该方法?

在普通的点分查找之前调用它。如果涨了AttributeError,我们打电话__getattr__

这种方法很少使用。标准库中只有两个定义:

$ grep -Erl  "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py

最佳实践

以编程方式控制对单个属性的访问的正确方法是使用property。类的D编写应如下所示(可以使用setter和Deleter来复制明显的预期行为):

class D(object):
    def __init__(self):
        self.test2=21

    @property
    def test(self):
        return 0.

    @test.setter
    def test(self, value):
        '''dummy function to avoid AttributeError on setting property'''

    @test.deleter
    def test(self):
        '''dummy function to avoid AttributeError on deleting property'''

和用法:

>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0

属性是数据描述符,因此它是常规点分查找算法中要查找的第一件事。

的选项 __getattribute__

如果您绝对需要通过来为每个属性实现查找,则有几种选择__getattribute__

  • 提高AttributeError,导致__getattr__被调用(如果已实现)
  • 从中退还东西
    • 通过super调用父类的(可能object的)执行
    • 呼唤 __getattr__
    • 以某种方式实现您自己的虚线查找算法

例如:

class NoisyAttributes(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self, name):
        print('getting: ' + name)
        try:
            return super(NoisyAttributes, self).__getattribute__(name)
        except AttributeError:
            print('oh no, AttributeError caught and reraising')
            raise
    def __getattr__(self, name):
        """Called if __getattribute__ raises AttributeError"""
        return 'close but no ' + name    


>>> n = NoisyAttributes()
>>> nfoo = n.foo
getting: foo
oh no, AttributeError caught and reraising
>>> nfoo
'close but no foo'
>>> n.test
getting: test
20

您最初想要的。

此示例说明了如何执行您最初想要的操作:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

并且会像这样:

>>> o = D()
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
>>> del o.test

Traceback (most recent call last):
  File "<pyshell#216>", line 1, in <module>
    del o.test
AttributeError: test

代码审查

您的代码带注释。您在中对自己进行了点查询__getattribute__。这就是为什么您会得到递归错误的原因。您可以检查名称是否可用,"__dict__"并使用它super来解决,但这并不覆盖__slots__。我将其留给读者练习。

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:      #   v--- Dotted lookup on self in __getattribute__
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

How is the __getattribute__ method used?

It is called before the normal dotted lookup. If it raises AttributeError, then we call __getattr__.

Use of this method is rather rare. There are only two definitions in the standard library:

$ grep -Erl  "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py

Best Practice

The proper way to programmatically control access to a single attribute is with property. Class D should be written as follows (with the setter and deleter optionally to replicate apparent intended behavior):

class D(object):
    def __init__(self):
        self.test2=21

    @property
    def test(self):
        return 0.

    @test.setter
    def test(self, value):
        '''dummy function to avoid AttributeError on setting property'''

    @test.deleter
    def test(self):
        '''dummy function to avoid AttributeError on deleting property'''

And usage:

>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0

A property is a data descriptor, thus it is the first thing looked for in the normal dotted lookup algorithm.

Options for __getattribute__

You several options if you absolutely need to implement lookup for every attribute via __getattribute__.

  • raise AttributeError, causing __getattr__ to be called (if implemented)
  • return something from it by
    • using super to call the parent (probably object‘s) implementation
    • calling __getattr__
    • implementing your own dotted lookup algorithm somehow

For example:

class NoisyAttributes(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self, name):
        print('getting: ' + name)
        try:
            return super(NoisyAttributes, self).__getattribute__(name)
        except AttributeError:
            print('oh no, AttributeError caught and reraising')
            raise
    def __getattr__(self, name):
        """Called if __getattribute__ raises AttributeError"""
        return 'close but no ' + name    


>>> n = NoisyAttributes()
>>> nfoo = n.foo
getting: foo
oh no, AttributeError caught and reraising
>>> nfoo
'close but no foo'
>>> n.test
getting: test
20

What you originally wanted.

And this example shows how you might do what you originally wanted:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

And will behave like this:

>>> o = D()
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
>>> del o.test

Traceback (most recent call last):
  File "<pyshell#216>", line 1, in <module>
    del o.test
AttributeError: test

Code review

Your code with comments. You have a dotted lookup on self in __getattribute__. This is why you get a recursion error. You could check if name is "__dict__" and use super to workaround, but that doesn’t cover __slots__. I’ll leave that as an exercise to the reader.

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:      #   v--- Dotted lookup on self in __getattribute__
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

如何在当前模块上调用setattr()?

问题:如何在当前模块上调用setattr()?

如何将第一个参数“ object”传递给函数setattr(object, name, value),以在当前模块上设置变量?

例如:

setattr(object, "SOME_CONSTANT", 42);

具有与以下相同的效果:

SOME_CONSTANT = 42

在包含这些行的模块中(带有正确的object)。

我在模块级别动态生成几个值,由于无法__getattr__在模块级别定义,所以这是我的后备。

What do I pass as the first parameter “object” to the function setattr(object, name, value), to set variables on the current module?

For example:

setattr(object, "SOME_CONSTANT", 42);

giving the same effect as:

SOME_CONSTANT = 42

within the module containing these lines (with the correct object).

I’m generate several values at the module level dynamically, and as I can’t define __getattr__ at the module level, this is my fallback.


回答 0

import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

或者,不使用setattr(打破问题的字母,但满足相同的实际目的;-):

globals()[name] = value

注意:在模块范围内,后者等效于:

vars()[name] = value

这更加简洁,但是在函数内部不起作用(vars()给出在其范围内调用的变量:在全局范围内调用时模块的变量,然后可以使用R / W,但在函数中可以使用变量在函数中调用时,然后必须将其视为R / O - Python在线文档对此特定区别可能会造成混淆。

import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

or, without using setattr (which breaks the letter of the question but satisfies the same practical purposes;-):

globals()[name] = value

Note: at module scope, the latter is equivalent to:

vars()[name] = value

which is a bit more concise, but doesn’t work from within a function (vars() gives the variables of the scope it’s called at: the module’s variables when called at global scope, and then it’s OK to use it R/W, but the function’s variables when called in a function, and then it must be treated as R/O — the Python online docs can be a bit confusing about this specific distinction).


回答 1

如果必须从模块内部设置模块范围的变量,那有什么问题global呢?

# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

从而:

>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']

If you must set module scoped variables from within the module, what’s wrong with global?

# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

thus:

>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']

回答 2

在Python 3.7中,您将可以__getattr__在模块级别使用(相关答案)。

根据PEP 562

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")

In Python 3.7, you will be able to use __getattr__ at the module level (related answer).

Per PEP 562:

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")

回答 3

  1. 你不会的 你会做globals()["SOME_CONSTANT"] = 42
  2. 你不会的 您会将动态生成的内容存储在模块以外的其他位置。
  1. You wouldn’t. You would do globals()["SOME_CONSTANT"] = 42
  2. You wouldn’t. You would store dynamically-generated content somewhere other than a module.

了解__getattr__和__getattribute__之间的区别

问题:了解__getattr__和__getattribute__之间的区别

我试图理解上的差异之间__getattr____getattribute__,但是,我在它失败。

堆栈溢出问题的答案与vs 之间的区别是__getattr____getattribute__

__getattribute__在查看对象的实际属性之前调用,因此很难正确实现。您可以非常轻松地进行无限递归。

我完全不知道那是什么意思。

然后继续说:

您几乎可以肯定想要__getattr__

为什么?

我读到,如果__getattribute__失败,__getattr__则称为。那么,为什么有两种不同的方法做同样的事情呢?如果我的代码实现了新样式类,我应该使用什么?

我正在寻找一些代码示例来清除此问题。我已尽我所能搜索Google,但是我发现的答案并未彻底讨论该问题。

如果有任何文档,我准备阅读。

I am trying to understand the difference between __getattr__ and __getattribute__, however, I am failing at it.

The answer to the Stack Overflow question Difference between __getattr__ vs __getattribute__ says:

__getattribute__ is invoked before looking at the actual attributes on the object, and so can be tricky to implement correctly. You can end up in infinite recursions very easily.

I have absolutely no idea what that means.

Then it goes on to say:

You almost certainly want __getattr__.

Why?

I read that if __getattribute__ fails, __getattr__ is called. So why are there two different methods doing the same thing? If my code implements the new style classes, what should I use?

I am looking for some code examples to clear this question. I have Googled to best of my ability, but the answers that I found don’t discuss the problem thoroughly.

If there is any documentation, I am ready to read that.


回答 0

首先了解一些基础知识。

对于对象,您需要处理其属性。通常我们会这么做instance.attribute。有时我们需要更多的控制权(当我们事先不知道属性名称时)。

例如,instance.attribute将变为getattr(instance, attribute_name)。使用此模型,我们可以通过提供attribute_name作为字符串来获取属性。

用于 __getattr__

您还可以告诉类如何处理它未显式管理的属性,并通过__getattr__方法进行操作。

每当您请求尚未定义的属性时,Python都会调用此方法,因此您可以定义该方法。

一个经典的用例:

class A(dict):
    def __getattr__(self, name):
       return self[name]
a = A()
# Now a.somekey will give a['somekey']

注意事项和使用 __getattribute__

如果您需要捕获每个属性(无论是否存在),请使用__getattribute__。不同之处在于,__getattr__仅调用实际上不存在的属性。如果您直接设置属性,则引用该属性将无需调用即可检索它__getattr__

__getattribute__ 一直被称为。

Some basics first.

With objects, you need to deal with its attributes. Ordinarily we do instance.attribute. Sometimes we need more control (when we do not know the name of the attribute in advance).

For example, instance.attribute would become getattr(instance, attribute_name). Using this model, we can get the attribute by supplying the attribute_name as a string.

Use of __getattr__

You can also tell a class how to deal with attributes which it doesn’t explicitly manage and do that via __getattr__ method.

Python will call this method whenever you request an attribute that hasn’t already been defined, so you can define what to do with it.

A classic use case:

class A(dict):
    def __getattr__(self, name):
       return self[name]
a = A()
# Now a.somekey will give a['somekey']

Caveats and use of __getattribute__

If you need to catch every attribute regardless whether it exists or not, use __getattribute__ instead. The difference is that __getattr__ only gets called for attributes that don’t actually exist. If you set an attribute directly, referencing that attribute will retrieve it without calling __getattr__.

__getattribute__ is called all the times.


回答 1

__getattribute__ 每当发生属性访问时都会调用。

class Foo(object):
    def __init__(self, a):
        self.a = 1

    def __getattribute__(self, attr):
        try:
            return self.__dict__[attr]
        except KeyError:
            return 'default'
f = Foo(1)
f.a

这将导致无限递归。罪魁祸首是排队return self.__dict__[attr]。让我们假装(这与事实很接近)所有属性都存储在self.__dict__名称中并可用。线

f.a

尝试访问的a属性f。这叫f.__getattribute__('a')__getattribute__然后尝试加载self.__dict____dict__是的属性,self == f因此python调用f.__getattribute__('__dict__')再次尝试访问属性'__dict__‘。这是无限递归。

如果__getattr__曾经使用过,那么

  1. 它永远不会运行,因为f具有a属性。
  2. 如果它已经运行((假设您要f.b)),则不会调用__dict__它,因为它已经存在,并且__getattr__仅当所有其他查找属性的方法均失败时才被调用。

编写上述类的“正确”方法__getattribute__

class Foo(object):
    # Same __init__

    def __getattribute__(self, attr):
        return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr)__getattribute__“最近”超类的方法(self通常是该类的“方法解析顺序”中的下一个类)绑定到当前对象,然后调用它并让其完成工作。

通过使用__getattr__python 可以在没有找到属性之前将其正常处理,从而避免了所有这些麻烦。到那时,Python将控制权交给您的__getattr__方法,并让它提出一些建议。

还值得注意的是,您可以使用进行无限递归__getattr__

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

我将把它留作练习。

__getattribute__ is called whenever an attribute access occurs.

class Foo(object):
    def __init__(self, a):
        self.a = 1

    def __getattribute__(self, attr):
        try:
            return self.__dict__[attr]
        except KeyError:
            return 'default'
f = Foo(1)
f.a

This will cause infinite recursion. The culprit here is the line return self.__dict__[attr]. Let’s pretend (It’s close enough to the truth) that all attributes are stored in self.__dict__ and available by their name. The line

f.a

attempts to access the a attribute of f. This calls f.__getattribute__('a'). __getattribute__ then tries to load self.__dict__. __dict__ is an attribute of self == f and so python calls f.__getattribute__('__dict__') which again tries to access the attribute '__dict__‘. This is infinite recursion.

If __getattr__ had been used instead then

  1. It never would have run because f has an a attribute.
  2. If it had run, (let’s say that you asked for f.b) then it would not have been called to find __dict__ because it’s already there and __getattr__ is invoked only if all other methods of finding the attribute have failed.

The ‘correct’ way to write the above class using __getattribute__ is

class Foo(object):
    # Same __init__

    def __getattribute__(self, attr):
        return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr) binds the __getattribute__ method of the ‘nearest’ superclass (formally, the next class in the class’s Method Resolution Order, or MRO) to the current object self and then calls it and lets that do the work.

All of this trouble is avoided by using __getattr__ which lets Python do it’s normal thing until an attribute isn’t found. At that point, Python hands control over to your __getattr__ method and lets it come up with something.

It’s also worth noting that you can run into infinite recursion with __getattr__.

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

I’ll leave that one as an exercise.


回答 2

我认为其他的答案做了解释之间的差异的一个伟大的工作__getattr____getattribute__,但有一点可能没有明确的是,为什么你会想使用__getattribute__。有趣的__getattribute__是,它本质上允许您在访问类时重载点。这使您可以自定义如何在较低级别访问属性。例如,假设我要定义一个类,其中所有仅带有自变量的方法都被视为属性:

# prop.py
import inspect

class PropClass(object):
    def __getattribute__(self, attr):
        val = super(PropClass, self).__getattribute__(attr)
        if callable(val):
            argcount = len(inspect.getargspec(val).args)
            # Account for self
            if argcount == 1:
                return val()
            else:
                return val
        else:
            return val

从交互式解释器中:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1

当然,这是一个愚蠢的示例,您可能永远也不想这样做,但是它向您展示了从覆盖获得的强大功能__getattribute__

I think the other answers have done a great job of explaining the difference between __getattr__ and __getattribute__, but one thing that might not be clear is why you would want to use __getattribute__. The cool thing about __getattribute__ is that it essentially allows you to overload the dot when accessing a class. This allows you to customize how attributes are accessed at a low level. For instance, suppose I want to define a class where all methods that only take a self argument are treated as properties:

# prop.py
import inspect

class PropClass(object):
    def __getattribute__(self, attr):
        val = super(PropClass, self).__getattribute__(attr)
        if callable(val):
            argcount = len(inspect.getargspec(val).args)
            # Account for self
            if argcount == 1:
                return val()
            else:
                return val
        else:
            return val

And from the interactive interpreter:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1

Of course this is a silly example and you probably wouldn’t ever want to do this, but it shows you the power you can get from overriding __getattribute__.


回答 3

我经历了别人的出色解释。但是,我从此博客Python Magic Methods和__getattr__找到了一个简单的答案。以下所有都是从那里开始的。

使用__getattr__magic方法,我们可以拦截不存在的属性查找并做一些事情,以确保它不会失败:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

但是,如果该属性确实存在,__getattr__将不会被调用:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__与相似__getattr__,但重要的区别是__getattribute__将拦截每个属性查找,而属性是否存在无关紧要。

class Dummy(object):

    def __getattribute__(self, attr):
        return 'YOU SEE ME?'

d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

在该示例中,d对象已经具有属性值。但是,当我们尝试访问它时,没有得到原始的期望值(“ Python”);我们只是得到任何__getattribute__回报。这意味着我们实际上失去了value属性;它已经变得“无法到达”。

I have gone through other’s excellent explanation. However, I found a simple answer from this blog Python Magic Methods and __getattr__. All the following are from there.

Using the __getattr__ magic method, we can intercept that inexistent attribute lookup and do something so it doesn’t fail:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

But if the attribute does exist, __getattr__ won’t be invoked:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__ is similar to __getattr__, with the important difference that __getattribute__ will intercept EVERY attribute lookup, doesn’t matter if the attribute exists or not.

class Dummy(object):

    def __getattribute__(self, attr):
        return 'YOU SEE ME?'

d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

In that example, the d object already has an attribute value. But when we try to access it, we don’t get the original expected value (“Python”); we’re just getting whatever __getattribute__ returned. It means that we’ve virtually lost the value attribute; it has become “unreachable”.


如何在不破坏默认行为的情况下在Python中重写__getattr__?

问题:如何在不破坏默认行为的情况下在Python中重写__getattr__?

我想重写__getattr__类上的方法以执行某些操作,但我不想破坏默认行为。

正确的方法是什么?

I want to override the __getattr__ method on a class to do something fancy but I don’t want to break the default behavior.

What’s the correct way to do this?


回答 0

覆盖__getattr__应该没问题- __getattr__仅可作为最后的选择,即,如果实例中没有与名称匹配的属性。例如,如果您访问foo.bar,则__getattr__仅当foo没有调用属性时才会被调用bar。如果该属性是您不想处理的属性,请引发AttributeError

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

但是,与不同的是__getattr____getattribute__将首先调用(仅适用于新样式类,即从对象继承的类)。在这种情况下,您可以保留默认行为,如下所示:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

有关更多信息,请参见Python文档

Overriding __getattr__ should be fine — __getattr__ is only called as a last resort i.e. if there are no attributes in the instance that match the name. For instance, if you access foo.bar, then __getattr__ will only be called if foo has no attribute called bar. If the attribute is one you don’t want to handle, raise AttributeError:

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

However, unlike __getattr__, __getattribute__ will be called first (only works for new style classes i.e. those that inherit from object). In this case, you can preserve default behaviour like so:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

See the Python docs for more.


回答 1

class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False

回答 2

为了扩展Michael的答案,如果您想使用来维持默认行为__getattr__,可以这样做:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

现在,异常消息更具描述性:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

To extend Michael answer, if you want to maintain the default behavior using __getattr__, you can do it like so:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

Now the exception message is more descriptive:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

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

问题:到底什么是getattr(),我该如何使用它?

我最近阅读了有关该getattr()功能的信息。问题是我仍然无法理解其用法。据我所知的唯一的事情getattr()getattr(li, "pop")相同调用li.pop

当书中提到您是如何使用它来获得对函数的引用时,直到运行时才知道它的名称,我不明白。总的来说,也许这是我在编程方面的菜鸟。有人可以阐明这个话题吗?我什么时候以及如何使用它?

I’ve recently read about the getattr() function. The problem is that I still can’t grasp the idea of its usage. The only thing I understand about getattr() is that getattr(li, "pop") is the same as calling li.pop.

I didn’t understand when the book mentioned how you use it to get a reference to a function without knowing its name until run-time. Maybe this is me being a noob in programming, in general. Could anyone shed some light on the subject? When and how do I use this exactly?


回答 0

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

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

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

getattr(object, 'x') is completely equivalent to object.x.

There are only two cases where getattr can be useful.

  • you can’t write object.x, because you don’t know in advance which attribute you want (it comes from a string). Very useful for meta-programming.
  • you want to provide a default value. object.y will raise an AttributeError if there’s no y. But getattr(object, 'y', 5) will return 5.

回答 1

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

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

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

Objects in Python can have attributes — data attributes and functions to work with those (methods). Actually, every object has built-in attributes.

For example you have an object person, that has several attributes: name, gender, etc.

You access these attributes (be it methods or data objects) usually writing: person.name, person.gender, person.the_method(), etc.

But what if you don’t know the attribute’s name at the time you write the program? For example you have attribute’s name stored in a variable called attr_name.

if

attr_name = 'gender'

then, instead of writing

gender = person.gender

you can write

gender = getattr(person, attr_name)

Some practice:

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 will raise AttributeError if attribute with the given name does not exist in the object:

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

But you can pass a default value as the third argument, which will be returned if such attribute does not exist:

>>> getattr(person, 'age', 0)
0

You can use getattr along with dir to iterate over all attribute names and get their values:

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

A practical use for this would be to find all methods whose names start with test and call them.

Similar to getattr there is setattr which allows you to set an attribute of an object having its name:

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

回答 2

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

它使您可以基于字符串的内容来调用方法,而不用键入方法名称。

例如,您不能执行以下操作:

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

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

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

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

For me, getattr is easiest to explain this way:

It allows you to call methods based on the contents of a string instead of typing the method name.

For example, you cannot do this:

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

because x is not of the type builtin, but str. However, you CAN do this:

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

It allows you to dynamically connect with objects based on your input. I’ve found it useful when dealing with custom objects and modules.


回答 3

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

例如,在Django或Pylons之类的Web框架中,getattr可以很容易地将Web请求的URL映射到将要处理它的函数。例如,如果您看一下Pylons路由的内幕,您会发现(至少默认情况下)它会截取请求的URL,例如:

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

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

一旦掌握了这个想法,就可以轻松扩展Web应用程序的功能:只需将新方法添加到控制器类中,然后在页面中创建链接,并为这些方法使用适当的URL。所有这些都可以通过来实现getattr

A pretty common use case for getattr is mapping data to functions.

For instance, in a web framework like Django or Pylons, getattr makes it straightforward to map a web request’s URL to the function that’s going to handle it. If you look under the hood of Pylons’s routing, for instance, you’ll see that (by default, at least) it chops up a request’s URL, like:

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

into “customers” and “list”. Then it searches for a controller class named CustomerController. Assuming it finds the class, it creates an instance of the class and then uses getattr to get its list method. It then calls that method, passing it the request as an argument.

Once you grasp this idea, it becomes really easy to extend the functionality of a web application: just add new methods to the controller classes, and then create links in your pages that use the appropriate URLs for those methods. All of this is made possible by getattr.


回答 4

这是一个简单而又肮脏的示例,它说明了一个类如何根据使用的操作系统来触发不同版本的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

现在,在示例中使用此类:

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

Here’s a quick and dirty example of how a class could fire different versions of a save method depending on which operating system it’s being executed on using 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

Now let’s use this class in an example:

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

回答 5

除了这里所有令人惊奇的答案之外,还有一种方法可以getattr用来保存大量的代码行并保持其紧密。这种想法是在有时可能需要用可怕的代码表示法之后提出的。

情境

假设您的目录结构如下:

- superheroes.py
- properties.py

而且,你有功能,让有关的信息ThorIron 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在正确的位置使用代码编写更清晰代码的见解。

Other than all the amazing answers here, there is a way to use getattr to save copious lines of code and keeping it snug. This thought came following the dreadful representation of code that sometimes might be a necessity.

Scenario

Suppose your directory structure is as follows:

- superheroes.py
- properties.py

And, you have functions for getting information about Thor, Iron Man, Doctor Strange in superheroes.py. You very smartly write down the properties of all of them in properties.py in a compact dict and then access them.

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'],
}

Now, let’s say you want to return capabilities of each of them on demand in superheroes.py. So, there are functions like

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

…and more functions returning different values based on the keys and superhero.

With the help of getattr, you could do something like:

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

You considerably reduced the number of lines of code, functions and repetition!

Oh and of course, if you have bad names like properties_of_thor for variables , they can be made and accessed by simply doing

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

NOTE: For this particular problem, there can be smarter ways to deal with the situation, but the idea is to give an insight about using getattr in right places to write cleaner code.


回答 6

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

回答 7

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

I sometimes use getattr(..) to lazily initialise attributes of secondary importance just before they are used in the code.

Compare the following:

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

To this:

class Graph(object):
    def plot(self):
        self.n_calls_to_plot = 1 + getattr(self, "n_calls_to_plot", 0)

The advantage of the second way is that n_calls_to_plot only appears around the place in the code where it is used. This is good for readability, because (1) you can immediately see what value it starts with when reading how it’s used, (2) it doesn’t introduce a distraction into the __init__(..) method, which ideally should be about the conceptual state of the class, rather than some utility counter that is only used by one of the function’s methods for technical reasons, such as optimisation, and has nothing to do with the meaning of the object.


回答 8

当我从存储在类中的数据创建XML文件时,如果属性不存在或类型是经常会收到错误None。在这种情况下,我的问题不是不知道您的问题中所说的属性名称是什么,而是数据曾经存储在该属性中。

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

如果我曾经hasattr这样做,True即使属性值是类型,它也会返回None,这将导致ElementTree set命令失败。

hasattr(temp, 'hair')
>>True

如果属性值为type 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

Quite frequently when I am creating an XML file from data stored in a class I would frequently receive errors if the attribute didn’t exist or was of type None. In this case, my issue wasn’t not knowing what the attribute name was, as stated in your question, but rather was data ever stored in that attribute.

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

If I used hasattr to do this, it would return True even if the attribute value was of type None and this would cause my ElementTree set command to fail.

hasattr(temp, 'hair')
>>True

If the attribute value was of type None, getattr would also return it which would cause my ElementTree set command to fail.

c = getattr(temp, 'hair')
type(c)
>> NoneType

I use the following method to take care of these cases now:

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

This is when and how I use getattr.


回答 9

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

Another use of getattr() in implementing a switch statement in Python. It uses both reflection to get the case type.

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

回答 10

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)

setattr()

We use setattr to add an attribute to our class instance. We pass the class instance, the attribute name, and the value.

getattr()

With getattr we retrive these values

For example

Employee = type("Employee", (object,), dict())

employee = Employee()

# Set salary to 1000
setattr(employee,"salary", 1000 )

# Get the Salary
value = getattr(employee, "salary")

print(value)

回答 11

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

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

I think this example is self explanatory. It runs the method of first parameter, whose name is given in the second parameter.

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

回答 12

它也在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:“人员”对象没有属性“性别”

It is also clarifying from 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)

The age is: 23

The age is: 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'))

The sex is: Male

AttributeError: ‘Person’ object has no attribute ‘sex’


回答 13

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

I have tried in Python2.7.17

Some of the fellow folks already answered. However I have tried to call getattr(obj, ‘set_value’) and this didn’t execute the set_value method, So i changed to getattr(obj, ‘set_value’)() –> This helps to invoke the same.

Example Code:

Example 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

__getattr__和__getattribute__之间的区别

问题:__getattr__和__getattribute__之间的区别

我想了解何时使用__getattr____getattribute__。该文件提到了__getattribute__适用于新样式类。什么是新型类?

I am trying to understand when to use __getattr__ or __getattribute__. The documentation mentions __getattribute__ applies to new-style classes. What are new-style classes?


回答 0

之间的主要差异__getattr__,并__getattribute__是,__getattr__如果属性没有被发现通常的途径,只调用。这对于实现缺少属性的后备方法很有用,并且可能是您想要的两个之一。

__getattribute__在查看对象的实际属性之前调用,因此正确实现可能会比较棘手。您可以非常轻松地进行无限递归。

新样式类是从派生而来的object,旧样式类是Python 2.x中没有显式基类的类。但旧式和新式的类之间的区别并不之间进行选择时的重要的__getattr____getattribute__

您几乎可以肯定想要__getattr__

A key difference between __getattr__ and __getattribute__ is that __getattr__ is only invoked if the attribute wasn’t found the usual ways. It’s good for implementing a fallback for missing attributes, and is probably the one of two you want.

__getattribute__ is invoked before looking at the actual attributes on the object, and so can be tricky to implement correctly. You can end up in infinite recursions very easily.

New-style classes derive from object, old-style classes are those in Python 2.x with no explicit base class. But the distinction between old-style and new-style classes is not the important one when choosing between __getattr__ and __getattribute__.

You almost certainly want __getattr__.


回答 1

让我们来看看两者的一些简单的例子__getattr____getattribute__魔术方法。

__getattr__

__getattr__每当您请求尚未定义的属性时,Python都会调用 method。在下面的示例中,我的Count类没有__getattr__方法。现在,当我尝试同时访问obj1.mymin和访问obj1.mymax属性时,一切正常。但是当我尝试访问obj1.mycurrent属性时-Python给了我AttributeError: 'Count' object has no attribute 'mycurrent'

class Count():
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent)  --> AttributeError: 'Count' object has no attribute 'mycurrent'

现在,我的ClassCount具有__getattr__方法。现在,当我尝试访问 obj1.mycurrent属性时-python返回我在__getattr__方法中实现的内容。在我的示例中,每当我尝试调用不存在的属性时,python都会创建该属性并将其设置为整数值0。

class Count:
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax    

    def __getattr__(self, item):
        self.__dict__[item]=0
        return 0

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)

__getattribute__

现在让我们看一下__getattribute__方法。如果您__getattribute__的类中有 方法,则python会为每个属性调用此方法,无论该属性是否存在。那么为什么我们需要__getattribute__方法呢?一个很好的理由是,您可以阻止访问属性并使它们更安全,如以下示例所示。

每当有人尝试访问以子字符串“ cur”开头的我的属性时,python都会引发AttributeError异常。否则,它将返回该属性。

class Count:

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item) 
        # or you can use ---return super().__getattribute__(item)

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

重要说明:为了避免__getattribute__方法中的无限递归,其实现应始终调用具有相同名称的基类方法以访问其所需的任何属性。例如:object.__getattribute__(self, name)super().__getattribute__(item)self.__dict__[item]

重要

如果您的类同时包含getattrgetattribute魔术方法,则将__getattribute__首先调用该方法 。但是,如果 __getattribute__引发 AttributeError异常,则该异常将被忽略,__getattr__方法将被调用。请参见以下示例:

class Count(object):

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattr__(self, item):
            self.__dict__[item]=0
            return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item)
        # or you can use ---return super().__getattribute__(item)
        # note this class subclass object

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

Lets see some simple examples of both __getattr__ and __getattribute__ magic methods.

__getattr__

Python will call __getattr__ method whenever you request an attribute that hasn’t already been defined. In the following example my class Count has no __getattr__ method. Now in main when I try to access both obj1.mymin and obj1.mymax attributes everything works fine. But when I try to access obj1.mycurrent attribute — Python gives me AttributeError: 'Count' object has no attribute 'mycurrent'

class Count():
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent)  --> AttributeError: 'Count' object has no attribute 'mycurrent'

Now my class Count has __getattr__ method. Now when I try to access obj1.mycurrent attribute — python returns me whatever I have implemented in my __getattr__ method. In my example whenever I try to call an attribute which doesn’t exist, python creates that attribute and set it to integer value 0.

class Count:
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax    

    def __getattr__(self, item):
        self.__dict__[item]=0
        return 0

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)

__getattribute__

Now lets see the __getattribute__ method. If you have __getattribute__ method in your class, python invokes this method for every attribute regardless whether it exists or not. So why we need __getattribute__ method? One good reason is that you can prevent access to attributes and make them more secure as shown in the following example.

Whenever someone try to access my attributes that starts with substring ‘cur’ python raises AttributeError exception. Otherwise it returns that attribute.

class Count:

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item) 
        # or you can use ---return super().__getattribute__(item)

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

Important: In order to avoid infinite recursion in __getattribute__ method, its implementation should always call the base class method with the same name to access any attributes it needs. For example: object.__getattribute__(self, name) or super().__getattribute__(item) and not self.__dict__[item]

IMPORTANT

If your class contain both getattr and getattribute magic methods then __getattribute__ is called first. But if __getattribute__ raises AttributeError exception then the exception will be ignored and __getattr__ method will be invoked. See the following example:

class Count(object):

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattr__(self, item):
            self.__dict__[item]=0
            return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item)
        # or you can use ---return super().__getattribute__(item)
        # note this class subclass object

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

回答 2

这只是基于Ned Batchelder的解释的示例

__getattr__ 例:

class Foo(object):
    def __getattr__(self, attr):
        print "looking up", attr
        value = 42
        self.__dict__[attr] = value
        return value

f = Foo()
print f.x 
#output >>> looking up x 42

f.x = 3
print f.x 
#output >>> 3

print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')

如果使用相同的示例,__getattribute__您将得到>>>RuntimeError: maximum recursion depth exceeded while calling a Python object

This is just an example based on Ned Batchelder’s explanation.

__getattr__ example:

class Foo(object):
    def __getattr__(self, attr):
        print "looking up", attr
        value = 42
        self.__dict__[attr] = value
        return value

f = Foo()
print f.x 
#output >>> looking up x 42

f.x = 3
print f.x 
#output >>> 3

print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')

And if same example is used with __getattribute__ You would get >>> RuntimeError: maximum recursion depth exceeded while calling a Python object


回答 3

新样式类继承自object,或从另一个新样式类继承:

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

旧式类不能:

class SomeObject:
    pass

这仅适用于Python 2-在Python 3中,以上所有内容都会创建新样式的类。

请参见9.类(Python教程),NewClassVsClassicClass和Python中旧样式类和新样式类之间的区别是什么?有关详细信息。

New-style classes inherit from object, or from another new style class:

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

Old-style classes don’t:

class SomeObject:
    pass

This only applies to Python 2 – in Python 3 all the above will create new-style classes.

See 9. Classes (Python tutorial), NewClassVsClassicClass and What is the difference between old style and new style classes in Python? for details.


回答 4

新型类是子类“对象”的子类(直接或间接)。他们除了具有__new__类方法外__init__,还具有更合理的低级行为。

通常,您将需要覆盖__getattr__(如果要覆盖两者之一),否则您将很难在方法中支持“ self.foo”语法。

额外信息:http : //www.devx.com/opensource/Article/31482/0/page/4

New-style classes are ones that subclass “object” (directly or indirectly). They have a __new__ class method in addition to __init__ and have somewhat more rational low-level behavior.

Usually, you’ll want to override __getattr__ (if you’re overriding either), otherwise you’ll have a hard time supporting “self.foo” syntax within your methods.

Extra info: http://www.devx.com/opensource/Article/31482/0/page/4


回答 5

在阅读Beazley&Jones PCB时,我偶然发现了一个明确而实际的用例,__getattr__该用例有助于回答OP的“何时”部分。从书中:

“该__getattr__()方法有点像属性查找的全部。如果代码尝试访问不存在的属性,则该方法会被调用。” 我们从以上答案中知道了这一点,但是在PCB配方8.15中,此功能用于实现委托设计模式。如果对象A具有对象B的属性,该属性实现了对象A要委派的许多方法,而不是仅在调用对象B的方法时重新定义对象A中的对象B的所有方法,请定义一个__getattr__()方法,如下所示:

def __getattr__(self, name):
    return getattr(self._b, name)

其中_b是对象A的属性名称,即对象B。在对象A上调用在对象B上定义的__getattr__方法时,该方法将在查找链的末尾被调用。这也将使代码更简洁,因为您没有定义仅用于委派给另一个对象的方法列表。

In reading through Beazley & Jones PCB, I have stumbled on an explicit and practical use-case for __getattr__ that helps answer the “when” part of the OP’s question. From the book:

“The __getattr__() method is kind of like a catch-all for attribute lookup. It’s a method that gets called if code tries to access an attribute that doesn’t exist.” We know this from the above answers, but in PCB recipe 8.15, this functionality is used to implement the delegation design pattern. If Object A has an attribute Object B that implements many methods that Object A wants to delegate to, rather than redefining all of Object B’s methods in Object A just to call Object B’s methods, define a __getattr__() method as follows:

def __getattr__(self, name):
    return getattr(self._b, name)

where _b is the name of Object A’s attribute that is an Object B. When a method defined on Object B is called on Object A, the __getattr__ method will be invoked at the end of the lookup chain. This would make code cleaner as well, since you do not have a list of methods defined just for delegating to another object.