4

Pythonドキュメントの「データモデル」の章の「新しいスタイルのクラスの特別なメソッドルックアップ」セクションから(太字の強調鉱山):

新しいスタイルのクラスの場合、特別なメソッドの暗黙的な呼び出しは、オブジェクトのインスタンス ディクショナリではなく、オブジェクトの型で定義されている場合にのみ正しく動作することが保証されます。その動作が、次のコードが例外を発生させる理由です (古いスタイルのクラスの同等の例とは異なります)。

>>> class C(object):
...     pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()

この動作の背後にある理論的根拠は、型オブジェクトを含むすべてのオブジェクトによって実装される__hash__()や などの多くの特別なメソッドにあります。__repr__()これらのメソッドの暗黙的なルックアップで従来のルックアップ プロセスが使用された場合、型オブジェクト自体で呼び出されたときに失敗します。

>>> 1 .__hash__() == hash(1)
True
>>> int.__hash__() == hash(int)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor ’__hash__’ of ’int’ object needs an argument

この方法でクラスのバインドされていないメソッドを誤って呼び出そうとすることは、「メタクラスの混乱」と呼ばれることがあり、特別なメソッドを検索するときにインスタンスをバイパスすることで回避されます。

>>> type(1).__hash__(1) == hash(1)
True
>>> type(int).__hash__(int) == hash(int)
True

太字の単語がうまく聞き取れません…</p>

4

3 に答える 3

2

ここで何が起こっているのかを理解するには、従来の属性検索プロセスを (基本的に) 理解する必要があります。典型的な入門用オブジェクト指向プログラミングの例を取り上げます -fidoは次のDogとおりです。

class Dog(object):
    pass

fido = Dog()

という場合、Python が最初に行うことは、( のエントリとして) で呼び出さfido.walk()れる関数を探し、引数なしで呼び出すことです。つまり、次のように定義されている関数です。walkfidofido.__dict__

def walk():
   print "Yay! Walking! My favourite thing!"

fido.walk = walk

そしてfido.walk()動作します。これを行わなかった場合、 ( である)walk内の属性を探し、最初の引数 (つまり) としてインスタンスを使用してそれを呼び出します。これは、Python でメソッドを定義する通常の方法によってトリガーされます。type(fido)Dogself

class Dog:
    def walk(self):
         print "Yay! Walking! My favourite thing!"

これで、 を呼び出すとrepr(fido)、最終的に特別なメソッド が呼び出され__repr__ます。次のように定義されている可能性があります (不十分ですが、例証的に)。

class Dog:
    def __repr__(self):
          return 'Dog()'

ただし、太字のテキストは、これを行うことも理にかなっていることを示しています。

 repr(Dog)

__repr__先ほど説明した検索プロセスの下で、最初に検索されるのは、割り当てられたDog... と呼ばれるメソッドです。ほら、1 つあるのです。したがって、Python は次のように呼び出します。

Dog.__repr__()

そして、それは私たちの顔に爆発します:

>>> Dog.__repr__()
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    Dog.__repr__()
TypeError: __repr__() takes exactly 1 argument (0 given)

インスタンスが引数として渡されることを__repr__()期待しているためです。これを機能させるためにこれを行うことができます:Dogself

class Dog:
    def __repr__(self=None):
       if self is None:
           # return repr of Dog
       # return repr of self

ただし、カスタム関数を作成するたびにこれを行う必要があります。__repr__クラスの を見つける方法を知る必要が__repr__あることは問題ですが、それほど大きな問題ではありません。Dog独自のクラス ( type(Dog)) に委譲し、引数として を呼び出す__repr__ことができますDogself

 if self is None:
   return type(Dog).__repr__(Dog)

しかし、最初に、クラス名が将来変更されると、同じ行で 2 回言及する必要があるため、これが壊れます。しかし、より大きな問題は、これが基本的にボイラープレートになることです: 実装の 99% は、チェーンを委任するか、それを忘れてバグが発生します。したがって、Python はこれらの段落で説明されているアプローチを採用repr(foo)__repr__ますfoo

type(foo).__repr__(foo) 
于 2012-09-01T14:08:25.400 に答える
1

覚えておかなければならないことは、クラスはそのメタクラスのインスタンスであるということです。インスタンスだけでなく、タイプに対してもいくつかの操作を実行する必要があります。インスタンスのメソッドが実行された場合、インスタンスのメソッド (この場合は実際にはクラス) はメタクラスではなくクラスのインスタンスを必要とするため、失敗します。

class MC(type):
  def foo(self):
    print 'foo'

class C(object):
  __metaclass__ = MC
  def bar(self):
    print 'bar'

C.foo()
C().bar()
C.bar()
于 2012-09-01T12:36:15.187 に答える