Zagadka na znajomość Pythona

0
class M(type):
  def __new__(cls, name, bases, loc):
    print(id(loc))
    return super().__new__(cls, name, bases, loc)

class C(metaclass=M):
  print(id(locals()))

Jak myślicie, co się tutaj wypisze i dlaczego?

0

Jakieś ID. Nie wiadomo jakie numerki bo przy każdym odpaleniu inne.
Dlaczego? Bo id zwraca id obiektu (który jest numerem)

0

Technicznie masz rację, ale to nie jest istotą pytania.

1

Dlaczego cokolwiek ma się wypisać, przecież nic nie wykonuje funkcji print wewnątrz klasy C?


Przede wszystkim wypisze się na standardowym wyjściu dwukrotnie ten sam identyfikator obiektu zwrócony przed id(). Pewnie większość Pythonowców słyszała stwierdzenie, że wszystko w tym języku jest obiektem. Tak samo jest z klasą. Tak więc umieszczenie w konsoli następującej definicji klasy spowoduje wypisanie na ekran napisu:

>>> class HelloWorld:
...     print("Hello World!")
...
Hello World!

Dzieje się tak dlatego, że klasa też jest obiektem i żeby taki obiekt mógł zostać utworzony, to interpreter musi przeparsować ciało klasy i zapisać sobie referencje do atrybutów i metod. Ponieważ C dziedziczy po metaklasie, to jej domyślny konstruktor __new__ (którego jawnie nie widzimy) wywołuje konstruktor __new__ rodzica (który widnieje w kodzie metaklasy). A ten również zawiera w sobie funkcję print, która zostaje wywołana na skutek parsowania.

Do czego odwołuje się wydrukowany identyfikator?


Zgodnie z dokumentacją:

The class’s suite is then executed in a new execution frame, using a newly created local namespace and the original global namespace.

tworzona jest lokalna przestrzeń nazw, do której dołączane są elementy z pierwotnej globalnej przestrzeni nazw. Można to łatwo sprawdzić, zamieniając kod na:

class M(type):
  def __new__(cls, name, bases, loc):
    print(loc)
    return super().__new__(cls, name, bases, loc)

class C(metaclass=M):
  print(locals())

W moim przypadku:

{'__module__': '__main__', '__qualname__': 'C'}
{'__module__': '__main__', '__qualname__': 'C'}

Dlaczego z każdym wywołaniem zmieniają się identyfikatory?


Moim zdaniem wynika to z zapisu do pamięci. Zauważyłem, że identyfikatory lokalnej przestrzeni nazw nie rosną monotonicznie, tylko skaczą. Raz są większe niż poprzednie id, raz mniejsze. Najprawdopodobniej jest za to odpowiedzialny GC, który sprząta w tle nadpisane przestrzenie.

0

Z grubsza.

Okazuje się, że Python przy deklaracji klasy tworzy sobie pod kopułą funkcję, którą wykonuje. Jej ciałem jest wszystko w bloku class. Różnica między normalną funkcją jest taka, że wszystko co tam zostanie zadeklarowane w localsach jest potem dołączane do powstałej klasy. Nie zdawałem sobie do tej pory z tego sprawy.

Fajne potworki można w ten sposób tworzyć.

class A():
  for i in range(100):
    locals()['a' + str(i)] = i

Na rozmowę rekrutacyjną to jest trochę zbyt tricky, ale mechanizm ciekawy.

0

Takie moje pytanie, po co to, bo software engineering z tego nie będzie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1