Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can this unbound variable work in Python (pyquery)?

The code is from the guide of pyquery

from pyquery import PyQuery
d = PyQuery('<p class="hello">Hi</p><p>Bye</p>')
d('p').filter(lambda i: PyQuery(this).text() == 'Hi')

My question is this in the 3rd line is an unbound variable and is never defined in current environment, but the above code still works.

How can it work? Why it doesn't complain NameError: name 'this' is not defined?

It seems that something happens at https://bitbucket.org/olauzanne/pyquery/src/c148e4445f49/pyquery/pyquery.py#cl-478 , could anybody explain it?

like image 456
Hanfei Sun Avatar asked Aug 05 '12 07:08

Hanfei Sun


People also ask

What are unbound methods in Python?

Methods that do not have an instance of the class as the first argument are known as unbound methods. As of Python 3.0, the unbound methods have been removed from the language. They are not bounded with any specific object of the class. To make the method myFunc2 () work in the above class it should be made into a static method

What is unboundlocalerror in Python?

It is very irritating when a code which ran smoothly minutes ago, stucks due to a stupid mistake and hence, shows an error which is popular or rather common among Python developers called as “ UnboundLocalError ” .

What is the difference between global and local variables in Python?

In python, all the variables inside a function are global if they are not assigned any value to them. That means if a variable is only referenced inside a function, it is global. However if we assign any value to a variable inside a function, its scope becomes local to that unless explicitly declared global.

What is an unused local variable in Python?

Unused local variable in Python Last Updated : 01 Oct, 2020 A variable defined inside a function block or a looping block loses its scope outside that block is called ad local variable to that block. A local variable cannot be accessed outside the block it is defined.


2 Answers

This is done via Python's func_globals magic, which is

A reference to the dictionary that holds the function’s global variables — the global namespace of the module in which the function was defined.

If you dive into PyQuery code:

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))
like image 93
Zaur Nasibov Avatar answered Oct 13 '22 19:10

Zaur Nasibov


Others have correctly point out how this is defined inside that lambda you are talking about.

To elaborate a bit more, try out the following code:

>>> 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()'s globals.
>>> f()
whoa!!

This is exactly what is happening there. On line 496, you would see the following code:

for i, this in enumerate(self):             #this is the current object/node.
     func_globals(selector)['this'] = this  #func_globals returns selector.__globals__
like image 41
UltraInstinct Avatar answered Oct 13 '22 17:10

UltraInstinct