Connecting slots and signals in PyQt4 in a loop
This is just, how scoping, name lookup and closures are defined in Python.
Python only introduces new bindings in namespace through assignment and through parameter lists of functions. i
is therefore not actually defined in the namespace of the lambda
, but in the namespace of __init__()
. The name lookup for i
in the lambda consequently ends up in the namespace of __init__()
, where i
is eventually bound to 9
. This is called "closure".
You can work around these admittedly not really intuitive (but well-defined) semantics by passing i
as a keyword argument with default value. As said, names in parameter lists introduce new bindings in the local namespace, so i
inside the lambda
then becomes independent from i
in .__init__()
:
self._numberButtons[i].clicked.connect(lambda checked, i=i: self._number(i))
UPDATE: clicked
has a default checked
argument that would override the value of i
, so it must be added to the argument list before the keyword value.
A more readable, less magic alternative is functools.partial
:
self._numberButtons[i].clicked.connect(partial(self._number, i))
I'm using new-style signal and slot syntax here simply for convenience, old style syntax works just the same.
You are creating closures. Closures really capture a variable, not the value of a variable. At the end of __init__
, i
is the last element of range(0, 10)
, i.e. 9
. All the lambdas you created in this scope refer to this i
and only when they are invoked, they get the value of i
at the time they are at invoked (however, seperate invocations of __init__
create lambdas referring to seperate variables!).
There are two popular ways to avoid this:
- Using a default parameter:
lambda i=i: self._number(i)
. This work because default parameters bind a value at function definition time. - Defining a helper function
helper = lambda i: (lambda: self._number(i))
and usehelper(i)
in the loop. This works because the "outer"i
is evaluated at the timei
is bound, and - as mentioned before - the next closure created in the next invokation ofhelper
will refer to a different variable.