如何让super()通过手动填充\uu class_uu单元格来工作?

2024-10-01 09:22:32 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python3中,可以使用super()代替super(MyClass, self),但这只适用于在类内部定义的方法。如Michele Simionato's article中所述,以下示例不起作用:

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

class C(object):
    __init__ = __init__

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

它失败是因为super()查找一个__class__cell,在本例中没有定义。在

是否可以在定义函数后手动设置此单元格,或者不可能?

不幸的是,我不明白细胞在这种情况下是如何工作的(没有找到很多相关的文档)。我希望像

^{pr2}$

当然,我只会在类赋值明确/唯一的情况下使用它(在我的例子中,向中的类添加方法的整个过程是自动化的,因此添加这样一行会很简单)。在


Tags: 方法self示例定义initdefarticlemyclass
3条回答

也许吧,但你会吗?在这两种情况下,你需要以某种方式明确它是哪一个类,因为隐式方法不起作用。也许你可以显式地设置单元格,但是没有理由这么做。只需显式地传递参数。在

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

class C(object):
    __init__ = __init__

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

(如果你可以直接在实际的课堂上通过,比如:

^{pr2}$

但是如果你能做到,你可以直接把__init__放在C上,所以假设你不能

说真的:你真的不想这么做。在

但是,对于Python的高级用户来说理解这一点是很有用的,所以我将对此进行解释。在

Cells和freevar是在创建闭包时指定的值。例如

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

f返回基于func的闭包,存储对a的引用。该引用存储在一个单元中(实际上是在一个freevar中,但是哪个单元取决于实现)。您可以检查:

^{pr2}$

(“细胞”和“自由变量”非常相似。freevar有名称,其中单元格有索引。它们都储存在func.__closure__中,细胞是第一位的。这里我们只关心freevars,因为这就是__class__是什么。)

一旦理解了这一点,就可以看到super()实际上是如何工作的。任何包含对super的调用的函数实际上都是一个闭包,其中有一个名为__class__的freevar(如果您自己引用__class__也会添加该值):

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

(警告:这是事情变得邪恶的地方。)

这些细胞在func.__closure__中可见,但它是只读的;您无法更改它。更改它的唯一方法是创建一个新函数,这是用types.FunctionType构造函数完成的。但是,您的__init__函数根本没有__class__自由变量,因此我们需要添加一个。这意味着我们还必须创建一个新的代码对象。在

下面的代码就是这样做的。我添加了一个基类B,以便于说明。这段代码做了一些假设,例如__init__还没有一个名为__class__的自由变量。在

这里还有另一个问题:似乎没有单元类型的构造函数。为了解决这个问题,我们创建了一个伪函数C.dummy,它有我们需要的cell变量。在

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()

你可以使用函数的字典。在

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。在

相关问题 更多 >