Skip to content

Commit 2bc1cd6

Browse files
committed
Update the sticy output example
Closes satwikkansal#245
1 parent c954b71 commit 2bc1cd6

File tree

2 files changed

+42
-24
lines changed

2 files changed

+42
-24
lines changed

CONTRIBUTORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Following are the wonderful people (in no specific order) who have contributed t
2323
| jab | [jab](https://github.com/jab) | [#77](https://github.com/satwikkansal/wtfpython/issues/77) |
2424
| Jongy | [Jongy](https://github.com/Jongy) | [#208](https://github.com/satwikkansal/wtfpython/issues/208), [#210](https://github.com/satwikkansal/wtfpython/issues/210), [#233](https://github.com/satwikkansal/wtfpython/issues/233) |
2525
| Diptangsu Goswami | [diptangsu](https://github.com/diptangsu) | [#193](https://github.com/satwikkansal/wtfpython/issues/193) |
26+
| Charles | [charles-l](https://github.com/charles-l) | [#245](https://github.com/satwikkansal/wtfpython/issues/245)
2627

2728
---
2829

README.md

+41-24
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ So, here we go...
4141
+ [▶ Evaluation time discrepancy](#-evaluation-time-discrepancy)
4242
+ [`is not ...` is not `is (not ...)`](#-is-not--is-not-is-not-)
4343
+ [▶ A tic-tac-toe where X wins in the first attempt!](#-a-tic-tac-toe-where-x-wins-in-the-first-attempt)
44-
+ [The sticky output function](#-the-sticky-output-function)
44+
+ [Schrödinger's variable](#-schrödingers-variable-)
4545
+ [▶ The chicken-egg problem *](#-the-chicken-egg-problem-)
4646
+ [▶ Subclass relationships](#-subclass-relationships)
4747
+ [▶ Methods equality and identity](#-methods-equality-and-identity)
@@ -989,10 +989,9 @@ We can avoid this scenario here by not using `row` variable to generate `board`.
989989
990990
---
991991
992-
### ▶ The sticky output function
992+
### ▶ Schrödinger's variable *
993993
<!-- Example ID: 4dc42f77-94cb-4eb5-a120-8203d3ed7604 --->
994994
995-
1\.
996995
997996
```py
998997
funcs = []
@@ -1006,45 +1005,63 @@ for x in range(7):
10061005
funcs_results = [func() for func in funcs]
10071006
```
10081007
1009-
**Output:**
1010-
1008+
**Output (Python version):**
10111009
```py
10121010
>>> results
10131011
[0, 1, 2, 3, 4, 5, 6]
10141012
>>> funcs_results
10151013
[6, 6, 6, 6, 6, 6, 6]
10161014
```
1017-
Even when the values of `x` were different in every iteration prior to appending `some_func` to `funcs`, all the functions return 6.
10181015
1019-
2\.
1016+
The values of `x` were different in every iteration prior to appending `some_func` to `funcs`, but all the functions return 6 when they're evaluated after the loop completes.
1017+
1018+
2.
10201019
10211020
```py
10221021
>>> powers_of_x = [lambda x: x**i for i in range(10)]
10231022
>>> [f(2) for f in powers_of_x]
10241023
[512, 512, 512, 512, 512, 512, 512, 512, 512, 512]
10251024
```
10261025
1027-
#### 💡 Explanation
1026+
#### 💡 Explanation:
1027+
* When defining a function inside a loop that uses the loop variable in its body, the loop function's closure is bound to the *variable*, not its *value*. The function looks up `x` in the surrounding context, rather than using the value of `x` at the time the function is created. So all of the functions use the latest value assigned to the variable for computation. We can see that it's using the `x` from the surrounding context (i.e. *not* a local variable) with:
1028+
```py
1029+
>>> import inspect
1030+
>>> inspect.getclosurevals(funcs[0])
1031+
ClosureVars(nonlocals={}, globals={'x': 6}, builtins={}, unbound=set())
1032+
```
1033+
Since `x` is a global value, we can change the value that the `funcs` will lookup and return by updating `x`:
10281034
1029-
- When defining a function inside a loop that uses the loop variable in its body, the loop function's closure is bound to the variable, not its value. So all of the functions use the latest value assigned to the variable for computation.
1035+
```py
1036+
>>> x = 42
1037+
>>> [func() for func in funcs]
1038+
[42, 42, 42, 42, 42, 42, 42]
1039+
```
10301040
1031-
- To get the desired behavior you can pass in the loop variable as a named variable to the function. **Why does this work?** Because this will define the variable
1032-
within the function's scope.
1041+
* To get the desired behavior you can pass in the loop variable as a named variable to the function. **Why does this work?** Because this will define the variable *inside* the function's scope. It will no longer go to the surrounding (global) scope to look up the variables value but will create a local variable that stores the value of `x` at that point in time.
10331042
1034-
```py
1035-
funcs = []
1036-
for x in range(7):
1037-
def some_func(x=x):
1038-
return x
1039-
funcs.append(some_func)
1040-
```
1043+
```py
1044+
funcs = []
1045+
for x in range(7):
1046+
def some_func(x=x):
1047+
return x
1048+
funcs.append(some_func)
1049+
```
10411050
1042-
**Output:**
1043-
```py
1044-
>>> funcs_results = [func() for func in funcs]
1045-
>>> funcs_results
1046-
[0, 1, 2, 3, 4, 5, 6]
1047-
```
1051+
**Output:**
1052+
1053+
```py
1054+
>>> funcs_results = [func() for func in funcs]
1055+
>>> funcs_results
1056+
[0, 1, 2, 3, 4, 5, 6]
1057+
```
1058+
1059+
It is not longer using the `x` in the global scope:
1060+
1061+
```py
1062+
>>> inspect.getclosurevars(funcs[0])
1063+
ClosureVars(nonlocals={}, globals={}, builtins={}, unbound=set())
1064+
```
10481065
10491066
---
10501067

0 commit comments

Comments
 (0)