繁体   English   中英

Python 中的方法到底是什么?

[英]What exactly is a method in Python?

在下面的代码中,object a有两个函数作为属性:一个是 class 属性,一个是它自己的实例属性。

class A:
    def foo(*args):
        pass

a = A()

def bar(*args):
    pass

a.bar = bar

print(a.foo)
print(a.bar)

我所期望的是bar()foo()都将是 object a方法,但是,从这段代码的 output 来看,事实并非如此——只有fooa的方法。

<bound method A.foo of <__main__.A object at 0x0000016F5F579AF0>>
<function bar at 0x0000016F5F5845E0>

那么Python中的方法到底是什么? 是不是一个包含 function 定义的 class 属性,似乎是这样。

为什么 Python 不将属性bar视为一种方法? Python 这种行为背后的想法究竟是什么?

方法是 class 方法的实例,该方法由function值的class属性的__get__ method返回。

a.bar实例属性,而不是 class 属性。

在寻找a.foo时,Python 首先寻找A.foo 找到它后,它接下来检查它的值是否有__get__方法(作为function值,它确实有。)因为A.foo是一个描述符(即有一个__get__方法),它的__get__方法被调用a.fooA.foo.__get__(a, A)相同。 返回值是一个method object,其__call__方法调用底层function,object和它自己的ZDBC16FB4CAA5BDA8ED7BDA77。 那是,

a.foo(x) == A.foo.__get__(a, A)(x)
         == A.foo(a, x)

因为a.bar是一个实例属性,所以调用描述符协议,所以a.barbar完全相同 object 。

(这是Descriptor HowTo Guide内容的一个极其精简的版本,尤其是关于方法的部分。我强烈推荐阅读它。)


a.bar的查找首先在A.__dict__中查找bar 如果找不到, Python 会查找a.__dict__ ,并找到function来调用:故事结束。

这是因为您将属性设置为 class 的实例,而不是其本身。

按照您的示例,让我们设置:

A.bar = bar

您会看到 output 是:

print(A.bar)
# <function bar at 0x0000021821D57280>

c = A()
print(c.bar)
# <bound method bar of <__main__.A object at 0x0000021821EEA400>>

绑定方法是需要 class 本身的实际实例的方法。 这就是为什么我们通常将self作为第一个参数:实例知道在调用时将自己传递给 function。 通过将function设置为实例的属性,方法不绑定,第一个参数不会是自己。

如果我们将 function bar设置为如下所示,我们可以看到这种行为:

def bar(*args):
    print("Argument types:", *map(type, args))

a.bar = bar
a.bar()
# Argument types:

A.bar = bar
c = A()
c.bar()
# Argument types: <class '__main__.A'>

我们看到,使用绑定方法,它会将自身传递给 function。 这是将方法设置为属性与实际将其设置为 class 本身的区别。

a.bar不是 BOUND 方法。 它是一个已设置为属性的可调用对象。 这意味着,除其他外,它不会自动获取self

class A:
    def __init__(self):
        self.baz = 'baz'
        
    def foo(self):
        print(self.baz)
    
def bar(obj):
    print(obj.baz)

a = A()
a.bar = bar

a.foo()
# prints baz

a.bar()
# raises TypeError: bar() missing 1 required positional argument: 'obj'

a.bar(a)
# prints baz

您可以使用types.MethodType将方法变成绑定方法:

a.bar = types.MethodType(bar, a)
a.bar()
# prints baz

这只会将它绑定到实例; 其他实例将没有此属性

a2 = A()
a2.bar()
# AttributeError: 'A' object has no attribute 'bar'

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM