6

次のコードではエラーが発生します。

class FooMeta(type):
  def __new__(mcl, classname, bases, classdict):
   return type.__new__(mcl, classname, bases, dict())

  def __init__(cls, name, bases, classdict):
    super(FooMeta, cls).__init__(name, bases, classdict)

    d = dict()
    for key, item in classdict.items():
      if key.startswith("_"):
        setattr(cls, key, item)
      else:
        d[key] = item
    setattr(cls, '_items', d)

class Foo(object):
  __metaclass__ = FooMeta

class Passing(Foo):
  def __init__(self, *args, **kw):
    pass

class Failing(Foo):
  def __new__(cls, *args, **kw):
    return super(Failing, cls).__new__(cls)

  def __init__(self, *args, **kw):
    pass

fail = Failing()

エラーは

Traceback (most recent call last):
  File ".../FooMeta,py", line 30, in <module>
    fail = Failing()
TypeError: unbound method __new__() must be called with Failing instance as first argument (got FooMeta instance instead)

__new__メタクラスから呼び出される場合、呼び出し方に若干の違いがあるようです。

1) どうしてこうなったか分かる人いますか? 2)それを修正する方法はありますか?

4

1 に答える 1

3

通常行われているように属性を設定できるようにする場合:

class FooMeta(type):
    def __new__(mcl, classname, bases, classdict):
        return type.__new__(mcl, classname, bases, classdict)

でそれらをいじらないでください。通常は静的__init__メソッドであることがわかります。Failing.__new__

class FooMeta(type):
    def __new__(mcl, classname, bases, classdict):
        return type.__new__(mcl, classname, bases, classdict)
        # return type.__new__(mcl, classname, bases, dict())

    def __init__(cls, name, bases, classdict):
        super(FooMeta, cls).__init__(name, bases, classdict)
        d = dict()
        for key, item in classdict.items():
            if key.startswith("_"):
                # setattr(cls, key, item)
                pass
            else:
                d[key] = item
        setattr(cls, '_items', d)

class Foo(object):
    __metaclass__ = FooMeta

class Failing(Foo):
    def __new__(cls, *args, **kw):
        return super(Failing, cls).__new__(cls)

    def __init__(self, *args, **kw):
        pass

print(Failing.__dict__['__new__'])
foo = Failing()

収量:

<staticmethod object at 0xb774ba94>

しかし、この print ステートメントを末尾に追加して投稿したコードを実行すると、次のようになります。

print(Failing.__dict__['__new__'])
foo = Failing()

__new__関数に変更されていることがわかります。

<function __new__ at 0xb747372c>

これを修正するには、classdict のすべての属性を通常どおりに設定できるようにしてから、 で始まらない属性を削除します_

class FooMeta(type):
    def __new__(mcl, classname, bases, classdict):
        return type.__new__(mcl, classname, bases, classdict)

    def __init__(cls, name, bases, classdict):
        super(FooMeta, cls).__init__(name, bases, classdict)
        d = dict()
        for key, item in classdict.items():
            if not key.startswith("_"):
                delattr(cls, key)
                d[key] = item
        setattr(cls, '_items', d)

class Foo(object):
    __metaclass__ = FooMeta

class Passing(Foo):
    def __init__(self, *args, **kw):
        pass

class Failing(Foo):
    def __new__(cls, *args, **kw):
        return super(Failing, cls).__new__(cls)

    def __init__(self, *args, **kw):
        pass

print(Failing.__dict__['__new__'])
foo = Failing()
于 2012-12-03T23:09:08.077 に答える