方法对象 vs 函数对象,Python 类实例 vs 类

23

我在尝试验证Python教程发布2.7.3(日期为2012年11月01日),第9章“类”,第66页最后一行所述的实例属性和类属性之间的差异 (来源):

实例对象的有效方法名称取决于它的类。根据定义,类的所有函数对象属性都定义其实例的相应方法。因此,在我们的示例中,x.f是一个有效的方法引用,因为MyClass.f是一个函数,但x.i不是,因为MyClass.i不是。 但是x.f并不等同于MyClass.f - 它是一个方法对象,而不是函数对象。

我有这个:

class MyClass:    
   """A simple example class"""    
   i = 12345   
   def f():    
      return 'hello world'

然后我这样做:

>>> x = MyClass()
>>> x.f
<bound method MyClass.f of <__main__.MyClass instance at 0x02BB8968>>
>>> MyClass.f
<unbound method MyClass.f>
>>> type(MyClass.f)
<type 'instancemethod'>
>>> type(x.f)
<type 'instancemethod'>
请注意,x.fMyClass.f的类型都是instancemethod。虽然教程中说有所不同,但实际上它们的类型并没有区别。有人能否请澄清一下?
2个回答

29

bound方法 vs unbound方法 - 一个解释。

...或者为什么Python有你指出的行为。

首先,注意在3.x中这是不同的。在3.x中,您将获得 MyClass.f 作为函数, x.f 作为方法-如预期一样。这种行为本质上是一个糟糕的设计决策,后来已经被更改了。

之所以会这样,是因为Python有一个与大多数语言不同的方法概念,它本质上是一个带有第一个参数预填为实例(self)的函数。预填充使其成为一个绑定方法

>>> x.foo
<bound method MyClass.foo of <__main__.MyClass instance at 0x1004989e0>>

在Python 2.x及之前的版本中,人们认为未附加到实例的方法将是一个未绑定方法,这是一个带有限制的函数,要求第一个参数(self)必须是对象的实例。然后可以将其绑定到实例并变成一个绑定方法

>>> MyClass.foo
<unbound method MyClass.foo>
随着时间的推移,越来越清晰地认识到未绑定方法实际上只是一个带有这种奇怪限制(即self必须是'correct'类型)的函数,因此它们从语言中移除了(在3.x中)。这本质上是使用鸭子类型来替代self,这非常适合这门语言。
Python 3.3.0 (default, Dec  4 2012, 00:30:24) 
>>> x.foo
<bound method MyClass.foo of <__main__.MyClass object at 0x100858ed0>>
>>> MyClass.foo
<function MyClass.foo at 0x10084f9e0>

进一步阅读。

这是Python创始人Guido van Rossum在他的"Python历史"系列中口述的解释(经过我从记忆中梳理而成)。


14

这个教程确实是错误的; class.functionnameinstance.functionname都会返回一个方法对象。

实际上,函数是一个描述器,它们会调用__get__方法来返回一个方法。方法具有指向原始函数的__func__属性:

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo.bar
<unbound method Foo.bar>
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x1090d6f10>>
>>> # accessing the original function
...
>>> Foo.bar.__func__
<function bar at 0x1090cc488>
>>> # turning a function back into a method
...
>>> Foo.bar.__func__.__get__(None, Foo)
<unbound method Foo.bar>
>>> Foo.bar.__func__.__get__(Foo(), Foo)
<bound method Foo.bar of <__main__.Foo object at 0x1090d6f90>>

然而,这在 Python 3 中已经发生了改变;在那里,Foo.bar 返回函数本身,不再存在未绑定方法:

Translated:

然而,这在 Python 3 中已经发生了改变;在那里,Foo.bar 返回函数本身,不再存在未绑定方法:

$ python3.3
Python 3.3.0 (default, Sep 29 2012, 08:16:08) 
[GCC 4.2.1 Compatible Apple Clang 3.1 (tags/Apple/clang-318.0.58)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
...     def bar(self):
...         pass
... 
>>> Foo.bar
<function Foo.bar at 0x105512dd0>
>>> Foo.bar.__get__(None, Foo)
<function Foo.bar at 0x105512dd0>
>>> Foo.bar.__get__(Foo(), Foo)
<bound method Foo.bar of <__main__.Foo object at 0x10552fe10>>

很棒的答案,我从中学到了几个有趣的东西。 - astrojuanlu
教程没有错。区别在于9.3.4方法对象章节的最后一段进行了解释。 - haccks
@haccks:然而,那一行的陈述并不正确。 - Martijn Pieters
然后这个语句:但是 x.fMyClass.f 不是同一个东西——它是一个方法对象,而不是一个函数对象。 在我看来是正确的。 - haccks
一个方法对象是通过将实例对象和找到的函数对象(指针)打包在一起放入抽象对象中创建的:这就是方法对象。这意味着,在调用函数对象时必须传递实例对象,而在方法对象的情况下,由于它是通过将实例对象(即self)和函数对象的指针进行打包创建的,因此不需要实例对象作为参数。 - haccks
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接