Poważnie: naprawdę nie chcesz tego robić.
Ale jest to przydatne dla zaawansowanych użytkowników Pythona, aby to zrozumieć, więc wytłumaczę to.
Komórki i freevary to wartości przypisane podczas tworzenia zamknięcia. Na przykład,
def f():
a = 1
def func():
print(a)
return func
f
zwraca zamknięcia w oparciu o func
, przechowywania odniesienie do a
. To odniesienie jest przechowywane w komórce (w rzeczywistości w freevar, ale która jest zależna od implementacji). Można sprawdzić w ten sposób:...
myfunc = f()
# ('a',)
print(myfunc.__code__.co_freevars)
# (<cell at 0xb7abce84: int object at 0x82b1de0>,)
print(myfunc.__closure__)
(„komórki” i „freevars” są bardzo podobne Freevars mają nazwy, gdzie komórki mają indeksy Oboje są przechowywane w func.__closure__
, z komórek pochodzących najpierw tylko dbają o freevars
tutaj, ponieważ to jest __class__
.)
Kiedy to zrozumiesz, zobaczysz, jak działa super(). Każda funkcja, która zawiera wezwanie do super
jest faktycznie zamknięcie, z freevar nazwie __class__
(który dodaje również, jeśli odnoszą się do __class__
siebie):
class foo:
def bar(self):
print(__class__)
(Ostrzeżenie:. To jest, gdy robi się zły)
Te komórki są widoczne w func.__closure__
, ale są tylko do odczytu; nie możesz tego zmienić. Jedynym sposobem na jej zmianę jest utworzenie nowej funkcji, która jest wykonywana z konstruktorem types.FunctionType
. Jednak twoja funkcja __init__
nie ma w ogóle freevar __class__
- więc musimy dodać. To oznacza, że musimy również utworzyć nowy obiekt kodu.
Poniższy kod to robi. Dodałem podstawową klasę B
dla celów poglądowych. Ten kod zawiera pewne założenia, np. że __init__
nie ma już wolnej zmiennej o nazwie __class__
.
Tutaj jest inny hack: nie wydaje się być konstruktorem dla typu komórki. Aby obejść ten problem, tworzona jest fikcyjna funkcja C.dummy
, która zawiera potrzebną zmienną komórki.
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()
"Czy można ustawić tę komórkę ręcznie"? Kiedy próbowałeś tego, co się stało? –
Ale jak mogę uzyskać dostęp do tej komórki po zdefiniowaniu funkcji? '__init __.__ class__' to oczywiście coś innego. Zmieniłem to pytanie, aby to wyjaśnić. – nikow
Dlaczego chcesz zdefiniować funkcję poza klasą? Chcesz tego, jeśli chcesz ponownie użyć funkcji lub użyć funkcji jako metody wiele razy. Nie możesz tego zrobić i poprawnie używać 'super (...)'. A jeśli modyfikujesz klasę w środowisku wykonawczym, możesz po prostu ją zakodować. –