14

在 Python 3 中,可以使用super()代替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 的高级用户来说理解这一点很有用,所以我将对其进行解释。

单元格和自由变量是创建闭包时分配的值。例如,

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

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

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() 是如何工作的。任何包含调用的函数super实际上都是一个闭包,带有一个名为的 freevar __class__(如果您引用__class__自己,也会添加它):

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

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

这些单元格在 中可见func.__closure__,但它是只读的;你不能改变它。更改它的唯一方法是创建一个新函数,该函数由types.FunctionType构造函数完成。但是,您的__init__函数根本没有__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

You can use the function's dictionary.

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

You can even add another attribute member_name if you don't want to hardcode the name of the name of the member inside the function.

于 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 回答