是的,方法只是包含适当形式的函数的属性(它们必须至少接受一个参数,即接收者,通常称为self
).
这是一个解释引用段落的示例:
class Foo():
def a(self):
print("FOO")
foo = Foo()
foo.a()
# => FOO
So, def
实际上附加了一个属性a
to Foo
作为 Python 2 中的“未绑定方法”(正如您在检查它时看到的那样 - 意思是,它还不知道谁正在接收它),或者只是一个普通的函数值(在 Python 3 中):
Foo.a
# => <unbound method Foo.a> (Python 2)
# => <function Foo.a at 0x10919fea0> (Python 3)
您可以像调用任何函数一样调用它(...有一个例外,在 Python 2 中:第一个参数must be类型的Foo
):
Foo.a(foo)
# => FOO
Foo.a(42)
# => TypeError: unbound method a() must be called with Foo instance as first argument (got int instance instead) (Python 2)
# => 42 (Python 3)
但是,如果您尝试在实例上找到它(“实例属性引用”),它现在报告为“绑定方法”:
foo.a
# => <bound method Foo.a of <__main__.Foo instance at 0x10ba11320>>
这可以说是“将实例对象和函数对象打包(指向)在一起”:有实例对象的引用,<__main__.Foo instance at 0x10ba11320>
(又名foo
),以及对函数对象的引用,Foo.a
,在一个包中我们称之为“绑定方法”。
与 JavaScript 不同,它不纯粹是语法问题。在 JavaScript 中,方法调用和函数调用之间的区别在于调用本身:如果它有一个点,则它是方法调用:
// JavaScript
let foo = new Foo()
foo.a(); // method invocation: `a` is told that the receiver is `foo`
let z = foo.a; z() // function invocation, `a` doesn't know about `foo`
在 Python 中,绑定函数在其内部携带其接收器:
// Back to Python
foo.a() // method invocation: `a` is told that the receiver is `foo`
z = foo.a; z() // STILL method invocation; `z` knows both `foo` and `Foo.a`
这个电话是如何运作的呢?该段落的其余部分解释:
当使用参数列表调用方法对象时,会根据实例对象和参数列表构造一个新的参数列表,并使用该新参数列表调用函数对象。
所以,当我们说
foo.a()
它将解压foo.a
into Foo.a
and foo
;前置foo
,接收者,到参数列表(我这里没有,所以参数列表是[foo] + []
最终参数列表[foo]
),最终被称为Foo.a(foo)
。顺便说一句,这正是您可以手动执行的操作:
Foo.a(foo)
# => FOO
即使使用内置对象:
"-".join(["foo", "bar", "baz"])
# => 'foo-bar-baz'
str.join("-", ["foo", "bar", "baz"])
# => 'foo-bar-baz'
Here, "-".join
是一个将接收者打包在一起的绑定方法"-"
和函数str.join
;当我们调用第一行时,接收者"-"
附加在其余参数之前[["foo", "bar", "baz"]]
最终参数列表["-", ["foo", "bar", "baz"]]
,并且它被发送到位于join
的属性str
(i.e. str.join
)。这为我们提供了第一行和第二行之间的清晰翻译。