You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Other modes: [Interactive Website](https://wtfpython-interactive.vercel.app) | [Interactive Notebook](https://colab.research.google.com/github/satwikkansal/wtfpython/blob/master/irrelevant/wtf.ipynb)
15
22
16
-
Python, being a beautifully designed high-level and interpreter-based programming language, provides us with many features for the programmer's comfort. But sometimes, the outcomes of a Python snippet may not seem obvious at first sight.
23
+
Python, being a beautifully designed high-level and interpreter-based programming language,
24
+
provides us with many features for the programmer's comfort.
25
+
But sometimes, the outcomes of a Python snippet may not seem obvious at first sight.
17
26
18
-
Here's a fun project attempting to explain what exactly is happening under the hood for some counter-intuitive snippets and lesser-known features in Python.
27
+
Here's a fun project attempting to explain what exactly is happening under the hood for some counter-intuitive snippets
28
+
and lesser-known features in Python.
19
29
20
-
While some of the examples you see below may not be WTFs in the truest sense, but they'll reveal some of the interesting parts of Python that you might be unaware of. I find it a nice way to learn the internals of a programming language, and I believe that you'll find it interesting too!
30
+
While some of the examples you see below may not be WTFs in the truest sense,
31
+
but they'll reveal some of the interesting parts of Python that you might be unaware of.
32
+
I find it a nice way to learn the internals of a programming language, and I believe that you'll find it interesting too!
21
33
22
-
If you're an experienced Python programmer, you can take it as a challenge to get most of them right in the first attempt. You may have already experienced some of them before, and I might be able to revive sweet old memories of yours! :sweat_smile:
34
+
If you're an experienced Python programmer, you can take it as a challenge to get most of them right in the first attempt
35
+
You may have already experienced some of them before, and I might be able to revive sweet old memories of yours! :sweat_smile:
23
36
24
-
PS: If you're a returning reader, you can learn about the new modifications [here](https://github.com/satwikkansal/wtfpython/releases/) (the examples marked with asterisk are the ones added in the latest major revision).
37
+
PS: If you're a returning reader, you can learn about the new modifications [here](https://github.com/satwikkansal/wtfpython/releases/)
38
+
(the examples marked with asterisk are the ones added in the latest major revision).
25
39
26
40
So, here we go...
27
41
@@ -116,6 +130,8 @@ So, here we go...
116
130
117
131
All the examples are structured like below:
118
132
133
+
> ## Section: (if necessary)
134
+
>
119
135
> ### ▶ Some fancy Title
120
136
>
121
137
> ```py
@@ -148,17 +164,20 @@ All the examples are structured like below:
148
164
># some justified output
149
165
>```
150
166
151
-
**Note:** All the examples are tested on Python 3.5.2 interactive interpreter, and they should work forall the Python versions unless explicitly specified before the output.
167
+
**Note:** All the examples are tested on Python 3.5.2 interactive interpreter,
168
+
and they should work forall the Python versions unless explicitly specified before the output.
152
169
153
170
# Usage
154
171
155
172
A nice way to get the most out of these examples, in my opinion, is to read them in sequential order, andfor every example:
156
173
157
-
- Carefully read the initial code for setting up the example. If you're an experienced Python programmer, you'll successfully anticipate what's going to happen next most of the time.
174
+
- Carefully read the initial code for setting up the example.
175
+
If you're an experienced Python programmer, you'll successfully anticipate what's going to happen next most of the time.
158
176
- Read the output snippets and,
159
177
- Check if the outputs are the same as you'd expect.
160
178
- Make sure if you know the exact reason behind the output being the way it is.
161
-
- If the answer is no (which is perfectly okay), take a deep breath, and read the explanation (andif you still don't understand, shout out! and create an issue [here](https://github.com/satwikkansal/wtfpython/issues/new)).
179
+
- If the answer is no (which is perfectly okay), take a deep breath, and read the explanation
180
+
(andif you still don't understand, shout out! and create an issue [here](https://github.com/satwikkansal/wtfpython/issues/new)).
162
181
- If yes, give a gentle pat on your back, and you may skip to the next example.
163
182
164
183
---
@@ -170,7 +189,6 @@ A nice way to get the most out of these examples, in my opinion, is to read them
170
189
### ▶ First things first! \*
171
190
172
191
<!-- Example ID: d3d73936-3cf1-4632-b5ab-817981338863-->
173
-
<!-- read-only -->
174
192
175
193
For some reason, the Python 3.8's "Walrus" operator (`:=`) has become quite popular. Let's check it out,
176
194
@@ -230,9 +248,8 @@ SyntaxError: invalid syntax
230
248
231
249
#### 💡 Explanation
232
250
233
-
**Quick walrus operator refresher**
234
-
235
-
The Walrus operator (`:=`) was introduced in Python 3.8, it can be useful in situations where you'd want to assign values to variables within an expression.
251
+
The Walrus operator (`:=`) was introduced in Python 3.8,
252
+
it can be useful in situations where you'd want to assign values to variables within an expression.
236
253
237
254
```py
238
255
def some_func():
@@ -264,12 +281,13 @@ if a := some_func():
264
281
265
282
This saved one line of code, and implicitly prevented invoking `some_func` twice.
266
283
267
-
- Unparenthesized "assignment expression" (use of walrus operator), is restricted at the top level, hence the `SyntaxError`in the `a :="wtf_walrus"` statement of the first snippet. Parenthesizing it worked as expected and assigned `a`.
268
-
269
-
- As usual, parenthesizing of an expression containing `=` operator isnot allowed. Hence the syntax error in`(a, b = 6, 9)`.
270
-
271
-
- The syntax of the Walrus operator is of the form `NAME:= expr`, where `NAME`is a valid identifier, and`expr`is a valid expression. Hence, iterable packing and unpacking are not supported which means,
272
-
284
+
- Unparenthesized "assignment expression" (use of walrus operator), is restricted at the top level,
285
+
hence the `SyntaxError`in the `a :="wtf_walrus"` statement of the first snippet.
286
+
Parenthesizing it worked as expected and assigned `a`.
287
+
- As usual, parenthesizing of an expression containing `=` operator isnot allowed.
288
+
Hence the syntax error in`(a, b = 6, 9)`.
289
+
- The syntax of the Walrus operator is of the form `NAME:= expr`, where `NAME`is a valid identifier,
290
+
and`expr`is a valid expression. Hence, iterable packing and unpacking are not supported which means,
273
291
-`(a :=6, 9)`is equivalent to `((a :=6), 9)`and ultimately `(a, 9)` (where `a`'s value is 6')
274
292
275
293
```py
@@ -319,7 +337,7 @@ a = "wtf!"; b = "wtf!"
319
337
assert a is b
320
338
```
321
339
322
-
4\. **Disclaimer - snippet is not relavant in modern Python versions**
340
+
4\. **Disclaimer - snippet is not relevant in modern Python versions**
323
341
324
342
**Output (< Python3.7 )**
325
343
@@ -334,12 +352,15 @@ Makes sense, right?
334
352
335
353
#### 💡 Explanation:
336
354
337
-
- The behavior in first and second snippets is due to a CPython optimization (called string interning) that tries to use existing immutable objects in some cases rather than creating a new object every time.
355
+
- The behavior in first and second snippets is due to a CPython optimization (called string interning)
356
+
that tries to use existing immutable objects in some cases rather than creating a new object every time.
338
357
- After being "interned," many variables may reference the same string objectin memory (saving memory thereby).
339
-
- In the snippets above, strings are implicitly interned. The decision of when to implicitly intern a string is implementation-dependent. There are some rules that can be used to guess if a string will be interned ornot:
358
+
- In the snippets above, strings are implicitly interned. The decision of when to implicitly intern a string is
359
+
implementation-dependent. There are some rules that can be used to guess if a string will be interned ornot:
340
360
- All length 0and length 1 strings are interned.
341
361
- Strings are interned at compile time (`'wtf'` will be interned but `''.join(['w', 't', 'f'])` will not be interned)
342
-
- Strings that are not composed of ASCII letters, digits or underscores, are not interned. This explains why `'wtf!'` was not interned due to `!`. CPython implementation of this rule can be found [here](https://github.com/python/cpython/blob/3.6/Objects/codeobject.c#L19)
362
+
- Strings that are not composed of ASCII letters, digits or underscores, are not interned.
363
+
This explains why `'wtf!'` was not interned due to `!`. CPython implementation of this rule can be found [here](https://github.com/python/cpython/blob/3.6/Objects/codeobject.c#L19)
343
364
344
365
<p align="center">
345
366
<picture>
@@ -349,10 +370,25 @@ Makes sense, right?
349
370
</picture>
350
371
</p>
351
372
352
-
- When `a`and`b` are set to `"wtf!"`in the same line, the Python interpreter creates a new object, then references the second variable at the same time. If you do it on separate lines, it doesn't "know" that there's already `"wtf!"`as an object (because `"wtf!"`isnot implicitly interned as per the facts mentioned above). It's a compile-time optimization. This optimization doesn't apply to 3.7.x versions of CPython (check this [issue](https://github.com/satwikkansal/wtfpython/issues/100) for more discussion).
353
-
- A compile unit in an interactive environment like IPython consists of a single statement, whereas it consists of the entire module in case of modules. `a, b = "wtf!", "wtf!"`is single statement, whereas `a = "wtf!"; b = "wtf!"` are two statements in a single line. This explains why the identities are different in`a = "wtf!"; b = "wtf!"`, and also explain why they are same when invoked in`some_file.py`
354
-
- The abrupt change in the output of the fourth snippet is due to a [peephole optimization](https://en.wikipedia.org/wiki/Peephole_optimization) technique known as Constant folding. This means the expression `'a'*20`is replaced by `'aaaaaaaaaaaaaaaaaaaa'` during compilation to save a few clock cycles during runtime. Constant folding only occurs for strings having a length of less than 21. (Why? Imagine the size of `.pyc`file generated as a result of the expression `'a'*10**10`). [Here's](https://github.com/python/cpython/blob/3.6/Python/peephole.c#L288) the implementation source for the same.
355
-
- Note: In Python 3.7, Constant folding was moved out from peephole optimizer to the new AST optimizer with some change in logic as well, so the fourth snippet doesn't work for Python 3.7. You can read more about the change [here](https://bugs.python.org/issue11549).
373
+
- When `a`and`b` are set to `"wtf!"`in the same line, the Python interpreter creates a new object,
374
+
then references the second variable at the same time. If you do it on separate lines, it doesn't "know" that
375
+
there's already `"wtf!"` as an object (because `"wtf!"` is not implicitly interned as per the facts mentioned above).
376
+
It's a compile-time optimization. This optimization doesn't apply to 3.7.x versions of CPython
377
+
(check this [issue](https://github.com/satwikkansal/wtfpython/issues/100) for more discussion).
378
+
- A compile unit in an interactive environment like IPython consists of a single statement,
379
+
whereas it consists of the entire module in case of modules. `a, b = "wtf!", "wtf!"`is single statement,
380
+
whereas `a = "wtf!"; b = "wtf!"` are two statements in a single line.
381
+
This explains why the identities are different in`a = "wtf!"; b = "wtf!"`,
382
+
and also explain why they are same when invoked in`some_file.py`
383
+
- The abrupt change in the output of the fourth snippet is due to a
384
+
[peephole optimization](https://en.wikipedia.org/wiki/Peephole_optimization) technique known as Constant folding.
385
+
This means the expression `'a'*20`is replaced by `'aaaaaaaaaaaaaaaaaaaa'` during compilation to save
386
+
a few clock cycles during runtime. Constant folding only occurs for strings having a length of less than 21.
387
+
(Why? Imagine the size of `.pyc`file generated as a result of the expression `'a'*10**10`).
388
+
[Here's](https://github.com/python/cpython/blob/3.6/Python/peephole.c#L288) the implementation source for the same.
389
+
- Note: In Python 3.7, Constant folding was moved out from peephole optimizer to the new AST optimizer
390
+
with some change in logic as well, so the fourth snippet doesn't work for Python 3.7.
391
+
You can read more about the change [here](https://bugs.python.org/issue11549).
356
392
357
393
---
358
394
@@ -385,19 +421,23 @@ False
385
421
386
422
As per https://docs.python.org/3/reference/expressions.html#comparisons
387
423
388
-
> Formally, if a, b, c, ..., y, z are expressions and op1, op2, ..., opN are comparison operators, then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and... y opN z, except that each expression is evaluated at most once.
424
+
> Formally, if a, b, c, ..., y, z are expressions and op1, op2, ..., opN are comparison operators,
425
+
then a op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and... y opN z,
426
+
except that each expression is evaluated at most once.
389
427
390
-
While such behavior might seem silly to you in the above examples, it's fantastic with stuff like `a == b == c` and `0 <= x <= 100`.
428
+
While such behavior might seem silly to you in the above examples,
429
+
it's fantastic with stuff like `a == b == c` and `0 <= x <= 100`.
391
430
392
431
-`FalseisFalseisFalse`is equivalent to `(FalseisFalse) and (FalseisFalse)`
393
-
-`TrueisFalse==False`is equivalent to `(TrueisFalse) and (False==False)`and since the first part of the statement (`TrueisFalse`) evaluates to `False`, the overall expression evaluates to `False`.
432
+
-`TrueisFalse==False`is equivalent to `(TrueisFalse) and (False==False)`
433
+
and since the first part of the statement (`TrueisFalse`) evaluates to `False`, the overall expression evaluates to `False`.
394
434
-`1>0<1`is equivalent to `(1>0) and (0<1)` which evaluates to `True`.
395
435
- The expression `(1>0) <1`is equivalent to `True<1`and
396
436
397
437
```py
398
438
>>>int(True)
399
439
1
400
-
>>>True+1#not relevant for this example, but just for fun
440
+
>>>True+1#not relevant for this example, but just for fun
401
441
2
402
442
```
403
443
@@ -1079,7 +1119,11 @@ The values of `x` were different in every iteration prior to appending `some_fun
1079
1119
1080
1120
#### 💡 Explanation:
1081
1121
1082
-
- 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:
1122
+
- When defining a function inside a loop that uses the loop variable in its body,
1123
+
the loop function's closure is bound to the _variable_, not its _value_.
1124
+
The function looks up `x`in the surrounding context, rather than using the value of `x` at the time
1125
+
the function is created. So all of the functions use the latest value assigned to the variable for computation.
1126
+
We can see that it's using the `x` from the surrounding context (i.e. _not_ a local variable) with:
1083
1127
1084
1128
```py
1085
1129
>>>import inspect
@@ -1313,10 +1357,10 @@ Accessing `classm` or `method` twice, creates equal but not _same_ objects for t
1313
1357
```
1314
1358
1315
1359
- Having to create new "method" objects every time Python calls instance methods and having to modify the arguments
1316
-
every time in order to insert `self` affected performance badly.
1317
-
CPython 3.7 [solved it](https://bugs.python.org/issue26110) by introducing new opcodes that deal with calling methods
1318
-
without creating the temporary method objects. This is used only when the accessed function is actually called, so the
1319
-
snippets here are not affected, and still generate methods :)
1360
+
every time in order to insert `self` affected performance badly.
1361
+
CPython 3.7 [solved it](https://bugs.python.org/issue26110) by introducing new opcodes that deal with calling methods
1362
+
without creating the temporary method objects. This is used only when the accessed function is actually called, so the
1363
+
snippets here are not affected, and still generate methods :)
0 commit comments