This commit is contained in:
חגי ישראל וילד 2023-08-27 20:44:01 +03:00 committed by GitHub
parent ccf5be1a6f
commit 427cd11c69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 142 additions and 16953 deletions

6
.gitignore vendored
View File

@ -2,9 +2,9 @@
node_modules
npm-debug.log
wtfpython-pypi/build/
wtfpython-pypi/dist/
wtfpython-pypi/wtfpython.egg-info
hasem-ishmorpython-pypi/build/
hasem-ishmorpython-pypi/dist/
hasem-ishmorpython-pypi/hasem-ishmorpython.egg-info
# Python-specific byte-compiled files should be ignored
__pycache__/

63
CONTRIBUTING.md vendored
View File

@ -1,63 +0,0 @@
All kinds of patches are welcome. Feel free to even suggest some catchy and funny titles for the existing Examples. The goal is to make this collection as interesting to read as possible. Here are a few ways through which you can contribute,
- If you are interested in translating the project to another language (some people have done that in the past), please feel free to open up an issue, and let me know if you need any kind of help.
- If the changes you suggest are significant, filing an issue before submitting the actual patch will be appreciated. If you'd like to work on the issue (highly encouraged), you can mention that you're interested in working on it while creating the issue and get assigned to it.
- If you're adding a new example, it is highly recommended to create an issue to discuss it before submitting a patch. You can use the following template for adding a new example:
<pre>
### ▶ Some fancy Title *
The asterisk at the end of the title indicates the example was not present in the first release and has been recently added.
```py
# Setting up the code.
# Preparation for the magic...
```
**Output (Python version):**
```py
>>> triggering_statement
Probably unexpected output
```
(Optional): One line describing the unexpected output.
#### 💡 Explanation:
* Brief explanation of what's happening and why is it happening.
```py
Setting up examples for clarification (if necessary)
```
**Output:**
```py
>>> trigger # some example that makes it easy to unveil the magic
# some justified output
```
```
</pre>
Few things that you can consider while writing an example,
- If you are choosing to submit a new example without creating an issue and discussing, please check the project to make sure there aren't similar examples already.
- Try to be consistent with the namings and the values you use with the variables. For instance, most variable names in the project are along the lines of `some_string`, `some_list`, `some_dict`, etc. You'd see a lot of `x`s for single letter variable names, and `"wtf"` as values for strings. There's no strictly enforced scheme in the project as such, but you can take a glance at other examples to get a gist.
- Try to be as creative as possible to add that element of "surprise" in the setting up part of an example. Sometimes this may mean writing a snippet a sane programmer would never write.
- Also, feel free to add your name to the [contributors list](/CONTRIBUTORS.md).
**Some FAQs**
What is is this after every snippet title (###) in the README: <!-- Example ID: 30f1d3fc-e267-4b30-84ef-4d9e7091ac1a --->? Should it be added manually or can it be ignored when creating new snippets?
That's a random UUID, it is used to keep identify the examples across multiple translations of the project. As a contributor, you don't have to worry about behind the scenes of how it is used, you just have to add a new random UUID to new examples in that format.
Where should new snippets be added? Top/bottom of the section, doesn't ?
There are multiple things that are considered to decide the order (the dependency on the other examples, difficulty level, category, etc). I'd suggest simply adding the new example at the bottom of a section you find more fitting (or just add it to the Miscellaneous section). Its order will be taken care of in future revisions.
What's the difference between the sections (the first two feel very similar)?
The section "Strain your brain" contains more contrived examples that you may not really encounter in real life, whereas the section "Slippery Slopes" contains examples that have the potential to be encountered more frequently while programming.
Before the table of contents it says that markdown-toc -i README.md --maxdepth 3 was used to create it. The pip package markdown-toc does not contain either -i or --maxdepth flags. Which package is meant, or what version of that package?
Should the new table of contents entry for the snippet be created with the above command or created manually (in case the above command does more than only add the entry)?
We use the [markdown-toc](https://www.npmjs.com/package/markdown-toc) npm package to generate ToC. It has some issues with special characters though (I'm not sure if it's fixed yet). More often than not, I just end up inserting the toc link manually at the right place. The tool is handy when I have to big reordering, otherwise just updating toc manually is more convenient imo.
If you have any questions feel free to ask on [this issue](https://github.com/satwikkansal/wtfpython/issues/269) (thanks to [@LiquidFun](https://github.com/LiquidFun) for starting it).

42
CONTRIBUTORS.md vendored
View File

@ -1,42 +0,0 @@
Following are the wonderful people (in no specific order) who have contributed their examples to wtfpython.
| Contributor | Github | Issues |
|-------------|--------|--------|
| Lucas-C | [Lucas-C](https://github.com/Lucas-C) | [#36](https://github.com/satwikkansal/wtfpython/issues/36) |
| MittalAshok | [MittalAshok](https://github.com/MittalAshok) | [#23](https://github.com/satwikkansal/wtfpython/issues/23) |
| asottile | [asottile](https://github.com/asottile) | [#40](https://github.com/satwikkansal/wtfpython/issues/40) |
| MostAwesomeDude | [MostAwesomeDude](https://github.com/MostAwesomeDude) | [#1](https://github.com/satwikkansal/wtfpython/issues/1) |
| tukkek | [tukkek](https://github.com/tukkek) | [#11](https://github.com/satwikkansal/wtfpython/issues/11), [#26](https://github.com/satwikkansal/wtfpython/issues/26) |
| PiaFraus | [PiaFraus](https://github.com/PiaFraus) | [#9](https://github.com/satwikkansal/wtfpython/issues/9) |
| chris-rands | [chris-rands](https://github.com/chris-rands) | [#32](https://github.com/satwikkansal/wtfpython/issues/32) |
| sohaibfarooqi | [sohaibfarooqi](https://github.com/sohaibfarooqi) | [#63](https://github.com/satwikkansal/wtfpython/issues/63) |
| ipid | [ipid](https://github.com/ipid) | [#145](https://github.com/satwikkansal/wtfpython/issues/145) |
| roshnet | [roshnet](https://github.com/roshnet) | [#140](https://github.com/satwikkansal/wtfpython/issues/140) |
| daidai21 | [daidai21](https://github.com/daidai21) | [#137](https://github.com/satwikkansal/wtfpython/issues/137) |
| scidam | [scidam](https://github.com/scidam) | [#136](https://github.com/satwikkansal/wtfpython/issues/136) |
| pmjpawelec | [pmjpawelec](https://github.com/pmjpawelec) | [#121](https://github.com/satwikkansal/wtfpython/issues/121) |
| leisurelicht | [leisurelicht](https://github.com/leisurelicht) | [#112](https://github.com/satwikkansal/wtfpython/issues/112) |
| mishaturnbull | [mishaturnbull](https://github.com/mishaturnbull) | [#108](https://github.com/satwikkansal/wtfpython/issues/108) |
| MuseBoy | [MuseBoy](https://github.com/MuseBoy) | [#101](https://github.com/satwikkansal/wtfpython/issues/101) |
| Ghost account | N/A | [#96](https://github.com/satwikkansal/wtfpython/issues/96)
| koddo | [koddo](https://github.com/koddo) | [#80](https://github.com/satwikkansal/wtfpython/issues/80), [#73](https://github.com/satwikkansal/wtfpython/issues/73) |
| jab | [jab](https://github.com/jab) | [#77](https://github.com/satwikkansal/wtfpython/issues/77) |
| 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) |
| Diptangsu Goswami | [diptangsu](https://github.com/diptangsu) | [#193](https://github.com/satwikkansal/wtfpython/issues/193) |
| Charles | [charles-l](https://github.com/charles-l) | [#245](https://github.com/satwikkansal/wtfpython/issues/245)
| LiquidFun | [LiquidFun](https://github.com/LiquidFun) | [#267](https://github.com/satwikkansal/wtfpython/issues/267)
---
**Translations**
| Translator | Github | Language |
|-------------|--------|--------|
| leisurelicht | [leisurelicht](https://github.com/leisurelicht) | [Chinese](https://github.com/leisurelicht/wtfpython-cn) |
| vuduclyunitn | [vuduclyunitn](https://github.com/vuduclyunitn) | [Vietnamese](https://github.com/vuduclyunitn/wtfptyhon-vi) |
| José De Freitas | [JoseDeFreitas](https://github.com/JoseDeFreitas) | [Spanish](https://github.com/JoseDeFreitas/wtfpython-es) |
Thank you all for your time and making wtfpython more awesome! :smile:
PS: This list is updated after every major release, if I forgot to add your contribution here, please feel free to raise a Pull request.

158
README.md vendored
View File

@ -1,21 +1,15 @@
<p align="center"><img src="/images/logo.png" alt=""></p>
<h1 align="center">What the f*ck Python! 😱</h1>
<h1 align="center">What the hasem-ishmor Python! 😱</h1>
<p align="center">Exploring and understanding Python through surprising snippets.</p>
Translations: [Chinese 中文](https://github.com/robertparley/wtfpython-cn) | [Vietnamese Tiếng Việt](https://github.com/vuduclyunitn/wtfptyhon-vi) | [Spanish Español](https://web.archive.org/web/20220511161045/https://github.com/JoseDeFreitas/wtfpython-es) | [Korean 한국어](https://github.com/buttercrab/wtfpython-ko) | [Russian Русский](https://github.com/frontdevops/wtfpython) | [Add translation](https://github.com/satwikkansal/wtfpython/issues/new?title=Add%20translation%20for%20[LANGUAGE]&body=Expected%20time%20to%20finish:%20[X]%20weeks.%20I%27ll%20start%20working%20on%20it%20from%20[Y].)
Other modes: [Interactive Website](https://wtfpython-interactive.vercel.app) | [Interactive Notebook](https://colab.research.google.com/github/satwikkansal/wtfpython/blob/master/irrelevant/wtf.ipynb) | [CLI](https://pypi.python.org/pypi/wtfpython)
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.
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.
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!
While some of the examples you see below may not be hasem-ishmors 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!
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:
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).
So, here we go...
# Table of Contents
@ -148,13 +142,13 @@ A nice way to get the most out of these examples, in my opinion, is to read them
- Read the output snippets and,
+ Check if the outputs are the same as you'd expect.
+ Make sure if you know the exact reason behind the output being the way it is.
- If the answer is no (which is perfectly okay), take a deep breath, and read the explanation (and if you still don't understand, shout out! and create an issue [here](https://github.com/satwikkansal/wtfpython/issues/new)).
- If the answer is no (which is perfectly okay), take a deep breath, and read the explanation (and if you still don't understand, shout out! and create an issue [here](https://github.com/satwikkansal/hasem-ishmorpython/issues/new)).
- If yes, give a gentle pat on your back, and you may skip to the next example.
PS: You can also read WTFPython at the command line using the [pypi package](https://pypi.python.org/pypi/wtfpython),
PS: You can also read hasem-ishmorPython at the command line using the [pypi package](https://pypi.python.org/pypi/hasem-ishmorpython),
```sh
$ pip install wtfpython -U
$ wtfpython
$ pip install hasem-ishmorpython -U
$ hasem-ishmorpython
```
---
@ -174,20 +168,20 @@ For some reason, the Python 3.8's "Walrus" operator (`:=`) has become quite popu
```py
# Python version 3.8+
>>> a = "wtf_walrus"
>>> a = "hasem-ishmor_walrus"
>>> a
'wtf_walrus'
'hasem-ishmor_walrus'
>>> a := "wtf_walrus"
>>> a := "hasem-ishmor_walrus"
File "<stdin>", line 1
a := "wtf_walrus"
a := "hasem-ishmor_walrus"
^
SyntaxError: invalid syntax
>>> (a := "wtf_walrus") # This works though
'wtf_walrus'
>>> (a := "hasem-ishmor_walrus") # This works though
'hasem-ishmor_walrus'
>>> a
'wtf_walrus'
'hasem-ishmor_walrus'
```
2 \.
@ -261,7 +255,7 @@ if a := some_func():
This saved one line of code, and implicitly prevented invoking `some_func` twice.
- 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`.
- Unparenthesized "assignment expression" (use of walrus operator), is restricted at the top level, hence the `SyntaxError` in the `a := "hasem-ishmor_walrus"` statement of the first snippet. Parenthesizing it worked as expected and assigned `a`.
- As usual, parenthesizing of an expression containing `=` operator is not allowed. Hence the syntax error in `(a, b = 6, 9)`.
@ -298,13 +292,13 @@ This saved one line of code, and implicitly prevented invoking `some_func` twice
2\.
```py
>>> a = "wtf"
>>> b = "wtf"
>>> a = "hasem-ishmor"
>>> b = "hasem-ishmor"
>>> a is b
True
>>> a = "wtf!"
>>> b = "wtf!"
>>> a = "hasem-ishmor!"
>>> b = "hasem-ishmor!"
>>> a is b
False
@ -313,19 +307,19 @@ False
3\.
```py
>>> a, b = "wtf!", "wtf!"
>>> a, b = "hasem-ishmor!", "hasem-ishmor!"
>>> a is b # All versions except 3.7.x
True
>>> a = "wtf!"; b = "wtf!"
>>> a = "hasem-ishmor!"; b = "hasem-ishmor!"
>>> a is b # This will print True or False depending on where you're invoking it (python shell / ipython / as a script)
False
```
```py
# This time in file some_file.py
a = "wtf!"
b = "wtf!"
a = "hasem-ishmor!"
b = "hasem-ishmor!"
print(a is b)
# prints True when the module is invoked!
@ -349,11 +343,11 @@ Makes sense, right?
+ After being "interned," many variables may reference the same string object in memory (saving memory thereby).
+ 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 or not:
* All length 0 and length 1 strings are interned.
* Strings are interned at compile time (`'wtf'` will be interned but `''.join(['w', 't', 'f'])` will not be interned)
* 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)
* Strings are interned at compile time (`'hasem-ishmor'` will be interned but `''.join(['w', 't', 'f'])` will not be interned)
* Strings that are not composed of ASCII letters, digits or underscores, are not interned. This explains why `'hasem-ishmor!'` 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)
![image](/images/string-intern/string_intern.png)
+ 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!"` is not 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).
+ 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`
+ When `a` and `b` are set to `"hasem-ishmor!"` 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 `"hasem-ishmor!"` as an object (because `"hasem-ishmor!"` is not 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/hasem-ishmorpython/issues/100) for more discussion).
+ 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 = "hasem-ishmor!", "hasem-ishmor!"` is single statement, whereas `a = "hasem-ishmor!"; b = "hasem-ishmor!"` are two statements in a single line. This explains why the identities are different in `a = "hasem-ishmor!"; b = "hasem-ishmor!"`, and also explain why they are same when invoked in `some_file.py`
+ 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.
+ 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).
@ -525,7 +519,7 @@ Similar optimization applies to other **immutable** objects like empty tuples as
True
```
* Why didn't this work for Python 3.7? The abstract reason is because such compiler optimizations are implementation specific (i.e. may change with version, OS, etc). I'm still figuring out what exact implementation change cause the issue, you can check out this [issue](https://github.com/satwikkansal/wtfpython/issues/100) for updates.
* Why didn't this work for Python 3.7? The abstract reason is because such compiler optimizations are implementation specific (i.e. may change with version, OS, etc). I'm still figuring out what exact implementation change cause the issue, you can check out this [issue](https://github.com/satwikkansal/hasem-ishmorpython/issues/100) for updates.
---
@ -599,43 +593,43 @@ So, why is Python all over the place?
### ▶ Deep down, we're all the same.
<!-- Example ID: 8f99a35f-1736-43e2-920d-3b78ec35da9b --->
```py
class WTF:
class hasem-ishmor:
pass
```
**Output:**
```py
>>> WTF() == WTF() # two different instances can't be equal
>>> hasem-ishmor() == hasem-ishmor() # two different instances can't be equal
False
>>> WTF() is WTF() # identities are also different
>>> hasem-ishmor() is hasem-ishmor() # identities are also different
False
>>> hash(WTF()) == hash(WTF()) # hashes _should_ be different as well
>>> hash(hasem-ishmor()) == hash(hasem-ishmor()) # hashes _should_ be different as well
True
>>> id(WTF()) == id(WTF())
>>> id(hasem-ishmor()) == id(hasem-ishmor())
True
```
#### 💡 Explanation:
* When `id` was called, Python created a `WTF` class object and passed it to the `id` function. The `id` function takes its `id` (its memory location), and throws away the object. The object is destroyed.
* When `id` was called, Python created a `hasem-ishmor` class object and passed it to the `id` function. The `id` function takes its `id` (its memory location), and throws away the object. The object is destroyed.
* When we do this twice in succession, Python allocates the same memory location to this second object as well. Since (in CPython) `id` uses the memory location as the object id, the id of the two objects is the same.
* So, the object's id is unique only for the lifetime of the object. After the object is destroyed, or before it is created, something else can have the same id.
* But why did the `is` operator evaluate to `False`? Let's see with this snippet.
```py
class WTF(object):
class hasem-ishmor(object):
def __init__(self): print("I")
def __del__(self): print("D")
```
**Output:**
```py
>>> WTF() is WTF()
>>> hasem-ishmor() is hasem-ishmor()
I
I
D
D
False
>>> id(WTF()) == id(WTF())
>>> id(hasem-ishmor()) == id(hasem-ishmor())
I
D
I
@ -808,7 +802,7 @@ Iteration 0
### ▶ For what?
<!-- Example ID: 64a9dccf-5083-4bc9-98aa-8aeecde4f210 --->
```py
some_string = "wtf"
some_string = "hasem-ishmor"
some_dict = {}
for i, some_dict[i] in enumerate(some_string):
i = 10
@ -980,7 +974,7 @@ And when the `board` is initialized by multiplying the `row`, this is what happe
![image](/images/tic-tac-toe/after_board_initialized.png)
We can avoid this scenario here by not using `row` variable to generate `board`. (Asked in [this](https://github.com/satwikkansal/wtfpython/issues/68) issue).
We can avoid this scenario here by not using `row` variable to generate `board`. (Asked in [this](https://github.com/satwikkansal/hasem-ishmorpython/issues/68) issue).
```py
>>> board = [['']*3 for _ in range(3)]
@ -1386,15 +1380,15 @@ SyntaxError: invalid syntax
<!-- Example ID: c55da3e2-1034-43b9-abeb-a7a970a2ad9e --->
**Output:**
```py
>>> print('wtfpython''')
wtfpython
>>> print("wtfpython""")
wtfpython
>>> print('hasem-ishmorpython''')
hasem-ishmorpython
>>> print("hasem-ishmorpython""")
hasem-ishmorpython
>>> # The following statements raise `SyntaxError`
>>> # print('''wtfpython')
>>> # print("""wtfpython")
>>> # print('''hasem-ishmorpython')
>>> # print("""hasem-ishmorpython")
File "<input>", line 3
print("""wtfpython")
print("""hasem-ishmorpython")
^
SyntaxError: EOF while scanning triple-quoted string literal
```
@ -1402,10 +1396,10 @@ SyntaxError: EOF while scanning triple-quoted string literal
#### 💡 Explanation:
+ Python supports implicit [string literal concatenation](https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation), Example,
```
>>> print("wtf" "python")
wtfpython
>>> print("wtf" "") # or "wtf"""
wtf
>>> print("hasem-ishmor" "python")
hasem-ishmorpython
>>> print("hasem-ishmor" "") # or "hasem-ishmor"""
hasem-ishmor
```
+ `'''` and `"""` are also string delimiters in Python which causes a SyntaxError because the Python interpreter was expecting a terminating triple quote as delimiter while scanning the currently encountered triple quoted string literal.
@ -1441,10 +1435,10 @@ for item in mixed_list:
2\.
```py
>>> some_bool = True
>>> "wtf" * some_bool
'wtf'
>>> "hasem-ishmor" * some_bool
'hasem-ishmor'
>>> some_bool = False
>>> "wtf" * some_bool
>>> "hasem-ishmor" * some_bool
''
```
@ -1611,7 +1605,7 @@ def some_func(val):
```py
def some_func(x):
if x == 3:
return ["wtf"]
return ["hasem-ishmor"]
else:
yield from range(x)
```
@ -1623,14 +1617,14 @@ def some_func(x):
[]
```
Where did the `"wtf"` go? Is it due to some special effect of `yield from`? Let's validate that,
Where did the `"hasem-ishmor"` go? Is it due to some special effect of `yield from`? Let's validate that,
2\.
```py
def some_func(x):
if x == 3:
return ["wtf"]
return ["hasem-ishmor"]
else:
for i in range(x):
yield i
@ -1653,7 +1647,7 @@ The same result, this didn't work either.
+ In the case of `some_func(3)`, `StopIteration` is raised at the beginning because of `return` statement. The `StopIteration` exception is automatically caught inside the `list(...)` wrapper and the `for` loop. Therefore, the above two snippets result in an empty list.
+ To get `["wtf"]` from the generator `some_func` we need to catch the `StopIteration` exception,
+ To get `["hasem-ishmor"]` from the generator `some_func` we need to catch the `StopIteration` exception,
```py
try:
@ -1664,7 +1658,7 @@ The same result, this didn't work either.
```py
>>> some_string
["wtf"]
["hasem-ishmor"]
```
---
@ -2048,7 +2042,7 @@ Yes, it runs for exactly **eight** times and stops.
* Iteration over a dictionary that you edit at the same time is not supported.
* It runs eight times because that's the point at which the dictionary resizes to hold more keys (we have eight deletion entries, so a resize is needed). This is actually an implementation detail.
* How deleted keys are handled and when the resize occurs might be different for different Python implementations.
* So for Python versions other than Python 2.7 - Python 3.5, the count might be different from 8 (but whatever the count is, it's going to be the same every time you run it). You can find some discussion around this [here](https://github.com/satwikkansal/wtfpython/issues/53) or in [this](https://stackoverflow.com/questions/44763802/bug-in-python-dict) StackOverflow thread.
* So for Python versions other than Python 2.7 - Python 3.5, the count might be different from 8 (but whatever the count is, it's going to be the same every time you run it). You can find some discussion around this [here](https://github.com/satwikkansal/hasem-ishmorpython/issues/53) or in [this](https://stackoverflow.com/questions/44763802/bug-in-python-dict) StackOverflow thread.
* Python 3.7.6 onwards, you'll see `RuntimeError: dictionary keys changed during iteration` exception if you try to do this.
---
@ -3066,7 +3060,7 @@ There we go.
> Recognized that the != inequality operator in Python 3.0 was a horrible, finger-pain inducing mistake, the FLUFL reinstates the <> diamond operator as the sole spelling.
- There were more things that Uncle Barry had to share in the PEP; you can read them [here](https://www.python.org/dev/peps/pep-0401/).
- It works well in an interactive environment, but it will raise a `SyntaxError` when you run via python file (see this [issue](https://github.com/satwikkansal/wtfpython/issues/94)). However, you can wrap the statement inside an `eval` or `compile` to get it working,
- It works well in an interactive environment, but it will raise a `SyntaxError` when you run via python file (see this [issue](https://github.com/satwikkansal/hasem-ishmorpython/issues/94)). However, you can wrap the statement inside an `eval` or `compile` to get it working,
```py
from __future__ import barry_as_FLUFL
print(eval('"Ruby" <> "Python"'))
@ -3720,9 +3714,9 @@ What makes those dictionaries become bloated? And why are newly created objects
* From Python 3.8 onwards you can use a typical f-string syntax like `f'{some_var=}` for quick debugging. Example,
```py
>>> some_string = "wtfpython"
>>> some_string = "hasem-ishmorpython"
>>> f'{some_string=}'
"some_string='wtfpython'"
"some_string='hasem-ishmorpython'"
```
* Python uses 2 bytes for local variable storage in functions. In theory, this means that only 65536 variables can be defined in a function. However, python has a handy solution built in that can be used to store more than 2^16 variable names. The following code demonstrates what happens in the stack when more than 65536 local variables are defined (Warning: This code prints around 2^18 lines of text, so be prepared!):
@ -3747,11 +3741,11 @@ What makes those dictionaries become bloated? And why are newly created objects
# File some_file.py
import time
print("wtfpython", end="_")
print("hasem-ishmorpython", end="_")
time.sleep(3)
```
This will print the `wtfpython` after 3 seconds due to the `end` argument because the output buffer is flushed either after encountering `\n` or when the program finishes execution. We can force the buffer to flush by passing `flush=True` argument.
This will print the `hasem-ishmorpython` after 3 seconds due to the `end` argument because the output buffer is flushed either after encountering `\n` or when the program finishes execution. We can force the buffer to flush by passing `flush=True` argument.
* List slicing with out of the bounds indices throws no errors
```py
@ -3762,7 +3756,7 @@ What makes those dictionaries become bloated? And why are newly created objects
* Slicing an iterable not always creates a new object. For example,
```py
>>> some_str = "wtfpython"
>>> some_str = "hasem-ishmorpython"
>>> some_list = ['w', 't', 'f', 'p', 'y', 't', 'h', 'o', 'n']
>>> some_list is some_list[:] # False expected because a new object is created.
False
@ -3798,52 +3792,52 @@ What makes those dictionaries become bloated? And why are newly created objects
# Contributing
A few ways in which you can contribute to wtfpython,
A few ways in which you can contribute to hasem-ishmorpython,
- Suggesting new examples
- Helping with translation (See [issues labeled translation](https://github.com/satwikkansal/wtfpython/issues?q=is%3Aissue+is%3Aopen+label%3Atranslation))
- Helping with translation (See [issues labeled translation](https://github.com/satwikkansal/hasem-ishmorpython/issues?q=is%3Aissue+is%3Aopen+label%3Atranslation))
- Minor corrections like pointing out outdated snippets, typos, formatting errors, etc.
- Identifying gaps (things like inadequate explanation, redundant examples, etc.)
- Any creative suggestions to make this project more fun and useful
Please see [CONTRIBUTING.md](/CONTRIBUTING.md) for more details. Feel free to create a new [issue](https://github.com/satwikkansal/wtfpython/issues/new) to discuss things.
Please see [CONTRIBUTING.md](/CONTRIBUTING.md) for more details. Feel free to create a new [issue](https://github.com/satwikkansal/hasem-ishmorpython/issues/new) to discuss things.
PS: Please don't reach out with backlinking requests, no links will be added unless they're highly relevant to the project.
# Acknowledgements
The idea and design for this collection were initially inspired by Denys Dovhan's awesome project [wtfjs](https://github.com/denysdovhan/wtfjs). The overwhelming support by Pythonistas gave it the shape it is in right now.
The idea and design for this collection were initially inspired by Denys Dovhan's awesome project [hasem-ishmorjs](https://github.com/denysdovhan/hasem-ishmorjs). The overwhelming support by Pythonistas gave it the shape it is in right now.
#### Some nice Links!
* https://www.youtube.com/watch?v=sH4XF6pKKmk
* https://www.reddit.com/r/Python/comments/3cu6ej/what_are_some_wtf_things_about_python
* https://www.reddit.com/r/Python/comments/3cu6ej/what_are_some_hasem-ishmor_things_about_python
* https://sopython.com/wiki/Common_Gotchas_In_Python
* https://stackoverflow.com/questions/530530/python-2-x-gotchas-and-landmines
* https://stackoverflow.com/questions/1011431/common-pitfalls-in-python
* https://www.python.org/doc/humor/
* https://github.com/cosmologicon/pywat#the-undocumented-converse-implication-operator
* https://www.codementor.io/satwikkansal/python-practices-for-efficient-code-performance-memory-and-usability-aze6oiq65
* https://github.com/wemake-services/wemake-python-styleguide/search?q=wtfpython&type=Issues
* https://github.com/wemake-services/wemake-python-styleguide/search?q=hasem-ishmorpython&type=Issues
* WFTPython discussion threads on [Hacker News](https://news.ycombinator.com/item?id=21862073) and [Reddit](https://www.reddit.com/r/programming/comments/edsh3q/what_the_fck_python_30_exploring_and/).
# 🎓 License
[![WTFPL 2.0][license-image]][license-url]
[![hasem-ishmorPL 2.0][license-image]][license-url]
&copy; [Satwik Kansal](https://satwikkansal.xyz)
[license-url]: http://www.wtfpl.net
[license-image]: https://img.shields.io/badge/License-WTFPL%202.0-lightgrey.svg?style=flat-square
[license-url]: http://www.hasem-ishmorpl.net
[license-image]: https://img.shields.io/badge/License-hasem-ishmorPL%202.0-lightgrey.svg?style=flat-square
## Surprise your friends as well!
If you like wtfpython, you can use these quick links to share it with your friends,
If you like hasem-ishmorpython, you can use these quick links to share it with your friends,
[Twitter](https://twitter.com/intent/tweet?url=https://github.com/satwikkansal/wtfpython&text=If%20you%20really%20think%20you%20know%20Python,%20think%20once%20more!%20Check%20out%20wtfpython&hashtags=python,wtfpython) | [Linkedin](https://www.linkedin.com/shareArticle?url=https://github.com/satwikkansal&title=What%20the%20f*ck%20Python!&summary=If%20you%20really%20thing%20you%20know%20Python,%20think%20once%20more!) | [Facebook](https://www.facebook.com/dialog/share?app_id=536779657179021&display=page&href=https%3A%2F%2Fgithub.com%2Fsatwikkansal%2Fwtfpython&quote=If%20you%20really%20think%20you%20know%20Python%2C%20think%20once%20more!)
[Twitter](https://twitter.com/intent/tweet?url=https://github.com/satwikkansal/hasem-ishmorpython&text=If%20you%20really%20think%20you%20know%20Python,%20think%20once%20more!%20Check%20out%20hasem-ishmorpython&hashtags=python,hasem-ishmorpython) | [Linkedin](https://www.linkedin.com/shareArticle?url=https://github.com/satwikkansal&title=What%20the%20hasem-ishmor%20Python!&summary=If%20you%20really%20thing%20you%20know%20Python,%20think%20once%20more!) | [Facebook](https://www.facebook.com/dialog/share?app_id=536779657179021&display=page&href=https%3A%2F%2Fgithub.com%2Fsatwikkansal%2Fhasem-ishmorpython&quote=If%20you%20really%20think%20you%20know%20Python%2C%20think%20once%20more!)
## Need a pdf version?
I've received a few requests for the pdf (and epub) version of wtfpython. You can add your details [here](https://form.jotform.com/221593245656057) to get them as soon as they are finished.
I've received a few requests for the pdf (and epub) version of hasem-ishmorpython. You can add your details [here](https://form.jotform.com/221593245656057) to get them as soon as they are finished.
**That's all folks!** For upcoming content like this, you can add your email [here](https://form.jotform.com/221593598380062).

74
code-of-conduct.md vendored
View File

@ -1,74 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance, race,
religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org

View File

@ -1,20 +1,20 @@
<p align="center"><img src="/images/logo.png" alt=""></p>
<h1 align="center">What the f*ck Python! 🐍</h1>
<h1 align="center">What the hasem-ishmor Python! 🐍</h1>
<p align="center">An interesting collection of surprising snippets and lesser-known Python features.</p>
[![WTFPL 2.0][license-image]][license-url]
[![hasem-ishmorPL 2.0][license-image]][license-url]
Translations: [Chinese 中文](https://github.com/leisurelicht/wtfpython-cn)
Translations: [Chinese 中文](https://github.com/leisurelicht/hasem-ishmorpython-cn)
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 to a regular user at first sight.
Here is a fun project to collect such tricky & counter-intuitive examples and lesser-known features in Python, attempting to discuss what exactly is happening under the hood!
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 think you'll find them interesting as well!
While some of the examples you see below may not be hasem-ishmors 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 think you'll find them interesting as well!
If you're an experienced Python programmer, you can take it as a challenge to get most of them right in first attempt. You may be already familiar with some of these examples, and I might be able to revive sweet old memories of yours being bitten by these gotchas :sweat_smile:
PS: If you're a returning reader, you can learn about the new modifications [here](https://github.com/satwikkansal/wtfpython/releases/).
PS: If you're a returning reader, you can learn about the new modifications [here](https://github.com/satwikkansal/hasem-ishmorpython/releases/).
So, here we go...
@ -87,7 +87,7 @@ So, here we go...
- [Acknowledgements](#acknowledgements)
- [🎓 License](#-license)
- [Help](#help)
- [Want to share wtfpython with friends?](#want-to-share-wtfpython-with-friends)
- [Want to share hasem-ishmorpython with friends?](#want-to-share-hasem-ishmorpython-with-friends)
- [Need a pdf version?](#need-a-pdf-version)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -133,22 +133,22 @@ A nice way to get the most out of these examples, in my opinion, will be just to
- Read the output snippets and,
+ Check if the outputs are the same as you'd expect.
+ Make sure if you know the exact reason behind the output being the way it is.
- If no, take a deep breath, and read the explanation (and if you still don't understand, shout out! and create an issue [here](https://github.com/satwikkansal/wtfPython)).
- If no, take a deep breath, and read the explanation (and if you still don't understand, shout out! and create an issue [here](https://github.com/satwikkansal/hasem-ishmorPython)).
- If yes, give a gentle pat on your back, and you may skip to the next example.
PS: You can also read WTFpython at the command line. There's a pypi package and an npm package (supports colored formatting) for the same.
PS: You can also read hasem-ishmorpython at the command line. There's a pypi package and an npm package (supports colored formatting) for the same.
To install the npm package [`wtfpython`](https://www.npmjs.com/package/wtfpython)
To install the npm package [`hasem-ishmorpython`](https://www.npmjs.com/package/hasem-ishmorpython)
```sh
$ npm install -g wtfpython
$ npm install -g hasem-ishmorpython
```
Alternatively, to install the pypi package [`wtfpython`](https://pypi.python.org/pypi/wtfpython)
Alternatively, to install the pypi package [`hasem-ishmorpython`](https://pypi.python.org/pypi/hasem-ishmorpython)
```sh
$ pip install wtfpython -U
$ pip install hasem-ishmorpython -U
```
Now, just run `wtfpython` at the command line which will open this collection in your selected `$PAGER`.
Now, just run `hasem-ishmorpython` at the command line which will open this collection in your selected `$PAGER`.
---
@ -170,17 +170,17 @@ Now, just run `wtfpython` at the command line which will open this collection in
2\.
```py
>>> a = "wtf"
>>> b = "wtf"
>>> a = "hasem-ishmor"
>>> b = "hasem-ishmor"
>>> a is b
True
>>> a = "wtf!"
>>> b = "wtf!"
>>> a = "hasem-ishmor!"
>>> b = "hasem-ishmor!"
>>> a is b
False
>>> a, b = "wtf!", "wtf!"
>>> a, b = "hasem-ishmor!", "hasem-ishmor!"
>>> a is b
True
```
@ -200,10 +200,10 @@ Makes sense, right?
+ After being interned, many variables may point to the same string object in memory (thereby saving memory).
+ In the snippets above, strings are implicitly interned. The decision of when to implicitly intern a string is implementation dependent. There are some facts that can be used to guess if a string will be interned or not:
* All length 0 and length 1 strings are interned.
* Strings are interned at compile time (`'wtf'` will be interned but `''.join(['w', 't', 'f']` will not be interned)
* 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)
* Strings are interned at compile time (`'hasem-ishmor'` will be interned but `''.join(['w', 't', 'f']` will not be interned)
* Strings that are not composed of ASCII letters, digits or underscores, are not interned. This explains why `'hasem-ishmor!'` 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)
<img src="/images/string-intern/string_intern.png" alt="">
+ 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!"` is not implicitly interned as per the facts mentioned above). It's a compiler optimization and specifically applies to the interactive environment.
+ When `a` and `b` are set to `"hasem-ishmor!"` 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 `hasem-ishmor!` as an object (because `"hasem-ishmor!"` is not implicitly interned as per the facts mentioned above). It's a compiler optimization and specifically applies to the interactive environment.
+ Constant folding is a technique for [peephole optimization](https://en.wikipedia.org/wiki/Peephole_optimization) in Python. This means the expression `'a'*20` is replaced by `'aaaaaaaaaaaaaaaaaaaa'` during compilation to reduce few clock cycles during runtime. Constant folding only occurs for strings having length less than 20. (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.
@ -273,43 +273,43 @@ def some_func():
### ▶ Deep down, we're all the same. *
```py
class WTF:
class hasem-ishmor:
pass
```
**Output:**
```py
>>> WTF() == WTF() # two different instances can't be equal
>>> hasem-ishmor() == hasem-ishmor() # two different instances can't be equal
False
>>> WTF() is WTF() # identities are also different
>>> hasem-ishmor() is hasem-ishmor() # identities are also different
False
>>> hash(WTF()) == hash(WTF()) # hashes _should_ be different as well
>>> hash(hasem-ishmor()) == hash(hasem-ishmor()) # hashes _should_ be different as well
True
>>> id(WTF()) == id(WTF())
>>> id(hasem-ishmor()) == id(hasem-ishmor())
True
```
#### 💡 Explanation:
* When `id` was called, Python created a `WTF` class object and passed it to the `id` function. The `id` function takes its `id` (its memory location), and throws away the object. The object is destroyed.
* When `id` was called, Python created a `hasem-ishmor` class object and passed it to the `id` function. The `id` function takes its `id` (its memory location), and throws away the object. The object is destroyed.
* When we do this twice in succession, Python allocates the same memory location to this second object as well. Since (in CPython) `id` uses the memory location as the object id, the id of the two objects is the same.
* So, object's id is unique only for the lifetime of the object. After the object is destroyed, or before it is created, something else can have the same id.
* But why did the `is` operator evaluated to `False`? Let's see with this snippet.
```py
class WTF(object):
class hasem-ishmor(object):
def __init__(self): print("I")
def __del__(self): print("D")
```
**Output:**
```py
>>> WTF() is WTF()
>>> hasem-ishmor() is hasem-ishmor()
I
I
D
D
False
>>> id(WTF()) == id(WTF())
>>> id(hasem-ishmor()) == id(hasem-ishmor())
I
D
I
@ -323,7 +323,7 @@ True
### ▶ For what?
```py
some_string = "wtf"
some_string = "hasem-ishmor"
some_dict = {}
for i, some_dict[i] in enumerate(some_string):
pass
@ -536,7 +536,7 @@ And when the `board` is initialized by multiplying the `row`, this is what happe
![image](/images/tic-tac-toe/after_board_initialized.png)
We can avoid this scenario here by not using `row` variable to generate `board`. (Asked in [this](https://github.com/satwikkansal/wtfpython/issues/68) issue).
We can avoid this scenario here by not using `row` variable to generate `board`. (Asked in [this](https://github.com/satwikkansal/hasem-ishmorpython/issues/68) issue).
```py
>>> board = [['']*3 for _ in range(3)]
@ -705,22 +705,22 @@ SyntaxError: invalid syntax
**Output:**
```py
>>> print('wtfpython''')
wtfpython
>>> print("wtfpython""")
wtfpython
>>> print('hasem-ishmorpython''')
hasem-ishmorpython
>>> print("hasem-ishmorpython""")
hasem-ishmorpython
>>> # The following statements raise `SyntaxError`
>>> # print('''wtfpython')
>>> # print("""wtfpython")
>>> # print('''hasem-ishmorpython')
>>> # print("""hasem-ishmorpython")
```
#### 💡 Explanation:
+ Python supports implicit [string literal concatenation](https://docs.python.org/2/reference/lexical_analysis.html#string-literal-concatenation), Example,
```
>>> print("wtf" "python")
wtfpython
>>> print("wtf" "") # or "wtf"""
wtf
>>> print("hasem-ishmor" "python")
hasem-ishmorpython
>>> print("hasem-ishmor" "") # or "hasem-ishmor"""
hasem-ishmor
```
+ `'''` and `"""` are also string delimiters in Python which causes a SyntaxError because the Python interpreter was expecting a terminating triple quote as delimiter while scanning the currently encountered triple quoted string literal.
@ -798,10 +798,10 @@ another_dict[1.0] = "Python"
3\.
```py
>>> some_bool = True
>>> "wtf"*some_bool
'wtf'
>>> "hasem-ishmor"*some_bool
'hasem-ishmor'
>>> some_bool = False
>>> "wtf"*some_bool
>>> "hasem-ishmor"*some_bool
''
```
@ -2346,15 +2346,15 @@ nan
All patches are Welcome! Please see [CONTRIBUTING.md](/CONTRIBUTING.md) for further details.
For discussions, you can either create a new [issue](https://github.com/satwikkansal/wtfpython/issues/new) or ping on the Gitter [channel](https://gitter.im/wtfpython/Lobby)
For discussions, you can either create a new [issue](https://github.com/satwikkansal/hasem-ishmorpython/issues/new) or ping on the Gitter [channel](https://gitter.im/hasem-ishmorpython/Lobby)
# Acknowledgements
The idea and design for this collection were initially inspired by Denys Dovhan's awesome project [wtfjs](https://github.com/denysdovhan/wtfjs). The overwhelming support by the community gave it the shape it is in right now.
The idea and design for this collection were initially inspired by Denys Dovhan's awesome project [hasem-ishmorjs](https://github.com/denysdovhan/hasem-ishmorjs). The overwhelming support by the community gave it the shape it is in right now.
#### Some nice Links!
* https://www.youtube.com/watch?v=sH4XF6pKKmk
* https://www.reddit.com/r/Python/comments/3cu6ej/what_are_some_wtf_things_about_python
* https://www.reddit.com/r/Python/comments/3cu6ej/what_are_some_hasem-ishmor_things_about_python
* https://sopython.com/wiki/Common_Gotchas_In_Python
* https://stackoverflow.com/questions/530530/python-2-x-gotchas-and-landmines
* https://stackoverflow.com/questions/1011431/common-pitfalls-in-python
@ -2367,20 +2367,20 @@ The idea and design for this collection were initially inspired by Denys Dovhan'
&copy; [Satwik Kansal](https://satwikkansal.xyz)
[license-url]: http://www.wtfpl.net
[license-image]: https://img.shields.io/badge/License-WTFPL%202.0-lightgrey.svg?style=flat-square
[license-url]: http://www.hasem-ishmorpl.net
[license-image]: https://img.shields.io/badge/License-hasem-ishmorPL%202.0-lightgrey.svg?style=flat-square
## Help
If you have any wtfs, ideas or suggestions, please share.
If you have any hasem-ishmors, ideas or suggestions, please share.
## Surprise your geeky pythonist friends?
You can use these quick links to recommend wtfpython to your friends,
You can use these quick links to recommend hasem-ishmorpython to your friends,
[Twitter](https://twitter.com/intent/tweet?url=https://github.com/satwikkansal/wtfpython&hastags=python,wtfpython)
| [Linkedin](https://www.linkedin.com/shareArticle?url=https://github.com/satwikkansal&title=What%20the%20f*ck%20Python!&summary=An%20interesting%20collection%20of%20subtle%20and%20tricky%20Python%20snippets.)
[Twitter](https://twitter.com/intent/tweet?url=https://github.com/satwikkansal/hasem-ishmorpython&hastags=python,hasem-ishmorpython)
| [Linkedin](https://www.linkedin.com/shareArticle?url=https://github.com/satwikkansal&title=What%20the%20hasem-ishmor%20Python!&summary=An%20interesting%20collection%20of%20subtle%20and%20tricky%20Python%20snippets.)
## Need a pdf version?
I've received a few requests for the pdf version of wtfpython. You can add your details [here](https://satwikkansal.xyz/wtfpython-pdf/) to get the pdf as soon as it is finished.
I've received a few requests for the pdf version of hasem-ishmorpython. You can add your details [here](https://satwikkansal.xyz/hasem-ishmorpython-pdf/) to get the pdf as soon as it is finished.

View File

@ -7,7 +7,7 @@ except ImportError:
from urllib import urlretrieve
url = ("http://raw.githubusercontent.com/satwikkansal/"
"wtfpython/master/README.md")
"hasem-ishmorpython/master/README.md")
file_path = join(dirname(dirname(realpath(__file__))), "content.md")

View File

@ -2,7 +2,7 @@
import sys
from wtf_python.main import load_and_read
from hasem-ishmor_python.main import load_and_read
if __name__ == "__main__":
sys.exit(load_and_read())

View File

@ -1,21 +1,21 @@
from setuptools import setup, find_packages
if __name__ == "__main__":
setup(name='wtfpython',
setup(name='hasem-ishmorpython',
version='0.2',
description='What the f*ck Python!',
description='What the hasem-ishmor Python!',
author="Satwik Kansal",
maintainer="Satwik Kansal",
maintainer_email='satwikkansal@gmail.com',
url='https://github.com/satwikkansal/wtfpython',
url='https://github.com/satwikkansal/hasem-ishmorpython',
platforms='any',
license="WTFPL 2.0",
license="hasem-ishmorPL 2.0",
long_description="An interesting collection of subtle & tricky Python Snippets"
" and features.",
keywords="wtfpython gotchas snippets tricky",
keywords="hasem-ishmorpython gotchas snippets tricky",
packages=find_packages(),
entry_points = {
'console_scripts': ['wtfpython = wtf_python.main:load_and_read']
'console_scripts': ['hasem-ishmorpython = hasem-ishmor_python.main:load_and_read']
},
classifiers=[
'Development Status :: 4 - Beta',

View File

@ -1,24 +0,0 @@
import uuid
new_file = []
original_file = []
fname = "../README.md"
def generate_random_id_comment():
random_id = uuid.uuid4()
return f"<!-- Example ID: {random_id} --!>"
with open(fname, "r") as f:
original_file = f.readlines()
for line in original_file:
new_file.append(line)
if line.strip().startswith("### "):
new_file.append(generate_random_id_comment())
with open(fname, "w") as f:
f.write("".join(new_file))

View File

@ -1,397 +0,0 @@
"""
An inefficient monolithic piece of code that'll generate jupyter notebook
from the projects main README.
PS: If you are a recruiter, please don't judge me by this piece of code. I wrote it
in hurry. I know this is messy and can be simplified, but I don't want to change it
much because it just works.
Simplifictions and improvements through patches are more than welcome however :)
#TODOs
- CLI arguments for running this thing
- Add it to prepush hook
- Add support for skip comments, to skip examples that are not meant for notebook environment.
- Use templates?
"""
import json
import os
import pprint
fpath = os.path.join(os.path.dirname( __file__ ), '..', 'README.md')
examples = []
# The globals
current_example = 1
sequence_num = 1
current_section_name = ""
STATEMENT_PREFIXES = ["...", ">>> ", "$ "]
HOSTED_NOTEBOOK_INSTRUCTIONS = """
## Hosted notebook instructions
This is just an experimental attempt of browsing wtfpython through jupyter notebooks. Some examples are read-only because,
- they either require a version of Python that's not supported in the hosted runtime.
- or they can't be reproduced in the notebook envrinonment.
The expected outputs are already present in collapsed cells following the code cells. The Google colab provides Python2 (2.7) and Python3 (3.6, default) runtimes. You can switch among these for Python2 specific examples. For examples specific to other minor versions, you can simply refer to collapsed outputs (it's not possible to control the minor version in hosted notebooks as of now). You can check the active version using
```py
>>> import sys
>>> sys.version
# Prints out Python version here.
```
That being said, most of the examples do work as expected. If you face any trouble, feel free to consult the original content on wtfpython and create an issue in the repo. Have fun!
---
"""
def generate_code_block(statements, output):
"""
Generates a code block that executes the given statements.
:param statements: The list of statements to execute.
:type statements: list(str)
"""
global sequence_num
result = {
"type": "code",
"sequence_num": sequence_num,
"statements": statements,
"output": output
}
sequence_num += 1
return result
def generate_markdown_block(lines):
"""
Generates a markdown block from a list of lines.
"""
global sequence_num
result = {
"type": "markdown",
"sequence_num": sequence_num,
"value": lines
}
sequence_num += 1
return result
def is_interactive_statement(line):
for prefix in STATEMENT_PREFIXES:
if line.lstrip().startswith(prefix):
return True
return False
def parse_example_parts(lines, title, current_line):
"""
Parse the given lines and return a dictionary with two keys:
build_up, which contains all the text before an H4 (explanation) is encountered,
and
explanation, which contains all the text after build_up until --- or another H3 is encountered.
"""
parts = {
"build_up": [],
"explanation": []
}
content = [title]
statements_so_far = []
output_so_far = []
next_line = current_line
# store build_up till an H4 (explanation) is encountered
while not (next_line.startswith("#### ")or next_line.startswith('---')):
# Watching out for the snippets
if next_line.startswith("```py"):
# It's a snippet, whatever found until now is text
is_interactive = False
output_encountered = False
if content:
parts["build_up"].append(generate_markdown_block(content))
content = []
next_line = next(lines)
while not next_line.startswith("```"):
if is_interactive_statement(next_line):
is_interactive = True
if (output_so_far):
parts["build_up"].append(generate_code_block(statements_so_far, output_so_far))
statements_so_far, output_so_far = [], []
statements_so_far.append(next_line)
else:
# can be either output or normal code
if is_interactive:
output_so_far.append(next_line)
elif output_encountered:
output_so_far.append(next_line)
else:
statements_so_far.append(next_line)
next_line = next(lines)
# Snippet is over
parts["build_up"].append(generate_code_block(statements_so_far, output_so_far))
statements_so_far, output_so_far = [], []
next_line = next(lines)
else:
# It's a text, go on.
content.append(next_line)
next_line = next(lines)
# Explanation encountered, save any content till now (if any)
if content:
parts["build_up"].append(generate_markdown_block(content))
# Reset stuff
content = []
statements_so_far, output_so_far = [], []
# store lines again until --- or another H3 is encountered
while not (next_line.startswith("---") or
next_line.startswith("### ")):
if next_line.lstrip().startswith("```py"):
# It's a snippet, whatever found until now is text
is_interactive = False
if content:
parts["explanation"].append(generate_markdown_block(content))
content = []
next_line = next(lines)
while not next_line.lstrip().startswith("```"):
if is_interactive_statement(next_line):
is_interactive = True
if (output_so_far):
parts["explanation"].append(generate_code_block(statements_so_far, output_so_far))
statements_so_far, output_so_far = [], []
statements_so_far.append(next_line)
else:
# can be either output or normal code
if is_interactive:
output_so_far.append(next_line)
else:
statements_so_far.append(next_line)
next_line = next(lines)
# Snippet is over
parts["explanation"].append(generate_code_block(statements_so_far, output_so_far))
statements_so_far, output_so_far = [], []
next_line = next(lines)
else:
# It's a text, go on.
content.append(next_line)
next_line = next(lines)
# All done
if content:
parts["explanation"].append(generate_markdown_block(content))
return next_line, parts
def remove_from_beginning(tokens, line):
for token in tokens:
if line.lstrip().startswith(token):
line = line.replace(token, "")
return line
def inspect_and_sanitize_code_lines(lines):
"""
Remove lines from the beginning of a code block that are not statements.
:param lines: A list of strings, each representing a line in the code block.
:returns is_print_present, sanitized_lines: A boolean indicating whether print was present in the original code and a list of strings representing
sanitized lines. The latter may be an empty list if all input lines were removed as comments or whitespace (and thus did not contain any statements).
This function does not remove blank lines at the end of `lines`.
"""
tokens_to_remove = STATEMENT_PREFIXES
result = []
is_print_present = False
for line in lines:
line = remove_from_beginning(tokens_to_remove, line)
if line.startswith("print ") or line.startswith("print("):
is_print_present = True
result.append(line)
return is_print_present, result
def convert_to_cells(cell_contents, read_only):
"""
Converts a list of dictionaries containing markdown and code cells into a Jupyter notebook.
:param cell_contents: A list of dictionaries, each
dictionary representing either a markdown or code cell. Each dictionary should have the following keys: "type", which is either "markdown" or "code",
and "value". The value for type = 'markdown' is the content as string, whereas the value for type = 'code' is another dictionary with two keys,
statements and output. The statements key contains all lines in between ```py\n``` (including) until ```\n```, while output contains all lines after
```.output py\n```.
:type cell_contents: List[Dict]
:param read_only (optional): If True then only print outputs are included in converted
cells. Default False
:type read_only (optional): bool
:returns A Jupyter notebook containing all cells from input parameter `cell_contents`.
Each converted cell has metadata attribute collapsed set to true if it's code-cell otherwise None if it's markdow-cell.
"""
cells = []
for stuff in cell_contents:
if stuff["type"] == "markdown":
# todo add metadata later
cells.append(
{
"cell_type": "markdown",
"metadata": {},
"source": stuff["value"]
}
)
elif stuff["type"] == "code":
if read_only:
# Skip read only
# TODO: Fix
cells.append(
{
"cell_type": "markdown",
"metadata": {},
"source": ["```py\n"] + stuff["statements"] + ["```\n"] + ["```py\n"] + stuff['output'] + ["```\n"]
}
)
continue
is_print_present, sanitized_code = inspect_and_sanitize_code_lines(stuff["statements"])
if is_print_present:
cells.append(
{
"cell_type": "code",
"metadata": {
"collapsed": True,
},
"execution_count": None,
"outputs": [{
"name": "stdout",
"output_type": "stream",
"text": stuff["output"]
}],
"source": sanitized_code
}
)
else:
cells.append(
{
"cell_type": "code",
"execution_count": None,
"metadata": {
"collapsed": True
},
"outputs": [{
"data": {
"text/plain": stuff["output"]
},
"output_type": "execute_result",
"metadata": {},
"execution_count": None
}],
"source": sanitized_code
}
)
return cells
def convert_to_notebook(pre_examples_content, parsed_json, post_examples_content):
"""
Convert a JSON file containing the examples to a Jupyter Notebook.
"""
result = {
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 2
}
notebook_path = "wtf.ipynb"
result["cells"] += convert_to_cells([generate_markdown_block(pre_examples_content)], False)
for example in parsed_json:
parts = example["parts"]
build_up = parts.get("build_up")
explanation = parts.get("explanation")
read_only = example.get("read_only")
if build_up:
result["cells"] += convert_to_cells(build_up, read_only)
if explanation:
result["cells"] += convert_to_cells(explanation, read_only)
result["cells"] += convert_to_cells([generate_markdown_block(post_examples_content)], False)
#pprint.pprint(result, indent=2)
with open(notebook_path, "w") as f:
json.dump(result, f, indent=2)
with open(fpath, 'r+', encoding="utf-8") as f:
lines = iter(f.readlines())
line = next(lines)
result = []
pre_examples_phase = True
pre_stuff = []
post_stuff = []
try:
while True:
if line.startswith("## "):
pre_examples_phase = False
# A section is encountered
current_section_name = line.replace("## ", "").strip()
section_text = []
line = next(lines)
# Until a new section is encountered
while not (line.startswith("## ") or line.startswith("# ")):
# check if it's a H3
if line.startswith("### "):
# An example is encountered
title_line = line
line = next(lines)
read_only = False
while line.strip() == "" or line.startswith('<!--'):
#TODO: Capture example ID here using regex.
if '<!-- read-only -->' in line:
read_only = True
line = next(lines)
example_details = {
"id": current_example,
"title": title_line.replace("### ", ""),
"section": current_section_name,
"read_only": read_only
}
line, example_details["parts"] = parse_example_parts(lines, title_line, line)
result.append(example_details)
current_example += 1
else:
section_text.append(line)
line = next(lines)
else:
if pre_examples_phase:
pre_stuff.append(line)
else:
post_stuff.append(line)
line = next(lines)
except StopIteration as e:
#pprint.pprint(result, indent=2)
pre_stuff.append(HOSTED_NOTEBOOK_INSTRUCTIONS)
result.sort(key = lambda x: x["read_only"])
convert_to_notebook(pre_stuff, result, post_stuff)

View File

@ -1,7 +0,0 @@
## Generating the notebook
- Expand the relative links in README.md to absolute ones
- Remove the TOC in README.md (because Google colab generates its own anyway)
- Reorder the examples, so that the ones that work are upfront.
- Run the `notebook_generator.py`, it will generate a notebook named `wtf.ipynb`
- Revert the README.md changes (optional)

View File

@ -1,152 +0,0 @@
Skipping lines?
a
Well, something is fishy...
a
Time for some hash brownies!
f
Evaluation time discrepancy
f
Modifying a dictionary while iterating over it
c
Deleting a list item while iterating over it
c
Backslashes at the end of string
f
Brace yourself!
t*
"this" is love :heart:
t*
Okay Python, Can you make me fly?
t*
`goto`, but why?
t*
Let's meet Friendly Language Uncle For Life
t*
Inpinity
t*
Strings can be tricky sometimes
f*
`+=` is faster
m
Let's make a giant string!
m
Yes, it exists!
t
`is` is not what it is!
f
`is not ...` is not `is (not ...)`
f
The function inside loop sticks to the same output
f
Loop variables leaking out of local scope!
c
A tic-tac-toe where X wins in the first attempt!
f
Beware of default mutable arguments!
c
Same operands, different story!
c
Mutating the immutable!
f
Using a variable not defined in scope
c
The disappearing variable from outer scope
f
Return return everywhere!
f
When True is actually False
f
Be careful with chained operations
c
Name resolution ignoring class scope
c
From filled to None in one instruction...
f
Explicit typecast of strings
m
Class attributes and instance attributes
f
Catching the Exceptions!
f
Midnight time doesn't exist?
f
What's wrong with booleans?
f
Needle in a Haystack
c
Teleportation
a*
yielding None
f
The surprising comma
f
For what?
f
not knot!
f
Subclass relationships
f*
Mangling time!
t*
Deep down, we're all the same.
f*
Half triple-quoted strings
f
Implicit key type conversion
f*
Stubborn `del` operator
c*
Let's see if you can guess this?
f
Minor Ones
m

View File

@ -1,53 +0,0 @@
"""
This script parses the README.md and generates the table
`CONTRIBUTORS.md`.
No longer works since we've moved on contributors to CONTRIBUTORS.md entirely.
"""
import pprint
import re
import requests
regex = ("[sS]uggested by @(\S+) in \[this\]\(https:\/\/github\.com\/satwikkansal"
"\/wtf[pP]ython\/issues\/(\d+)\) issue")
fname = "README.md"
contribs = {}
table_header = """
| Contributor | Github | Issues |
|-------------|--------|--------|
"""
table_row = '| {} | [{}](https://github.com/{}) | {} |'
issue_format = '[#{}](https:/github.com/satwikkansal/wtfpython/issues/{})'
rows_so_far = []
github_rest_api = "https://api.github.com/users/{}"
with open(fname, 'r') as f:
file_content = f.read()
matches = re.findall(regex, file_content)
for match in matches:
if contribs.get(match[0]) and match[1] not in contribs[match[0]]:
contribs[match[0]].append(match[1])
else:
contribs[match[0]] = [match[1]]
for handle, issues in contribs.items():
issue_string = ', '.join([issue_format.format(i, i) for i in issues])
resp = requests.get(github_rest_api.format(handle))
name = handle
if resp.status_code == 200:
pprint.pprint(resp.json()['name'])
else:
print(handle, resp.content)
rows_so_far.append(table_row.format(name,
handle,
handle,
issue_string))
print(table_header + "\n".join(rows_so_far))

File diff suppressed because it is too large Load Diff

View File

@ -1,151 +0,0 @@
# -*- coding: utf-8 -*-
"""
This inefficient module would parse the README.md in the initial version of
WTFPython, and enable me to categorize and reorder a hell lot of examples with
the help of the file `add_categories` (part of which is automatically
generated).
After the refactor, this module would not work now with necessary updates in
the code.
"""
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
fname = "README.md"
snippets = []
with open(fname, 'r') as f:
lines = iter(f.readlines())
line = lines.next()
try:
while True:
# check if it's a H3
if line.startswith("### "):
title = line.replace("### ", "")
description = []
next_line = lines.next()
# store lines till an H4 (explanation) is encountered
while not next_line.startswith("#### "):
description.append(next_line)
next_line = lines.next()
explanation = []
# store lines again until --- or another H3 is encountered
while not (next_line.startswith("---") or
next_line.startswith("### ")):
explanation.append(next_line)
next_line = lines.next()
# Store the results finally
snippets.append({
"title": title,
"description": '\n'.join(description),
"explanation": '\n'.join(explanation)
})
line = next_line
else:
line = lines.next()
except StopIteration:
snippets.append({
"title": title,
"description": '\n'.join(description),
"explanation": '\n'.join(explanation)
})
'''
# Create a file
file_content = "\n\n".join([snip["title"] for snip in snippets])
with open("add_categories", "w") as f:
f.write(file_content)
'''
snips_by_title = {}
with open("add_categories", "r") as f:
content = iter(f.readlines())
try:
while True:
title = content.next()
cat = content.next().strip()
is_new = True if cat[-1]=="*" else False
cat = cat.replace('*','')
snips_by_title[title] = {
"category": cat,
"is_new": is_new
}
content.next()
except StopIteration:
pass
for idx, snip in enumerate(snippets):
snippets[idx]["category"] = snips_by_title[snip["title"]]["category"]
snippets[idx]["is_new"] = snips_by_title[snip["title"]]["is_new"]
snips_by_cat = {}
for snip in snippets:
cat = snip["category"]
if not snips_by_cat.get(cat):
snips_by_cat[cat] = []
snips_by_cat[cat].append(snip)
snippet_template = """
### ▶ {title}{is_new}
{description}
{explanation}
---
"""
category_template = """
---
## {category}
{content}
"""
result = ""
category_names = {
"a": "Appearances are Deceptive!",
"t": "The Hiddent treasures",
"f": "Strain your Brain",
"c": "Be careful of these",
"m": "Miscallaneous"
}
categories_in_order = ["a", "t", "f", "c", "m"]
for category in categories_in_order:
snips = snips_by_cat[category]
for i, snip in enumerate(snips):
print(i, ":", snip["title"])
content = ""
for _ in snips:
snip = snips[int(raw_input())]
is_new = " *" if snip["is_new"] else ""
content += snippet_template.format(title=snip["title"].strip(),
is_new=is_new,
description=snip["description"].strip().replace("\n\n", "\n"),
explanation=snip["explanation"].strip().replace("\n\n", "\n"))
result += category_template.format(category=category_names[category], content=content.replace("\n\n\n", "\n\n"))
with open("generated.md", "w") as f:
f.write(result.replace("\n\n\n", "\n\n"))
print("Done!")

13478
irrelevant/wtf.ipynb vendored

File diff suppressed because it is too large Load Diff