web-dev-qa-db-ja.com

メソッドオブジェクトと関数オブジェクト、Pythonクラスインスタンスとクラス

Pythonチュートリアルリリース2.7.3、2012年11月1日、第9章:クラス、66ページの最後の行(- ソース ):

インスタンスオブジェクトの有効なメソッド名は、そのクラスによって異なります。定義上、関数オブジェクトであるクラスのすべての属性は、そのインスタンスの対応するメソッドを定義します。したがって、この例では、MyClass.fは関数であるため、x.fは有効なメソッド参照ですが、MyClass.iは関数ではないため、x.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.fおよびMyClass.fはinstancemethodです。タイプに違いはありませんが、チュートリアルではそうではないと述べています。誰かが明確にできますか?

21
Ankur Agarwal

BoundvsUnboundメソッド-説明。

...またはなぜ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以前では、インスタンスにアタッチされていないメソッドはunboundメソッドになると考えられていました。 )は、最初の引数(self)がオブジェクトのインスタンスである必要があるという制限付きの関数でした。これにより、インスタンスにバインドしてバインドされたメソッド

>>> MyClass.foo
<unbound method MyClass.foo>

時間が経つにつれて、バインドされていないメソッドは実際にはこの奇妙な制限のある関数であり、実際には問題ではないことが明らかになりました(self '正しい'type)なので、言語から削除されました(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作成者のGuidovan Rossum自身の口から 彼の 'Pythonの歴史'シリーズ から完全に読むことができます。

24
Gareth Latty

チュートリアルは確かに間違っています。両方とも class.functionnameおよびinstance.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は関数自体を返し、バインドされていないメソッドは存在しなくなります。

$ 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>>
13
Martijn Pieters