Почему эта несвязанная переменная работает в Python (pyquery)?
Код из руководства pyquery
from pyquery import PyQuery
d = PyQuery('<p class="hello">Hi</p><p>Bye</p>')
d('p').filter(lambda i: PyQuery(this).text() == 'Hi')
Мой вопрос this
в третьей строке является несвязанной переменной и никогда не определяется в текущей среде, но приведенный выше код все еще работает.
Как это работает? Почему он не жалуется NameError: name 'this' is not defined
?
Кажется, что-то происходит в https://bitbucket.org/olauzanne/pyquery/src/c148e4445f49/pyquery/pyquery.py#cl-478, может ли кто-нибудь объяснить это?
Ответы
Ответ 1
Это делается с помощью магии Python func_globals
, которая
Ссылка на словарь, который содержит глобальные переменные функций - глобальное пространство имен модуля, в котором определена функция.
Если вы погрузитесь в код PyQuery:
def func_globals(f):
return f.__globals__ if PY3k else f.func_globals
def filter(self, selector):
if not hasattr(selector, '__call__'):
return self._filter_only(selector, self)
else:
elements = []
try:
for i, this in enumerate(self):
# The magic happens here
func_globals(selector)['this'] = this
if callback(selector, i):
elements.append(this)
finally:
f_globals = func_globals(selector)
if 'this' in f_globals:
del f_globals['this']
return self.__class__(elements, **dict(parent=self))
Ответ 2
Другие правильно указывают, как this
определяется внутри этой лямбда, о которой вы говорите.
Чтобы подробнее рассказать об этом, попробуйте следующий код:
>>> def f():
... print f_global
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
NameError: global name 'f_global' is not defined
>>> f.__globals__['f_global'] = "whoa!!" #Modify f() globals.
>>> f()
whoa!!
Это именно то, что происходит там. В строке 496 вы увидите следующий код:
for i, this in enumerate(self): #this is the current object/node.
func_globals(selector)['this'] = this #func_globals returns selector.__globals__
Ответ 3
Это не бросает NameError, потому что переменная может существовать в момент вызова фактической функции.
>>> f = lambda i: some_non_named_var
>>> f(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
NameError: global name 'some_non_named_var' is not defined
Вышеописанная информация не является ошибкой, пока вы не вызовете функцию, которую вы отложили. В приведенном ниже примере кода они вручную устанавливают переменную с именем this
в func_globals
перед вызовом функции лямбда selector
.