En serio: realmente no quiere hacer esto.
Pero, es útil para los usuarios avanzados de Python entender esto, así que lo explicaré.
Las celdas y los freevars son los valores asignados cuando se crea un cierre. Por ejemplo,
def f():
a = 1
def func():
print(a)
return func
f
devuelve un cierre basado en func
, almacenar una referencia a a
. Esa referencia se almacena en una celda (en realidad en una versión libre, pero cuál corresponde a la implementación). Puede examinar esto:...
myfunc = f()
# ('a',)
print(myfunc.__code__.co_freevars)
# (<cell at 0xb7abce84: int object at 0x82b1de0>,)
print(myfunc.__closure__)
("células" y "freevars" son muy similares Freevars tienen nombres, donde las células tienen índices Los dos son almacenados en func.__closure__
, con células procedentes primera sólo nos interesan freevars
aquí, ya que eso es lo que __class__
es.)
Una vez que comprenda eso, podrá ver cómo funciona super(). Cualquier función que contiene una llamada a super
es en realidad un cierre, con un freevar llamado __class__
(que también se añade si se refiere a __class__
mismo):
class foo:
def bar(self):
print(__class__)
(Advertencia:. Aquí es donde las cosas se ponen mal)
Estas celdas son visibles en func.__closure__
, pero es de solo lectura; no puedes cambiarlo La única forma de cambiarlo es crear una nueva función, que se realiza con el constructor types.FunctionType
. Sin embargo, su función __init__
no tiene ningún __class__
freevar, por lo que debemos agregar una. Eso significa que tenemos que crear un nuevo objeto de código también.
El siguiente código hace esto. Agregué una clase base B
con fines demostrativos. Este código hace algunas suposiciones, ej. que __init__
aún no tiene una variable gratuita llamada __class__
.
Hay otro truco aquí: no parece haber un constructor para el tipo de celda. Para solucionarlo, se crea una función ficticia C.dummy
que tiene la variable de celda que necesitamos.
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()
"¿Es posible configurar esta celda manualmente?" Cuando probaste esto, ¿qué pasó? –
Pero, ¿cómo puedo obtener acceso a esta celda después de definir la función? '__init __.__ class__' es, por supuesto, algo más. Ahora he editado la pregunta para aclarar esto. – nikow
¿Por qué quieres definir la función fuera de la clase? Querrá esto si quiere reutilizar una función, o usar una función como método varias veces. No puedes hacer esas cosas y usar 'super (...)' correctamente. Y si está modificando una clase en tiempo de ejecución, simplemente puede codificarla. –