Python 如何通过手动填充_类_)单元格使super()工作?
在Python3中,可以使用Python 如何通过手动填充_类_)单元格使super()工作?,python,metaprogramming,python-3.x,super,Python,Metaprogramming,Python 3.x,Super,在Python3中,可以使用super()而不是super(MyClass,self),但这只适用于类内部定义的方法。如以下示例中所述,不起作用: def __init__(self): print('calling __init__') super().__init__() class C(object): __init__ = __init__ if __name__ == '__main__': c = C() 它失败是因为super()查找一个\uu
super()
而不是super(MyClass,self)
,但这只适用于类内部定义的方法。如以下示例中所述,不起作用:
def __init__(self):
print('calling __init__')
super().__init__()
class C(object):
__init__ = __init__
if __name__ == '__main__':
c = C()
它失败是因为super()
查找一个\uuuuuuu类
,该类在本例中未定义
在定义函数后,是否可以手动设置此单元格,还是不可能? 不幸的是,我不理解单元格在这种情况下是如何工作的(没有找到太多的文档)。我希望像这样的事情
__init__.__class_cell_thingy__ = C
当然,我只会在类赋值明确/唯一的情况下使用它(在我的例子中,向中的类添加方法的整个过程是自动化的,因此添加这样一行会很简单)。也许吧,但是你会这样做吗?在这两种情况下,您都需要明确它是哪个类,因为隐式方法不起作用。也许您可以以某种方式显式设置单元格,但没有理由这样做。只需显式地传入参数
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()
但是如果你能做到这一点,你可以直接把
\uuu init\uuu
放在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\u name
。认真地说:您真的不想这样做
但是,Python的高级用户理解这一点很有用,所以我将对此进行解释
单元格和自由变量是创建闭包时指定的值。例如
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__)
(警告:这就是事情变得邪恶的地方。)
这些单元格在func.\uuuuu closure\uuuuuu
中可见,但它是只读的;您不能更改它。更改它的唯一方法是创建一个新函数,这是使用类型.FunctionType
构造函数完成的。但是,您的\uuuuu init\uuuuuuu
函数根本没有\uuuuuuu类
自由变量,因此我们需要添加一个。这意味着我们还必须创建一个新的代码对象
下面的代码就是这样做的。为了演示的目的,我添加了一个基类B
。这段代码做了一些假设,例如\uuuu init\uuuu
还没有一个名为\uu class\uuuu
的自由变量
这里还有另一个问题:似乎没有单元格类型的构造函数。为了解决这个问题,创建了一个虚拟函数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()
“是否可以手动设置此单元格”?当您尝试此操作时,发生了什么?但是在定义函数后,我如何才能访问此单元格?
\uuuuu init\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhod多次。你不能正确地使用super(…)
。如果你在运行时修改一个类,你可以简单地对它进行硬编码。@nikow:嗯,调用super()在类自己的\uuuuu init\uuuuu
中,然后可能用一个修饰符来包装该init。@s.Lott:在我的示例中使用\uuuuuu init\uuuu
方法并不重要在我看来,我的问题相当具体,我得到了一个很好的答案。所以我真的不明白为什么我要为这个问题辩护。谢谢你的回答,但你在这里提出的建议是非常错误的。你不能用self.\uu class\uuuuuuuu
和super
,就像最近在这里解释的那样:@nikow:好吧,如果你要去的话如果子类也使用super(),那么要将其子类化,您可能会遇到问题。因此,您希望对许多不同的类使用相同的\uuuu init\uuuu
(因为否则您不会在类之外定义它)你不知道哪些类,也不知道这些类是否或如何子类化?在这种情况下,我会说你解决问题的方法是错误的。我可以推荐一个类装饰器,或者一个带有适配器的组件架构吗?@Lennart:添加的函数是按照AOP的想法在别处定义的,这在我们的项目中非常有效t、 在实际代码中,这些“方面”是在运行时激活/停用的,因此使用decorator类并不是一个真正的选项。我只是希望能够启用简化的super()
调用,毕竟它们是出于某种原因被添加到Python 3中的。@nikow:当然,我理解。只是调用super()从您案例中添加的方法来看,这似乎是一个坏主意。因此,建议使用适配器(实现使代码在其他地方定义的相同功能)或者decorators。由于它是运行时,deocrators是不可能的,但适配器不是。您应该真正了解Zope组件体系结构,它是面向方面的,即使它不喜欢吹嘘它。:)但对于正在进行的项目来说可能太晚了,但是你可能会有想法。你永远不应该使用self.\uu class\uu
来super()
,因为现在如果你将C
子类化,你会得到一个无限循环。传入显式类对象,或为\uuuuuu class\uuuuu
提供闭包<代码>self.\uuuu类\uuuu
是错误的引用。这会起作用,但我不确定我是否更喜欢它,而不是编写类的显式解决方案。毕竟我知道该方法将属于哪个类,我只想从更方便的Python3super()
中获益
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()