判断__getattr__是方法还是属性调用

问题描述 投票:0回答:4

有没有办法使用 __getattr__ 来确定方法和属性调用之间的区别?

即在:

class Bar(object):
    def __getattr__(self, name):
        if THIS_IS_A_METHOD_CALL:
            # Handle method call
            def method(**kwargs):
                return 'foo'
            return method
        else:
            # Handle attribute call
            return 'bar'

foo=Bar()
print(foo.test_method()) # foo
print(foo.test_attribute) # bar

这些方法不是本地的,因此无法使用 getattr/callable 来确定它。我也明白方法是属性,并且可能没有解决方案。只希望有一个。

python getattr
4个回答
14
投票

你根本无法知道一个对象将如何在

__getattr__
钩子中使用。例如,您可以访问方法而不调用它们,将它们存储在变量中,然后稍后调用它们。

返回一个带有

__call__
方法的对象,调用时会调用:

class CallableValue(object):
    def __init__(self, name):
        self.name = name
    def __call__(self, *args, **kwargs):
        print "Lo, {} was called!".format(self.name)

class Bar(object):
    def __getattr__(self, name):
        return CallableValue(name)

但是它的实例不会同时与字符串或列表相同。

演示:

>>> class CallableValue(object):
...     def __init__(self, name):
...         self.name = name
...     def __call__(self, *args, **kwargs):
...         print "Lo, {} was called!".format(self.name)
... 
>>> class Bar(object):
...     def __getattr__(self, name):
...         return CallableValue(name)
... 
>>> b = Bar()
>>> something = b.test_method
>>> something
<__main__.CallableValue object at 0x10ac3c290>
>>> something()
Lo, test_method was called!

2
投票

不可能知道随后是否从

__getattr__
调用某个属性,Martijn Pieters 对此进行了解释。

虽然不能判断某个属性是否被调用,但是通过

callable
可以知道某个属性是否可以被调用。另一种方法是使用
type
来跟踪各种对象,或制作属性名称列表。

class Foo(object):
    bar_attribute = 'callable'

    def __getattr__(self, name):
        instanceOrValue = getattr(self, "bar_%s" %name)

        if callable(instanceOrValue):
            # Handle object that can be called
            def wrap(**kwargs):
                return "is %s" %instanceOrValue(**kwargs)
            return wrap

        # Handle object that can not be called
        return 'not %s' %instanceOrValue

    def bar_method(self, **kwargs):
        return 'callable';
    

foo=Foo()
print(foo.method()) # is callable
print(foo.attribute) # not callable

__getattr__
只能跟踪某些事情,但在许多情况下它可能是正确的解决方案,因为更改调用(
__call__
)对所有情况下的调用都有影响,而不仅仅是当 Foo 类是使用过。

调用方法和访问属性之间的区别

什么是“可调用”?

Python __call__ 特殊方法实例


0
投票

简而言之,不,没有可靠的方法 - 问题是方法 Python 中的属性 - 没有区别。它恰好是一个作为绑定方法的属性。

您可以检查该属性是否是一个方法,但不能保证这意味着它会被调用,例如:

class Test:
    def test(self):
        ...

Test().test  # This accesses the method, but doesn't call it!

访问函数的调用无法知道函数返回时是否会被调用 - 这是一个尚未处理的未来事件。

如果您愿意假设正在访问的方法是正在调用的方法,则可以通过如下检查确定它是正在访问的方法:

hasattr(value, "__self__") and value.__self__ is self

其中

value
是您要检查它是否是方法或其他属性的属性,
self
是您要查看它是否是其方法的实例。

如果你需要在调用它时发生一些事情,你可以利用这个时刻来装饰该函数。

可以在here找到这方面的可靠代码示例。


0
投票

稍微扩展一下Martijn的答案

这并不适用于所有情况,但您可以返回其自身的另一个实例,以便对之后发生的情况有更多的控制。

这也允许链接属性和调用。

并且您可以实现

__str__
更多的方法,以提供不同的表示。

class CallableValue():
    name = None
    called = False
    args = []
    kwargs = {}

    def __init__(self, name = None, called=False, args=[], kwargs={}):
        self.name = name or "default"
        self.called = called
        self.args = args
        self.kwargs = kwargs

    def __call__(self, *args, **kwargs):
        return CallableValue(self.name, True, args, kwargs)
      
    def __getattr__(self, name):
        return CallableValue(name)

    def __str__(self):
        return self.name if not self.called else f"called {self.name} with {self.args} and {self.kwargs}"

t = CallableValue()
print(t)
print(t.attr)
print(t.something)
print(t.something1.something2.something3)
print(t.method())
print(t.other_method("value", key="value"))
print(t.something.method2("value"))
print(t.method1().method2().method3(key="value"))

结果:

default
attr
something
something3
called method with () and {}
called other_method with ('value',) and {'key': 'value'}
called method2 with ('value',) and {}
called method3 with () and {'key': 'value'}
© www.soinside.com 2019 - 2024. All rights reserved.