3

最近、派生クラスメソッドを呼び出すメタクラスで問題が発生しました。たとえばtestA、クラスメソッドを持つ単純な baseclass を取得しますdo1(a)

class testA(object):

    @classmethod
    def do1(cls, a):
        print "in testA:",cls, a

次に、cls を出力するだけのメタクラスを作成します。

class testMetaA(type):
    def __init__(cls,cname,bases,cdict):
        print "in testMetaA: %s"%cls

次に、メタクラスを使用してtestB、期待どおりに動作するサブクラスを構築できます。

class testB(testA):

    @classmethod
    def do1(cls, a):
        print "in testB: %s"%cls
        super(testB, cls).do1(a)
    __metaclass__=testMetaA

次のように出力されますin testMetaA: <class '__main__.testB'>。そしてtestB.do1(a)期待どおりに動作します:

>>> testB.do1('hello')
in testB: <class '__main__.testB'>
in testA: <class '__main__.testB'> hello

superただし、次のように" " を含むメタクラス内で classmethod を呼び出そうとするとtestMetaB、エラーが発生します: NameError: global name 'testC' is not defined.

class testMetaB(type):
    def __init__(cls,cname,bases,cdict):
        print "in testMetaB: %s"%cls
        cls.do1("hello")

class testC(testA):

    @classmethod
    def do1(cls, a):
        print "in testC: %s"%cls
        super(testC, cls).do1(a)
    __metaclass__=testMetaB

super(cls, cls)私は最終的に代わりに使用してそれを解決する方法を見つけましたsuper(testC, cls):

class testD(testA):

    @classmethod
    def do1(cls, a):
        print "in testD: %s"%cls
        super(cls, cls).do1(a)
    __metaclass__=testMetaB

次のように印刷されます。

in testMetaB: <class '__main__.testD'>
in testD: <class '__main__.testD'>
in testA: <class '__main__.testD'> hello

testD.do1(a)も期待どおりに機能します。

>>> testD.do1('Well done')
in testD: <class '__main__.testD'>
in testA: <class '__main__.testD'> Well done

クラスメソッドでスーパーを使用する最も正しい方法はどれですか? super(cls,cls)現在のクラス名を明示的に記述する代わりに、常に使用する必要がありますか?

ありがとう!

@jsbueno

コードの一部が派生クラスを動的に作成するなどのトリックに頼っている場合、それは重要です。その名前がクラス自体ではなく別のオブジェクトに割り当てられている場合、クラス名を Super の最初のパラメーターとして使用しないでください。代わりに、クラス メソッドの cls、またはself.__class__インスタンス メソッドを Super に渡すことができます。

これは、一般的にスーパーにクラス名を使用するのは悪い考えであることを意味しますか?

私自身、通常super(type(self),self)の代わりにsuper(type(self.__class__),self)通常の方法で使用しています。を使用する大きな利点があるかどうかはわかりませんself.__class__。@jsbueno の例をこのように繰り返します。ここでは C を使用しますsuper(type(self),self)。そのD2()ため、クラスCが変更されても動作は変更されません。

>>> class A(object):
    def do(self):
        print "in class A"


>>> class B(A):
    def do(self):
        super(B, self).do()


>>> class C(A):
    def do(self):
        super(type(self),self).do()

>>> D1=B
>>> D2=C
>>> D1().do()
in class A
>>> D2().do()
in class A
>>> class B(A):
    def do(self):
        print "in new class B"


>>> D1().do()

Traceback (most recent call last):
  File "<pyshell#52>", line 1, in <module>
    D1().do()
  File "<pyshell#37>", line 3, in do
    super(B, self).do()
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> class C(A):
    def do(self):
        print "in new class C"
>>> D2().do()
in class A

@Don Question の提案によると、ここに Python のバージョンを入れます: sys.version= 2.7.2+ (default, Oct 4 2011, 20:06:09) [GCC 4.6.1]

4

1 に答える 1

3

ただし、testMetaB に続くように「スーパー」を含むメタクラス内で classmethod を呼び出そうとすると、エラーが発生します: NameError: グローバル名 'testC' が定義されていません。

名前TestCは、MetaClass が作業を終了した後にのみ、新しいクラスにバインドされます。それは、その__init__(および の前__init____new__) メソッドから戻った後です。

クラス名を最初のパラメーターとして使用する「スーパー」呼び出しを使用すると、クラス名は魔法のようにそこに表示されません。通常の状況では、クラス自体が割り当てられる(モジュール)グローバル変数です。

この場合、名前はまだ割り当てられていませんが、クラスメソッドであるため、cls変数内のクラスへの参照があるため、機能します。

コードの一部が派生クラスを動的に作成するなどのトリックに頼る場合、それは重要です。その名前がクラス自体ではなく別のオブジェクトに割り当てられている場合、クラス名を Super の最初のパラメーターとして使用しないでください。代わりにcls、クラス メソッドまたはself.__class__インスタンス メソッドを Super に渡すことができます。

これは、クラス名のグローバルな名前バインディングが super で使用されるものであることを示すスニペットです。

>>> class A(object):
...   def do(self):
...      print "In class A"
... 
>>> class B(A):
...   def do(self):
...     super(B, self).do()
... 
>>> C = B
>>> C().do()
In class A
>>> class B(object):
...   def do(self):
...      print "in new class B"
... 
>>> C().do()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in do
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> 
于 2012-07-13T11:47:31.963 に答える