Почему co_varnames не возвращает список всех имен переменных? - PullRequest
2 голосов
/ 01 мая 2020

Почему результаты приведенных ниже фрагментов отличаются?

def Foo():
  i = 0
  def Bar():
    nonlocal i
    i = 1
  return Bar()

print(Foo.__code__.co_varnames)

# it will print: ('Bar',)
def Foo():
  i = 0
  def Bar():
    i = 1
  return Bar()

print(Foo.__code__.co_varnames)

# it will print: ('i', 'Bar',)

Как видите, результаты разные, и я не знаю, почему они разные.

1 Ответ

2 голосов
/ 02 мая 2020

Хорошо, это немного неуловимо. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * i * *1004* * * * * * * * * * * * * * * * * * * *1004* *1004* *1004*. .

Обратите внимание, что происходит, когда мы разбираем байт-код:

In [2]: def Foo():
   ...:   i = 0
   ...:   def Bar():
   ...:     nonlocal i
   ...:     i = 1
   ...:   return Bar()
   ...:

In [3]: Foo.__code__.co_varnames
Out[3]: ('Bar',)


In [4]: import dis

In [5]: dis.dis(Foo)
  2           0 LOAD_CONST               1 (0)
              2 STORE_DEREF              0 (i)

  3           4 LOAD_CLOSURE             0 (i)
              6 BUILD_TUPLE              1
              8 LOAD_CONST               2 (<code object Bar at 0x101072be0, file "<ipython-input-4-a3a062461e32>", line 3>)
             10 LOAD_CONST               3 ('Foo.<locals>.Bar')
             12 MAKE_FUNCTION            8 (closure)
             14 STORE_FAST               0 (Bar)

  6          16 LOAD_FAST                0 (Bar)
             18 CALL_FUNCTION            0
             20 RETURN_VALUE

Disassembly of <code object Bar at 0x101072be0, file "<ipython-input-4-a3a062461e32>", line 3>:
  5           0 LOAD_CONST               1 (1)
              2 STORE_DEREF              0 (i)
              4 LOAD_CONST               0 (None)
              6 RETURN_VALUE

Обратите внимание, код операции STORE_DEREF не STORE_FAST. Итак, рассмотрим:

In [9]: def Foo():
   ...:   i = 0
   ...:   def Bar():
   ...:     nonlocal i
   ...:     i = 1
   ...:   return Bar
   ...:

In [10]: bar = Foo()

In [11]: bar.__closure__
Out[11]: (<cell at 0x1040a7940: int object at 0x100980bc0>,)

Поскольку на него ссылаются во вложенной функции, это будет доступно в co_cellvars:

In [12]: foo_code = Foo.__code__

In [13]: foo_code.co_cellvars
Out[13]: ('i',)

Это объясняется в модели данных документация для внутренних типов:

co_varnames - кортеж, содержащий имена локальных переменных (начиная с имен аргументов); co_cellvars - это кортеж , содержащий имена локальных переменных, на которые ссылаются вложенные функции ; co_freevars - это кортеж, содержащий имена свободных переменных ...

Итак, чтобы получить все локальные переменные , вам нужно:

In [16]: Foo.__code__.co_varnames + Foo.__code__.co_cellvars
Out[16]: ('Bar', 'i')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...