14

Python 3super()では、の代わりに使用できますsuper(MyClass, self)が、これはクラス内で定義されたメソッドでのみ機能します。Michele Simionatoの記事で説明されているように、次の例は機能しません。

def __init__(self):
    print('calling __init__')
    super().__init__()

class C(object):
    __init__ = __init__

if __name__ == '__main__':
    c = C()

この場合は定義されていないセルsuper()を探すため、失敗します。__class__

関数の定義後にこのセルを手動で設定することは可能ですか、それとも不可能ですか?

残念ながら、私はこのコンテキストでセルがどのように機能するかを理解していません(そのためのドキュメントはあまり見つかりませんでした)。私は次のようなものを望んでいます

__init__.__class_cell_thingy__ = C

もちろん、これはクラスの割り当てが明確/一意である状況でのみ使用します(私の場合、クラスにメソッドを追加するプロセス全体が自動化されているため、このような行を追加するのは簡単です)。

4

3 に答える 3

27

真剣に:あなたは本当にこれをしたくありません。

ただし、Pythonの上級ユーザーにとってはこれを理解しておくと便利なので、説明します。

セルとfreevarsは、クロージャが作成されたときに割り当てられる値です。例えば、

def f():
    a = 1
    def func():
        print(a)
    return func

fに基づいてクロージャを返し、funcへの参照を格納しますa。その参照はセルに格納されます(実際にはfreevarに格納されますが、どちらが実装次第です)。あなたはこれを調べることができます:

myfunc = f()
# ('a',)
print(myfunc.__code__.co_freevars)
# (<cell at 0xb7abce84: int object at 0x82b1de0>,)
print(myfunc.__closure__)

(「cells」と「freevars」は非常に似ています。Freevarsには名前があり、セルにはインデックスがあります。どちらもに格納されfunc.__closure__、セルが最初になります。ここでのみ気にします。freevarsそれが何で__class__あるかです。)

それを理解すると、super()が実際にどのように機能するかを確認できます。toの呼び出しを含む関数は、super実際にはクロージャーであり、freevarという名前が付けられて__class__います(これは、自分自身を参照する場合にも追加され__class__ます)。

class foo:
    def bar(self):
        print(__class__)

(警告:これは物事が悪になるところです。)

これらのセルはに表示されますがfunc.__closure__、読み取り専用です。変更することはできません。これを変更する唯一の方法は、コンストラクターを使用して実行される新しい関数を作成することですtypes.FunctionType。ただし、__init__関数にはfreevarがまったくないため、__class__freevarを追加する必要があります。つまり、新しいコードオブジェクトも作成する必要があります。

以下のコードはこれを行います。B実例となる目的で基本クラスを追加しました。このコードは、いくつかの仮定をします。。という__init__名前の自由変数はまだありません__class__

ここには別のハックがあります。セル型のコンストラクターがないようです。これを回避するために、C.dummy必要なセル変数を持つダミー関数が作成されます。

import types

class B(object):
    def __init__(self):
        print("base")

class C(B):
    def dummy(self): __class__

def __init__(self):
    print('calling __init__')
    super().__init__()

def MakeCodeObjectWithClass(c):
    """
    Return a copy of the code object c, with __class__ added to the end
    of co_freevars.
    """
    return types.CodeType(c.co_argcount, c.co_kwonlyargcount, c.co_nlocals,
            c.co_stacksize, c.co_flags, c.co_code, c.co_consts, c.co_names,
            c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno,
            c.co_lnotab, c.co_freevars + ('__class__',), c.co_cellvars)

new_code = MakeCodeObjectWithClass(__init__.__code__)
old_closure = __init__.__closure__ or ()
C.__init__ = types.FunctionType(new_code, globals(), __init__.__name__,
    __init__.__defaults__, old_closure + (C.dummy.__closure__[0],))

if __name__ == '__main__':
    c = C()
于 2011-02-03T12:09:05.630 に答える
2

関数の辞書を使用できます。

def f(self):
    super(f.owner_cls, self).f()
    print("B")

def add_to_class(cls, member, name=None):
    if hasattr(member, 'owner_cls'):
        raise ValueError("%r already added to class %r" % (member, member.owner_cls))
    member.owner_cls = cls
    if name is None:
        name = member.__name__
    setattr(cls, name, member)

class A:
     def f(self):
         print("A")

class B(A):
     pass

add_to_class(B, f)

B().f()

member_name関数内のメンバーの名前の名前をハードコーディングしたくない場合は、別の属性を追加することもできます。

于 2011-02-03T11:56:50.687 に答える
1

たぶん、でもあなたは?どちらの場合も、暗黙的な方法が機能しなかったため、どのクラスであるかを何らかの形で明示する必要があります。どういうわけかセルを明示的に設定できるかもしれませんが、そうする理由はありません。パラメータを明示的に渡すだけです。

def __init__(self):
    print('calling __init__')
    super(self.__class__, self).__init__()

class C(object):
    __init__ = __init__

if __name__ == '__main__':
    c = C()

(次のように、実際のクラスを直接渡すことができるとよいでしょう。

def __init__(self):
    print('calling __init__')
    super(C, self).__init__()

class C(object):
    __init__ = __init__

if __name__ == '__main__':
    c = C()

しかし、それができれば、__init__直接C着ることができるので、できないと仮定してください。

于 2011-02-03T11:16:01.250 に答える