From 596186d81c82cfef0290d28cd2ddb227e916a306 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Wed, 1 May 2019 19:12:17 +0530 Subject: [PATCH 01/29] Add json generator --- irrelevant/json_generator.py | 156 +++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 irrelevant/json_generator.py diff --git a/irrelevant/json_generator.py b/irrelevant/json_generator.py new file mode 100644 index 0000000..86820f1 --- /dev/null +++ b/irrelevant/json_generator.py @@ -0,0 +1,156 @@ +import pprint +fname = "/Users/300041709/code/self/wtfpython/README.md" +examples = [] + +# The globals +current_example = 1 +current_section_name = "" + + +def parse_example_parts(lines): + parts = { + "build_up": [], + "explanation": [] + } + next_line = next(lines) + sequence_num = 1 + content = [] + # store build_up till an H4 (explanation) is encountered + while not next_line.startswith("#### "): + # Watching out for the snippets + if next_line.startswith("```"): + # It's a snippet, whatever found until now is text + if content: + parts["build_up"].append( + { + "type": "text", + "sequence_num": sequence_num, + "value": content + } + ) + sequence_num += 1 + content = [] + + next_line = next(lines) + while not next_line.startswith("```"): + content.append(next_line) + next_line = next(lines) + # Snippet is over + parts["build_up"].append( + { + "type": "code", + "sequence_num": sequence_num, + "value": content + } + ) + sequence_num += 1 + content = [] + next_line = next(lines) + continue + 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( + { + "type": "text", + "sequence_num": sequence_num, + "value": content + } + ) + + # Reset stuff + sequence_num = 1 + content = [] + + # store lines again until --- or another H3 is encountered + while not (next_line.startswith("---") or + next_line.startswith("### ")): + + if next_line.startswith("```"): + # It's a snippet, whatever found until now is text + if content: + parts["explanation"].append( + { + "type": "text", + "sequence_num": sequence_num, + "value": content + } + ) + sequence_num += 1 + content = [] + + next_line = next(lines) + while not next_line.startswith("```"): + content.append(next_line) + next_line = next(lines) + # Snippet is over + parts["explanation"].append( + { + "type": "code", + "sequence_num": sequence_num, + "value": content + } + ) + sequence_num += 1 + content = [] + next_line = next(lines) + continue + else: + # It's a text, go on. + content.append(next_line) + next_line = next(lines) + + # All done + if content: + parts["explanation"].append( + { + "type": "text", + "sequence_num": sequence_num, + "value": content + } + ) + + return next_line, parts + + +with open(fname, 'r+', encoding="utf-8") as f: + lines = iter(f.readlines()) + line = next(lines) + result = [] + try: + while True: + if line.startswith("## "): + # A section is encountered + current_section_name = line.replace("## ", "").strip() + line = next(lines) + # Until a new section is encountered + while not (line.startswith("## " )): + # check if it's a H3 + if line.startswith("### "): + # An example is encountered + title = line.replace("### ", "") + example_details = { + "id": current_example, + "title": line.replace("### ", ""), + "section": current_section_name + } + line, example_details["parts"] = parse_example_parts(lines) + result.append(example_details) + current_example += 1 + else: + # todo catch section text + line = next(lines) + else: + line = next(lines) + + except StopIteration: + pprint.pprint(result, indent=2) + print(len(result)) + + + + From c475cbfe826f2b65d3674f3acf724f14f97c50dc Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Wed, 1 May 2019 19:55:46 +0530 Subject: [PATCH 02/29] Use absolute path for the pager. Fixes https://github.com/satwikkansal/wtfpython/issues/119 --- wtfpython-pypi/wtf_python/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/wtfpython-pypi/wtf_python/main.py b/wtfpython-pypi/wtf_python/main.py index 73de9b8..b8bfdce 100644 --- a/wtfpython-pypi/wtf_python/main.py +++ b/wtfpython-pypi/wtf_python/main.py @@ -1,3 +1,5 @@ +from os.path import dirname, join, realpath + import pydoc try: from urllib.request import urlretrieve @@ -6,13 +8,14 @@ except ImportError: url = ("https://raw.githubusercontent.com/satwikkansal/" "wtfpython/master/README.md") -file_name = "content.md" + +file_path = join(dirname(dirname(realpath(__file__))), "content.md") def fetch_updated_doc(): try: print("Fetching the latest version...") - urlretrieve(url, file_name) + urlretrieve(url, file_path) print("Done!") except Exception as e: print(e) @@ -21,7 +24,7 @@ def fetch_updated_doc(): def render_doc(): - with open(file_name, 'r') as f: + with open(file_path, 'r', encoding="utf-8") as f: content = f.read() pydoc.pager(content) From 5c8c4c58439dd2ce020c536343b2b4460ce3cd02 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 6 May 2019 23:47:24 +0530 Subject: [PATCH 03/29] Get the generator in a working state --- irrelevant/json_generator.py | 239 +++++++++++++++++++++++++---------- 1 file changed, 175 insertions(+), 64 deletions(-) diff --git a/irrelevant/json_generator.py b/irrelevant/json_generator.py index 86820f1..ea75cb5 100644 --- a/irrelevant/json_generator.py +++ b/irrelevant/json_generator.py @@ -1,52 +1,85 @@ +import json import pprint fname = "/Users/300041709/code/self/wtfpython/README.md" examples = [] # The globals current_example = 1 +sequence_num = 1 current_section_name = "" -def parse_example_parts(lines): +STATEMENT_PREFIXES = ["...", ">>> ", "$ "] + + +def generate_code_block(statements, output): + global sequence_num + result = { + "type": "code", + "sequence_num": sequence_num, + "statements": statements, + "output": output + } + sequence_num += 1 + return result + + +def generate_markdown_block(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.startswith(prefix): + return True + return False + +def parse_example_parts(lines, example_title_line): parts = { "build_up": [], "explanation": [] } - next_line = next(lines) - sequence_num = 1 content = [] + statements_so_far = [] + output_so_far = [] + next_line = example_title_line # store build_up till an H4 (explanation) is encountered while not next_line.startswith("#### "): # Watching out for the snippets if next_line.startswith("```"): # It's a snippet, whatever found until now is text + is_interactive = False if content: - parts["build_up"].append( - { - "type": "text", - "sequence_num": sequence_num, - "value": content - } - ) - sequence_num += 1 + parts["build_up"].append(generate_markdown_block(content)) content = [] next_line = next(lines) + while not next_line.startswith("```"): - content.append(next_line) + 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) + else: + statements_so_far.append(next_line) next_line = next(lines) + # Snippet is over - parts["build_up"].append( - { - "type": "code", - "sequence_num": sequence_num, - "value": content - } - ) - sequence_num += 1 - content = [] + parts["build_up"].append(generate_code_block(statements_so_far, output_so_far)) + statements_so_far, output_so_far = [], [] next_line = next(lines) - continue else: # It's a text, go on. content.append(next_line) @@ -54,51 +87,43 @@ def parse_example_parts(lines): # Explanation encountered, save any content till now (if any) if content: - parts["build_up"].append( - { - "type": "text", - "sequence_num": sequence_num, - "value": content - } - ) + parts["build_up"].append(generate_markdown_block(content)) # Reset stuff - sequence_num = 1 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.startswith("```"): # It's a snippet, whatever found until now is text + is_interactive = False if content: - parts["explanation"].append( - { - "type": "text", - "sequence_num": sequence_num, - "value": content - } - ) - sequence_num += 1 + parts["build_up"].append(generate_markdown_block(content)) content = [] next_line = next(lines) + while not next_line.startswith("```"): - content.append(next_line) + 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) + else: + statements_so_far.append(next_line) next_line = next(lines) + # Snippet is over - parts["explanation"].append( - { - "type": "code", - "sequence_num": sequence_num, - "value": content - } - ) - sequence_num += 1 - content = [] + parts["build_up"].append(generate_code_block(statements_so_far, output_so_far)) + statements_so_far, output_so_far = [], [] next_line = next(lines) - continue else: # It's a text, go on. content.append(next_line) @@ -106,16 +131,105 @@ def parse_example_parts(lines): # All done if content: - parts["explanation"].append( - { - "type": "text", - "sequence_num": sequence_num, - "value": content - } - ) + parts["explanation"].append(generate_markdown_block(content)) return next_line, parts +def remove_from_beginning(tokens, line): + for token in tokens: + if line.startswith(token): + line = line.replace(token, "") + return line + + +def inspect_and_sanitize_code_lines(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): + 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": + 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(parsed_json): + result = { + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 2 + } + for example in parsed_json: + parts = example["parts"] + build_up = parts.get("build_up") + explanation = parts.get("explanation") + notebook_path = "test.ipynb" + + if(build_up): + result["cells"] += convert_to_cells(build_up) + + if(explanation): + result["cells"] += convert_to_cells(explanation) + + pprint.pprint(result, indent=2) + with open(notebook_path, "w") as f: + json.dump(result, f) + + with open(fname, 'r+', encoding="utf-8") as f: lines = iter(f.readlines()) @@ -126,6 +240,7 @@ with open(fname, 'r+', encoding="utf-8") as f: if line.startswith("## "): # 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("## " )): @@ -138,19 +253,15 @@ with open(fname, 'r+', encoding="utf-8") as f: "title": line.replace("### ", ""), "section": current_section_name } - line, example_details["parts"] = parse_example_parts(lines) + line, example_details["parts"] = parse_example_parts(lines, line) result.append(example_details) current_example += 1 else: - # todo catch section text + section_text.append(line) line = next(lines) else: line = next(lines) except StopIteration: pprint.pprint(result, indent=2) - print(len(result)) - - - - + convert_to_notebook(result) From 45bec627d1511690fa7f1b5623860da94bb74822 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 6 May 2019 23:49:57 +0530 Subject: [PATCH 04/29] Add todos --- irrelevant/json_generator.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/irrelevant/json_generator.py b/irrelevant/json_generator.py index ea75cb5..cfebf4f 100644 --- a/irrelevant/json_generator.py +++ b/irrelevant/json_generator.py @@ -1,3 +1,15 @@ +""" +An inefficient monolithic piece of code that'll generate jupyter notebook +from the projects main README. + +TODO + +- CLI arguments for running this thing. +- Add it to prepush hook +- Use templates? + +""" + import json import pprint fname = "/Users/300041709/code/self/wtfpython/README.md" From f03f7dcad956c941e9f9d9247f50e8ffde98e013 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Fri, 7 Jun 2019 23:48:07 +0530 Subject: [PATCH 05/29] Some hideous typora corrections and a new snippets added Also closes https://github.com/satwikkansal/wtfpython/issues/73 --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8f116bd..d4de4d7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

+

What the f*ck Python! 🐍

An interesting collection of surprising snippets and lesser-known Python features.

@@ -201,11 +201,10 @@ Makes sense, right? * 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) - + + 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. + 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. - --- ### ▶ Time for some hash brownies! @@ -246,7 +245,7 @@ some_dict[5] = "Python" --- -### ▶ Return return everywhere! +### ▶ Keep trying? * ```py def some_func(): @@ -254,18 +253,55 @@ def some_func(): return 'from_try' finally: return 'from_finally' + +def another_func(): + for _ in range(3): + try: + continue + finally: + print("Finally!") + +def one_more_func(): # A gotcha! + try: + for i in range(3): + try: + 1 / i + except ZeroDivisionError: + # Let's throw it here and handle it outside for loop + raise ZeroDivisionError("A trivial divide by zero error") + finally: + print("Iteration", i) + break + except ZeroDivisionError as e: + print("Zero division error ocurred", e) ``` **Output:** + ```py >>> some_func() 'from_finally' + +>>> another_func() +Finally! +Finally! +Finally! + +>>> 1 / 0 +Traceback (most recent call last): + File "", line 1, in +ZeroDivisionError: division by zero + +>>> one_more_func() +Iteration 0 + ``` #### 💡 Explanation: - When a `return`, `break` or `continue` statement is executed in the `try` suite of a "try…finally" statement, the `finally` clause is also executed ‘on the way out. - The return value of a function is determined by the last `return` statement executed. Since the `finally` clause always executes, a `return` statement executed in the `finally` clause will always be the last one executed. +- The caveat here is, if the finally clause executes a `return` or `break` statement, the temporarily saved exception is discarded. --- @@ -529,11 +565,11 @@ We didn't assign 3 "X"s or did we? When we initialize `row` variable, this visualization explains what happens in the memory -![image](/images/tic-tac-toe/after_row_initialized.png) +![image](images/tic-tac-toe/after_row_initialized.png) And when the `board` is initialized by multiplying the `row`, this is what happens inside the memory (each of the elements `board[0]`, `board[1]` and `board[2]` is a reference to the same list referred by `row`) -![image](/images/tic-tac-toe/after_board_initialized.png) +![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). @@ -1183,8 +1219,9 @@ a, b = a[b] = {}, 5 (target_list "=")+ (expression_list | yield_expression) ``` and - > An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right. - + +> An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right. + * The `+` in `(target_list "=")+` means there can be **one or more** target lists. In this case, target lists are `a, b` and `a[b]` (note the expression list is exactly one, which in our case is `{}, 5`). * After the expression list is evaluated, it's value is unpacked to the target lists from **left to right**. So, in our case, first the `{}, 5` tuple is unpacked to `a, b` and we now have `a = {}` and `b = 5`. @@ -1318,6 +1355,7 @@ Shouldn't that be 100? * **Don't mix tabs and spaces!** The character just preceding return is a "tab", and the code is indented by multiple of "4 spaces" elsewhere in the example. * This is how Python handles tabs: + > First, tabs are replaced (from left to right) by one to eight spaces such that the total number of characters up to and including the replacement is a multiple of eight <...> * So the "tab" at the last line of `square` function is replaced with eight spaces, and it gets into the loop. * Python 3 is kind enough to throw an error for such cases automatically. @@ -1978,6 +2016,7 @@ There we go. #### 💡 Explanation: - This is relevant to [PEP-401](https://www.python.org/dev/peps/pep-0401/) released on April 1, 2009 (now you know, what it means). - Quoting from the PEP-401 + > 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/). From 3806284f78b1631fb693bd828c55b9ed4885c5cc Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Fri, 7 Jun 2019 23:48:51 +0530 Subject: [PATCH 06/29] Update content.md --- wtfpython-pypi/content.md | 79 ++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/wtfpython-pypi/content.md b/wtfpython-pypi/content.md index 486a98e..9827fc4 100644 --- a/wtfpython-pypi/content.md +++ b/wtfpython-pypi/content.md @@ -1,9 +1,10 @@

-

What the f*ck Python! 🐍

-

An interesting collection of surprising snippets and lesser-known Python features.

+

What the f*ck Python! 🐍

+

An interesting collection of surprising snippets and lesser-known Python features.

[![WTFPL 2.0][license-image]][license-url] +Translations: [Chinese 中文](https://github.com/leisurelicht/wtfpython-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. @@ -13,7 +14,7 @@ While some of the examples you see below may not be WTFs in the truest sense, bu 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: -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/wtfpython/releases/). So, here we go... @@ -77,7 +78,7 @@ So, here we go... - [▶ Yes, it exists!](#-yes-it-exists) - [▶ Inpinity *](#-inpinity-) - [▶ Mangling time! *](#-mangling-time-) - - [Section: Miscallaneous](#section-miscallaneous) + - [Section: Miscellaneous](#section-miscellaneous) - [▶ `+=` is faster](#--is-faster) - [▶ Let's make a giant string!](#-lets-make-a-giant-string) - [▶ Explicit typecast of strings](#-explicit-typecast-of-strings) @@ -97,22 +98,22 @@ All the examples are structured like below: > ### ▶ 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) @@ -200,10 +201,10 @@ Makes sense, right? + 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 `!`. + * 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) + 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. -+ 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. But since the python bytecode generated after compilation is stored in `.pyc` files, the strings greater than length of 20 are discarded for peephole optimization (Why? Imagine the size of `.pyc` file generated as a result of the expression `'a'*10**10`) ++ 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. --- @@ -241,7 +242,7 @@ some_dict[5] = "Python" True ``` **Note:** Objects with different values may also have same hash (known as hash collision). -* When the statement `some_dict[5] = "Python"` is executed, the existing value "JavaScript" is overwritten with "Python" because Python recongnizes `5` and `5.0` as the same keys of the dictionary `some_dict`. +* When the statement `some_dict[5] = "Python"` is executed, the existing value "JavaScript" is overwritten with "Python" because Python recognizes `5` and `5.0` as the same keys of the dictionary `some_dict`. * This StackOverflow [answer](https://stackoverflow.com/a/32211042/4354153) explains beautifully the rationale behind it. --- @@ -296,16 +297,24 @@ True * But why did the `is` operator evaluated to `False`? Let's see with this snippet. ```py class WTF(object): - def __init__(self): print("I ") - def __del__(self): print("D ") + def __init__(self): print("I") + def __del__(self): print("D") ``` **Output:** ```py >>> WTF() is WTF() - I I D D + I + I + D + D + False >>> id(WTF()) == id(WTF()) - I D I D + I + D + I + D + True ``` As you may observe, the order in which the objects are destroyed is what made all the difference here. @@ -530,7 +539,7 @@ And when the `board` is initialized by multiplying the `row`, this is what happe 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). ```py ->>> board = [(['']*3)*3] # board = = [['']*3 for _ in range(3)] +>>> board = [['']*3 for _ in range(3)] >>> board[0][0] = "X" >>> board [['X', '', ''], ['', '', ''], ['', '', '']] @@ -547,7 +556,7 @@ for x in range(7): def some_func(): return x funcs.append(some_func) - results.append(some_func()) + results.append(some_func()) # note the function call here funcs_results = [func() for func in funcs] ``` @@ -766,10 +775,10 @@ for item in mixed_list: **Output:** ```py ->>> booleans_found_so_far -0 >>> integers_found_so_far 4 +>>> booleans_found_so_far +0 ``` 2\. @@ -830,7 +839,7 @@ class C(A): pass ``` -**Ouptut:** +**Output:** ```py >>> A.x, B.x, C.x (1, 1, 1) @@ -1135,7 +1144,7 @@ str and type(other) is SomeClass and super().__eq__(other) ) - + # When we define a custom __eq__, Python stops automatically inheriting the # __hash__ method, so we need to define it as well __hash__ = str.__hash__ @@ -1148,7 +1157,7 @@ str >>> s = SomeClass('s') >>> some_dict[s] = 40 >>> some_dict - {'s': 40} + {'s': 40, 's': 42} >>> keys = list(some_dict.keys()) >>> type(keys[0]), type(keys[1]) (__main__.SomeClass, str) @@ -1313,7 +1322,7 @@ Shouldn't that be 100? > First, tabs are replaced (from left to right) by one to eight spaces such that the total number of characters up to and including the replacement is a multiple of eight <...> * So the "tab" at the last line of `square` function is replaced with eight spaces, and it gets into the loop. * Python 3 is kind enough to throw an error for such cases automatically. - + **Output (Python 3.x):** ```py TabError: inconsistent use of tabs and spaces in indentation @@ -1697,7 +1706,7 @@ a += [5, 6, 7, 8] * The expression `a = a + [5,6,7,8]` generates a new list and sets `a`'s reference to that new list, leaving `b` unchanged. -* The expression `a + =[5,6,7,8]` is actually mapped to an "extend" function that operates on the list such that `a` and `b` still point to the same list that has been modified in-place. +* The expression `a += [5,6,7,8]` is actually mapped to an "extend" function that operates on the list such that `a` and `b` still point to the same list that has been modified in-place. --- @@ -1731,7 +1740,7 @@ UnboundLocalError: local variable 'a' referenced before assignment a += 1 return a ``` - + **Output:** ```py >>> another_func() @@ -1895,7 +1904,7 @@ Sshh.. It's a super secret. #### 💡 Explanation: + `antigravity` module is one of the few easter eggs released by Python developers. + `import antigravity` opens up a web browser pointing to the [classic XKCD comic](http://xkcd.com/353/) about Python. -+ Well, there's more to it. There's **another easter egg inside the easter egg**. If look at the [code](https://github.com/python/cpython/blob/master/Lib/antigravity.py#L7-L17), there's a function defined that purports to implement the [XKCD's geohashing algorithm](https://xkcd.com/426/). ++ Well, there's more to it. There's **another easter egg inside the easter egg**. If you look at the [code](https://github.com/python/cpython/blob/master/Lib/antigravity.py#L7-L17), there's a function defined that purports to implement the [XKCD's geohashing algorithm](https://xkcd.com/426/). --- @@ -2115,7 +2124,7 @@ AttributeError: 'Yo' object has no attribute '__honey' True ``` -Why did `Yo()._Yo__honey` worked? Only Indian readers would understand. +Why did `Yo()._Yo__honey` work? Only Indian readers would understand. #### 💡 Explanation: @@ -2127,7 +2136,7 @@ Why did `Yo()._Yo__honey` worked? Only Indian readers would understand. --- -## Section: Miscallaneous +## Section: Miscellaneous ### ▶ `+=` is faster @@ -2272,7 +2281,7 @@ nan ### ▶ Minor Ones * `join()` is a string operation instead of list operation. (sort of counter-intuitive at first usage) - + **💡 Explanation:** If `join()` is a method on a string then it can operate on any iterable (list, tuple, iterators). If it were a method on a list, it'd have to be implemented separately by every type. Also, it doesn't make much sense to put a string-specific method on a generic `list` object API. @@ -2350,7 +2359,7 @@ The idea and design for this collection were initially inspired by Denys Dovhan' * 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://www.satwikkansal.xyz/archives/posts/python/My-Python-archives/ +* https://www.codementor.io/satwikkansal/python-practices-for-efficient-code-performance-memory-and-usability-aze6oiq65 # 🎓 License @@ -2365,12 +2374,12 @@ The idea and design for this collection were initially inspired by Denys Dovhan' If you have any wtfs, ideas or suggestions, please share. -## Want to share wtfpython with friends? +## Surprise your geeky pythonist friends? -You can use these quick links for Twitter and Linkedin. +You can use these quick links to recommend wtfpython 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/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.) ## Need a pdf version? From bc68ae0ff32480c715f08800ece3bd0d709be912 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 8 Jun 2019 00:23:33 +0530 Subject: [PATCH 07/29] Update evaluation time discrepency example: Add another snippet Resolves https://github.com/satwikkansal/wtfpython/issues/41 --- README.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d4de4d7..116032d 100644 --- a/README.md +++ b/README.md @@ -408,7 +408,7 @@ for i, some_dict[i] in enumerate(some_string): --- -### ▶ Evaluation time discrepancy +### ▶ Evaluation time discrepancy ^ 1\. ```py @@ -444,6 +444,23 @@ array_2[:] = [1,2,3,4,5] [1,2,3,4,5] ``` +3\. + +```py +array_3 = [1, 2, 3] +array_4 = [10, 20, 30] +g = (i + j for i in array_3 for j in array_4) + +array_3 = [4, 5, 6] +array_4 = [400, 500, 600] +``` + +**Output:** +```py +>>> print(list(g)) +[401, 501, 601, 402, 502, 602, 403, 503, 603] +``` + #### 💡 Explanation - In a [generator](https://wiki.python.org/moin/Generators) expression, the `in` clause is evaluated at declaration time, but the conditional clause is evaluated at runtime. @@ -451,6 +468,9 @@ array_2[:] = [1,2,3,4,5] - The differences in the output of `g1` and `g2` in the second part is due the way variables `array_1` and `array_2` are re-assigned values. - In the first case, `array_1` is binded to the new object `[1,2,3,4,5]` and since the `in` clause is evaluated at the declaration time it still refers to the old object `[1,2,3,4]` (which is not destroyed). - In the second case, the slice assignment to `array_2` updates the same old object `[1,2,3,4]` to `[1,2,3,4,5]`. Hence both the `g2` and `array_2` still have reference to the same object (which has now been updated to `[1,2,3,4,5]`). +- Okay, going by the logic discussed so far, shouldn't be the value of `list(g)` in the third snippet be `[11, 21, 31, 12, 22, 32, 13, 23, 33]`? (because `array_3` and `array_4` are going to behave just like `array_1`). The reason why (only) `array_4` values got updated is explained in [PEP-289](https://www.python.org/dev/peps/pep-0289/#the-details) + > Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run. + --- @@ -1221,7 +1241,7 @@ a, b = a[b] = {}, 5 and > An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right. - + * The `+` in `(target_list "=")+` means there can be **one or more** target lists. In this case, target lists are `a, b` and `a[b]` (note the expression list is exactly one, which in our case is `{}, 5`). * After the expression list is evaluated, it's value is unpacked to the target lists from **left to right**. So, in our case, first the `{}, 5` tuple is unpacked to `a, b` and we now have `a = {}` and `b = 5`. From e0cb30bf3be6cd0e9988a0530536f239dfc9d6a7 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 8 Jun 2019 00:41:58 +0530 Subject: [PATCH 08/29] Modifying a dictionary while iterating over it: Add note Resolves https://github.com/satwikkansal/wtfpython/issues/53 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 116032d..8378981 100644 --- a/README.md +++ b/README.md @@ -469,8 +469,8 @@ array_4 = [400, 500, 600] - In the first case, `array_1` is binded to the new object `[1,2,3,4,5]` and since the `in` clause is evaluated at the declaration time it still refers to the old object `[1,2,3,4]` (which is not destroyed). - In the second case, the slice assignment to `array_2` updates the same old object `[1,2,3,4]` to `[1,2,3,4,5]`. Hence both the `g2` and `array_2` still have reference to the same object (which has now been updated to `[1,2,3,4,5]`). - Okay, going by the logic discussed so far, shouldn't be the value of `list(g)` in the third snippet be `[11, 21, 31, 12, 22, 32, 13, 23, 33]`? (because `array_3` and `array_4` are going to behave just like `array_1`). The reason why (only) `array_4` values got updated is explained in [PEP-289](https://www.python.org/dev/peps/pep-0289/#the-details) + > Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run. - --- @@ -1423,7 +1423,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. -* For more information, you may refer to this StackOverflow [thread](https://stackoverflow.com/questions/44763802/bug-in-python-dict) explaining a similar example in detail. +* 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. --- From 092dd096bfc8cf9ceebef444597958ee5f4c0b98 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 8 Jun 2019 02:53:53 +0530 Subject: [PATCH 09/29] Expand strings and backslashes Resolves https://github.com/satwikkansal/wtfpython/issues/69 --- README.md | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 8378981..05bbe15 100644 --- a/README.md +++ b/README.md @@ -469,7 +469,7 @@ array_4 = [400, 500, 600] - In the first case, `array_1` is binded to the new object `[1,2,3,4,5]` and since the `in` clause is evaluated at the declaration time it still refers to the old object `[1,2,3,4]` (which is not destroyed). - In the second case, the slice assignment to `array_2` updates the same old object `[1,2,3,4]` to `[1,2,3,4,5]`. Hence both the `g2` and `array_2` still have reference to the same object (which has now been updated to `[1,2,3,4,5]`). - Okay, going by the logic discussed so far, shouldn't be the value of `list(g)` in the third snippet be `[11, 21, 31, 12, 22, 32, 13, 23, 33]`? (because `array_3` and `array_4` are going to behave just like `array_1`). The reason why (only) `array_4` values got updated is explained in [PEP-289](https://www.python.org/dev/peps/pep-0289/#the-details) - + > Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run. --- @@ -702,30 +702,47 @@ SyntaxError: invalid syntax --- -### ▶ Backslashes at the end of string +### ▶ Strings and the backslashes\ **Output:** -``` ->>> print("\\ C:\\") -\ C:\ ->>> print(r"\ C:") -\ C: ->>> print(r"\ C:\") +```py +>>> print("\"") +" - File "", line 1 - print(r"\ C:\") - ^ +>>> print(r"\"") +\" + +>>> print(r"\") +File "", line 1 + print(r"\") + ^ SyntaxError: EOL while scanning string literal + +>>> r'\'' == "\\'" +True ``` #### 💡 Explanation -- In a raw string literal, as indicated by the prefix `r`, the backslash doesn't have the special meaning. - ```py - >>> print(repr(r"wt\"f")) - 'wt\\"f' - ``` -- What the interpreter actually does, though, is simply change the behavior of backslashes, so they pass themselves and the following character through. That's why backslashes don't work at the end of a raw string. +- In a normal python string, the backslash is used to escape characters that may have special meaning (like single-quote, double-quote and the backslash itself). + ```py + >>> 'wt\"f' + 'wt"f' + ``` +- In a raw string literal (as indicated by the prefix `r`), the backslashes pass themselves as is along with the behavior of escaping the following character. + ```py + >>> r'wt\"f' == 'wt\\"f' + True + >>> print(repr(r'wt\"f') + 'wt\\"f' + + >>> print("\n") + + + >>> print(r"\\n") + '\\\\n' + ``` +- This means when a parser encounters a backslash in a raw string, it expects another character following it. And in our case (`print(r"\")`), the backslash escaped the trailing quote, leaving the parser without a terminating quote (hence the `SyntaxError`). That's why backslashes don't work at the end of a raw string. --- From c52919b9ec6b3c945b30730a786af15ad9507ca3 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 8 Jun 2019 14:21:19 +0530 Subject: [PATCH 10/29] Add new snippet: Splitsies Resolves https://github.com/satwikkansal/wtfpython/issues/80 --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 05bbe15..2947bb7 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,44 @@ Makes sense, right? --- +### ▶ Splitsies ^ + +```py +>>> 'a'.split() +['a'] + +# is same as +>>> 'a'.split(' ') +['a'] + +# but +>>> len(''.split()) +0 + +# isn't the same as +>>> len(''.split(' ')) +1 +``` + +#### 💡 Explanation: + +- It might appear at first that the default seperator for split is a single space `' '`, but as per the [docs](https://docs.python.org/2.7/library/stdtypes.html#str.split), + > If sep is not specified or is None, a different splitting algorithm is applied: runs of consecutive whitespace are regarded as a single separator, and the result will contain no empty strings at the start or end if the string has leading or trailing whitespace. Consequently, splitting an empty string or a string consisting of just whitespace with a None separator returns `[]`. + > If sep is given, consecutive delimiters are not grouped together and are deemed to delimit empty strings (for example, `'1,,2'.split(',')` returns `['1', '', '2']`). Splitting an empty string with a specified separator returns `['']`. +- Noticing how the leading and trailing whitespaces are handled in the following snippet will make things clear, + ```py + >>> ' a '.split(' ') + ['', 'a', ''] + >>> ' a '.split() + ['a'] + >>> ''.split(' ') + [''] + ``` + +--- + + + ### ▶ Time for some hash brownies! 1\. @@ -702,7 +740,7 @@ SyntaxError: invalid syntax --- -### ▶ Strings and the backslashes\ +### ▶ Strings and the backslashes\ ^ **Output:** ```py From a33f715b2620e60d1ae2a15c998e907e8d5e4d17 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 8 Jun 2019 14:49:59 +0530 Subject: [PATCH 11/29] Let's meet Friendly Language Uncle For Life: Add notes for non-interactive evn Resolves https://github.com/satwikkansal/wtfpython/issues/94 --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2947bb7..dc6fd7f 100644 --- a/README.md +++ b/README.md @@ -2071,7 +2071,7 @@ Braces? No way! If you think that's disappointing, use Java. --- -### ▶ Let's meet Friendly Language Uncle For Life * +### ▶ Let's meet Friendly Language Uncle For Life ^ **Output (Python 3.x)** ```py @@ -2094,6 +2094,11 @@ 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 on 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, + ```py + from __future__ import barry_as_FLUFL + print(eval('"Ruby" <> "Python"')) + ``` --- From 85352fc45d80f20d46fc4d6cb5a0cea483408089 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 8 Jun 2019 16:30:53 +0530 Subject: [PATCH 12/29] Add example: Ellipsis Resolves https://github.com/satwikkansal/wtfpython/issues/96 --- README.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/README.md b/README.md index dc6fd7f..e32e1ce 100644 --- a/README.md +++ b/README.md @@ -2203,6 +2203,66 @@ Try block executed successfully... - `else` clause after try block is also called "completion clause" as reaching the `else` clause in a `try` statement means that the try block actually completed successfully. --- +### ▶ Ellipsis ^ + +```py +def some_func(): + Ellipsis +``` + +**Output** +```py +>>> some_func() +# No output, No Error + +>>> SomeRandomString +Traceback (most recent call last): + File "", line 1, in +NameError: name 'SomeRandomString' is not defined + +>>> Ellipsis +Ellipsis +``` + +#### 💡 Explanation +- In Python, `Ellipsis` is a globally available builtin object which is equivalent to `...`. + ```py + >>> ... + Ellipsis + ``` +- Eliipsis can be used for several purposes, + + As a placeholder for code that hasn't been written yet (just like `pass` statement) + + In slicing syntax to represent the full slices in remaining direction + ```py + >>> import numpy as np + >>> three_dimensional_array = np.arange(8).reshape(2, 2, 2) + array([ + [ + [0, 1], + [2, 3] + ], + + [ + [4, 5], + [6, 7] + ] + ]) + ``` + So our `three_dimensional_array` is an array of array of arrays. Let's say we want to print the second element (index `1`) of all the innermost arrays, we can use Ellipsis to bypass all the preceding dimensions + ```py + >>> three_dimensional_array[:,:,1] + array([[1, 3], + [5, 7]]) + >>> three_dimensional_array[..., 1] # using Ellipsis. + array([[1, 3], + [5, 7]]) + ``` + Note: this will work for any number of dimensions. You can even select slice in first and last dimension and ignore the middle ones this way (`n_dimensional_array[firs_dim_slice, ..., last_dim_slice]`) + + In [type hinting](https://docs.python.org/3/library/typing.html) to indicate only a part of the type (like `(Callable[..., int]` or `Tuple[str, ...]`)) + + You may also use Ellipsis as a default function argument (in the cases when you want to differentiate between the "no argument passed" and "None value passed" scenarios). + + +--- ### ▶ Inpinity * From 72e39d3248facb57b11f01f30dee8f99cda6548d Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 8 Jun 2019 23:25:37 +0530 Subject: [PATCH 13/29] Update example "is is not what it is" Related to https://github.com/satwikkansal/wtfpython/issues/100 --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cbe9c68..72e1ebd 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ True >>> a is b False ->>> a, b = "wtf!", "wtf!" +>>> a = "wtf!"; b = "wtf!"; >>> a is b True ``` @@ -513,10 +513,11 @@ array_4 = [400, 500, 600] --- -### ▶ `is` is not what it is! +### ▶ Messing around with `is` operator^ The following is a very famous example present all over the internet. +1\. ```py >>> a = 256 >>> b = 256 @@ -527,6 +528,37 @@ True >>> b = 257 >>> a is b False +``` + +2\. + +```py +>>> a = [] +>>> b = [] +>>> a is b +False + +>>> a = tuple() +>>> b = tuple() +>>> a is b +True +``` + +3\. +**Output (< Python 3.7)** +``` +>>> a, b = 257, 257 +True + +>>> a = 257; b = 257 +>>> a is b +True +``` + +**Output (Python 3.7)** +``` +>>> a, b = 257, 257 +False >>> a = 257; b = 257 >>> a is b @@ -541,9 +573,8 @@ True * `==` operator compares the values of both the operands and checks if they are the same. * So `is` is for reference equality and `==` is for value equality. An example to clear things up, ```py - >>> [] == [] - True - >>> [] is [] # These are two empty lists at two different memory locations. + >>> class A: pass + >>> A() is A() # These are two empty objects at two different memory locations. False ``` @@ -575,8 +606,11 @@ Quoting from https://docs.python.org/3/c-api/long.html Here the interpreter isn't smart enough while executing `y = 257` to recognize that we've already created an integer of the value `257,` and so it goes on to create another object in the memory. +Similar optimization applies to other **immutable** objects like empty tuples as well. Since lists are mutable, that's why `[] is []` will return `False` and `() is ()` will return `True`. This explains our second snippet. Let's move on to the third one, + **Both `a` and `b` refer to the same object when initialized with same value in the same line.** +**Output (< Python 3.7)** ```py >>> a, b = 257, 257 >>> id(a) @@ -593,6 +627,7 @@ Here the interpreter isn't smart enough while executing `y = 257` to recognize t * When a and b are set to `257` 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 `257` as an object. * It's a compiler optimization and specifically applies to the interactive environment. When you enter two lines in a live interpreter, they're compiled separately, therefore optimized separately. If you were to try this example in a `.py` file, you would not see the same behavior, because the file is compiled all at once. +* 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. --- @@ -776,6 +811,7 @@ True 'wt\\"f' >>> print("\n") + ``` >>> print(r"\\n") @@ -2262,8 +2298,7 @@ Ellipsis + In [type hinting](https://docs.python.org/3/library/typing.html) to indicate only a part of the type (like `(Callable[..., int]` or `Tuple[str, ...]`)) + You may also use Ellipsis as a default function argument (in the cases when you want to differentiate between the "no argument passed" and "None value passed" scenarios). - ---- +--- ### ▶ Inpinity * From 9ea7ab0c5a05cbafc41c4432293202a7836d908f Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 9 Jun 2019 00:07:02 +0530 Subject: [PATCH 14/29] New example: The chicken-egg problem Closes https://github.com/satwikkansal/wtfpython/issues/101 --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index 72e1ebd..c9b68ee 100644 --- a/README.md +++ b/README.md @@ -730,6 +730,54 @@ Even when the values of `x` were different in every iteration prior to appending --- +### ▶ The chicken-egg problem ^ + +1\. +```py +>>> isinstance(3, int) +True +>>> isinstance(type, object) +True +>>> isinstance(object, type) +True +``` + +2\. So which is the ultimate, base class? And wait, there's more to the confusion + +```py +>>> class A: pass +>>> isinstance(A, A) +False +>>> isinstance(type, type) +True +>>> isinstance(object, object) +True +``` + +3\. + +```py +>>> issubclass(int, object) +True +>>> issubclass(type, object) +True +>>> issubclass(object, type) +False +``` + + +#### 💡 Explanation + +- `type` is a [metaclass](https://realpython.com/python-metaclasses/) in Python. +- **Everything** is an `object` in Python, which includes classes as well as their objects (instances). +- class `type` is the metaclass of class `object`, and every class (including `type`) has inherited directly or indirectly from `object`. +- There is no real base class among `object` and `type`. The confusion in the above snippets is arising because we're thinking these relationships (`issubclass` and `isinstance`) in terms of Python classes. The relationship between `object` and `type` can't be reproduced in pure python. To be more precise the following relationships can't be reproduced in pure Python, + + class A is instance of class B, and class B is an instance of class A. + + class A is an instance of itself. +- These relationships between `object` and `type` (both being instances of eachother as well as themselves) exist in Python because of "cheating" at implementation level. + +--- + ### ▶ `is not ...` is not `is (not ...)` ```py From 2bfa6d2e5bb123a177d6c4c4c20a20ac3f822fca Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 9 Jun 2019 03:59:09 +0530 Subject: [PATCH 15/29] Let's make a giant string: Use %timeit for measuring time Fixes https://github.com/satwikkansal/wtfpython/issues/106 --- README.md | 63 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index c9b68ee..f9a22ab 100644 --- a/README.md +++ b/README.md @@ -2450,40 +2450,47 @@ def convert_list_to_string(l, iters): ``` **Output:** + ```py ->>> timeit(add_string_with_plus(10000)) -1000 loops, best of 3: 972 µs per loop ->>> timeit(add_bytes_with_plus(10000)) -1000 loops, best of 3: 815 µs per loop ->>> timeit(add_string_with_format(10000)) -1000 loops, best of 3: 508 µs per loop ->>> timeit(add_string_with_join(10000)) -1000 loops, best of 3: 878 µs per loop ->>> l = ["xyz"]*10000 ->>> timeit(convert_list_to_string(l, 10000)) -10000 loops, best of 3: 80 µs per loop +# Executed in ipython shell using %timeit for better readablity of results. +# You can also use the timeit module in normal python shell/scriptm=, example usage below +# timeit.timeit('add_string_with_plus(10000)', number=1000, globals=globals()) + +>>> NUM_ITERS = 1000 +>>> %timeit -n1000 add_string_with_plus(NUM_ITERS) +124 µs ± 4.73 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) +>>> %timeit -n1000 add_bytes_with_plus(NUM_ITERS) +211 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) +>>> %timeit -n1000 add_string_with_format(NUM_ITERS) +61 µs ± 2.18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) +>>> %timeit -n1000 add_string_with_join(NUM_ITERS) +117 µs ± 3.21 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) +>>> l = ["xyz"]*NUM_ITERS +>>> %timeit -n1000 convert_list_to_string(l, NUM_ITERS) +10.1 µs ± 1.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) ``` Let's increase the number of iterations by a factor of 10. ```py ->>> timeit(add_string_with_plus(100000)) # Linear increase in execution time -100 loops, best of 3: 9.75 ms per loop ->>> timeit(add_bytes_with_plus(100000)) # Quadratic increase -1000 loops, best of 3: 974 ms per loop ->>> timeit(add_string_with_format(100000)) # Linear increase -100 loops, best of 3: 5.25 ms per loop ->>> timeit(add_string_with_join(100000)) # Linear increase -100 loops, best of 3: 9.85 ms per loop ->>> l = ["xyz"]*100000 ->>> timeit(convert_list_to_string(l, 100000)) # Linear increase -1000 loops, best of 3: 723 µs per loop +>>> NUM_ITERS = 10000 +>>> %timeit -n1000 add_string_with_plus(NUM_ITERS) # Linear increase in execution time +1.26 ms ± 76.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) +>>> %timeit -n1000 add_bytes_with_plus(NUM_ITERS) # Quadratic increase +6.82 ms ± 134 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) +>>> %timeit -n1000 add_string_with_format(NUM_ITERS) # Linear increase +645 µs ± 24.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) +>>> %timeit -n1000 add_string_with_join(NUM_ITERS) # Linear increase +1.17 ms ± 7.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) +>>> l = ["xyz"]*NUM_ITERS +>>> %timeit -n1000 convert_list_to_string(l, NUM_ITERS) # Linear increase +86.3 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) ``` #### 💡 Explanation -- You can read more about [timeit](https://docs.python.org/3/library/timeit.html) from here. It is generally used to measure the execution time of snippets. +- You can read more about [timeit](https://docs.python.org/3/library/timeit.html) or [%timeit](https://ipython.org/ipython-doc/dev/interactive/magics.html#magic-timeit) on these links. They are used to measure the execution time of code pieces. - Don't use `+` for generating long strings — In Python, `str` is immutable, so the left and right strings have to be copied into the new string for every pair of concatenations. If you concatenate four strings of length 10, you'll be copying (10+10) + ((10+10)+10) + (((10+10)+10)+10) = 90 characters instead of just 40 characters. Things get quadratically worse as the number and size of the string increases (justified with the execution times of `add_bytes_with_plus` function) -- Therefore, it's advised to use `.format.` or `%` syntax (however, they are slightly slower than `+` for short strings). +- Therefore, it's advised to use `.format.` or `%` syntax (however, they are slightly slower than `+` for very short strings). - Or better, if already you've contents available in the form of an iterable object, then use `''.join(iterable_object)` which is much faster. - `add_string_with_plus` didn't show a quadratic increase in execution time unlike `add_bytes_with_plus` because of the `+=` optimizations discussed in the previous example. Had the statement been `s = s + "x" + "y" + "z"` instead of `s += "xyz"`, the increase would have been quadratic. ```py @@ -2493,10 +2500,10 @@ Let's increase the number of iterations by a factor of 10. s = s + "x" + "y" + "z" assert len(s) == 3*iters - >>> timeit(add_string_with_plus(10000)) - 100 loops, best of 3: 9.87 ms per loop - >>> timeit(add_string_with_plus(100000)) # Quadratic increase in execution time - 1 loops, best of 3: 1.09 s per loop + >>> %timeit -n100 add_string_with_plus(1000) + 388 µs ± 22.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) + >>> %timeit -n100 add_string_with_plus(10000) # Quadratic increase in execution time + 9 ms ± 298 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) ``` --- From 80a969a556f2a018941e537031367b1cca30a37a Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 9 Jun 2019 17:34:50 +0530 Subject: [PATCH 16/29] Brace yourself: Add to explanation Resolves https://github.com/satwikkansal/wtfpython/issues/114 --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f9a22ab..fbba80a 100644 --- a/README.md +++ b/README.md @@ -2148,11 +2148,13 @@ from __future__ import braces SyntaxError: not a chance ``` -Braces? No way! If you think that's disappointing, use Java. +Braces? No way! If you think that's disappointing, use Java. Okay, another surprising thing, can you find where's the `SyntaxError` raised in `__future__` module [code](https://github.com/python/cpython/blob/master/Lib/__future__.py)? #### 💡 Explanation: -+ The `__future__` module is normally used to provide features from future versions of Python. The "future" here is however ironic. ++ The `__future__` module is normally used to provide features from future versions of Python. The "future" in this specific context is however ironic. + This is an easter egg concerned with the community's feelings on this issue. ++ The code is actually present [here](https://github.com/python/cpython/blob/025eb98dc0c1dc27404df6c544fc2944e0fa9f3a/Python/future.c#L49) in `future.c` file. ++ When the CPython compiler encounters a [future statement](https://docs.python.org/3.3/reference/simple_stmts.html#future-statements), it first runs the appropriate code in `future.c` before treating it as a normal import statement. --- From bf967b0332ff2c5d645ab4f420f1c24a9b0702db Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 9 Jun 2019 17:56:36 +0530 Subject: [PATCH 17/29] String can be tricky: Remove outdated snippet Related to https://github.com/satwikkansal/wtfpython/issues/120 --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fbba80a..71088cd 100644 --- a/README.md +++ b/README.md @@ -179,13 +179,12 @@ True >>> b = "wtf!" >>> a is b False - ->>> a = "wtf!"; b = "wtf!"; ->>> a is b -True ``` 3\. + +**Output (< Python3.7 )** + ```py >>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa' True @@ -203,8 +202,8 @@ Makes sense, right? * 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) -+ 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. + 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. ++ 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 third snippet doesn't work for Python 3.7. You can read more about the change [here](https://bugs.python.org/issue11549). --- From 4d5a2f0d97d4dc348ad5f0754855268c6daa99dd Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 9 Jun 2019 19:45:13 +0530 Subject: [PATCH 18/29] New example: Lossy zip of iterators Resolves https://github.com/satwikkansal/wtfpython/issues/121 --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/README.md b/README.md index 71088cd..c8de242 100644 --- a/README.md +++ b/README.md @@ -1251,6 +1251,55 @@ I've lost faith in truth! --- +### ▶ Lossy zip of iterators + +```py +>>> numbers = list(range(7)) +>>> numbers +[0, 1, 2, 3, 4, 5, 6] +>>> first_three, remaining = numbers[:3], numbers[3:] +>>> first_three, remaining +([0, 1, 2], [3, 4, 5, 6]) +>>> numbers_iter = iter(numbers) +>>> list(zip(numbers_iter, first_three)) +[(0, 0), (1, 1), (2, 2)] +# so far so good, let's zip the remaining +>>> list(zip(numbers_iter, remaining)) +[(4, 3), (5, 4), (6, 5)] +``` +Where did element `3` go from the `numbers` list? + +#### 💡 Explanation: + +- From Python [docs](https://docs.python.org/3.3/library/functions.html#zip), here's an approximate implementation of zip function, + ```py + def zip(*iterables): + sentinel = object() + iterators = [iter(it) for it in iterables] + while iterators: + result = [] + for it in iterators: + elem = next(it, sentinel) + if elem is sentinel: + return + result.append(elem) + yield tuple(result) + ``` +- So the function takes in arbitrary number of itreable objects, adds each of their items to the `result` list by calling the `next` function on them, and stops whenever any of the iterable is exhausted. +- The caveat here is when any iterable is exhausted, the existing elements in the `result` list are discarded. That's what happened with `3` in the `numbers_iter`. +- The correct way to do the above using `zip` would be, + ```py + >>> numbers = list(range(7)) + >>> numbers_iter = iter(numbers) + >>> list(zip(first_three, numbers_iter)) + [(0, 0), (1, 1), (2, 2)] + >>> list(zip(remaining, numbers_iter)) + [(3, 3), (4, 4), (5, 5), (6, 6)] + ``` + The first argument of zip should be the one with fewest elements. + +--- + ### ▶ From filled to None in one instruction... ```py From 362447a5d252c3be58d76d68285bde9d077c87de Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 10 Jun 2019 00:14:01 +0530 Subject: [PATCH 19/29] Add new example: Disorder within order Resolves https://github.com/satwikkansal/wtfpython/issues/77 --- README.md | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c8de242..27995d3 100644 --- a/README.md +++ b/README.md @@ -261,26 +261,131 @@ some_dict[5] = "Python" "Ruby" >>> some_dict[5.0] "Python" ->>> some_dict[5] +>>> some_dict[5] # "Python" destroyed the existence of "JavaScript"? +"Python" + +>>> complex_five = 5 + 0j +>>> type(complex_five) +complex +>>> some_dict[complex_five] "Python" ``` -"Python" destroyed the existence of "JavaScript"? +So, why is Python all over the place? + #### 💡 Explanation * Python dictionaries check for equality and compare the hash value to determine if two keys are the same. * Immutable objects with same value always have the same hash in Python. ```py - >>> 5 == 5.0 + >>> 5 == 5.0 == 5 + 0j True - >>> hash(5) == hash(5.0) + >>> hash(5) == hash(5.0) == hash(5 + 0j) True ``` **Note:** Objects with different values may also have same hash (known as hash collision). * When the statement `some_dict[5] = "Python"` is executed, the existing value "JavaScript" is overwritten with "Python" because Python recognizes `5` and `5.0` as the same keys of the dictionary `some_dict`. * This StackOverflow [answer](https://stackoverflow.com/a/32211042/4354153) explains beautifully the rationale behind it. +--- + +### ▶ The disorder within order ^ + +```py +from collections import OrderedDict + +dictionary = dict() +dictionary[1] = 'a'; dictionary[2] = 'b'; + +ordered_dict = OrderedDict() +ordered_dict[1] = 'a'; ordered_dict[2] = 'b'; + +another_ordered_dict = OrderedDict() +another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a'; + +class DictWithHash(dict): + """ + A dict that also implements __hash__ magic. + """ + __hash__ = lambda self: 0 + +class OrderedDictWithHash(OrderedDict): + """ + A dict that also implements __hash__ magic. + """ + __hash__ = lambda self: 0 +``` + +**Output** +```py +>>> dictionary == ordered_dict # If a == b +True +>>> dictionary == another_ordered_dict # and b == c +True +>>> ordered_dict == another_ordered_dict # the why isn't c == a ?? +False + +# We all know that a set consists of only unique elements, +# let's try making a set of these dictionaries and see what happens... + +>>> len({dictionary, ordered_dict, another_ordered_dict}) +Traceback (most recent call last): + File "", line 1, in +TypeError: unhashable type: 'dict' + +# Makes sense since dict don't have __hash__ implemented, let's use +# our wrapper classes. +>>> dictionary = DictWithHash() +>>> dictionary[1] = 'a'; dictionary[2] = 'b'; +>>> ordered_dict = OrderedDictWithHash() +>>> ordered_dict[1] = 'a'; ordered_dict[2] = 'b'; +>>> another_ordered_dict = OrderedDictWithHash() +>>> another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a'; +>>> len({dictionary, ordered_dict, another_ordered_dict}) +1 +>>> len({ordered_dict, another_ordered_dict, dictionary}) # changing the order +2 +``` + +What is going on here? + +#### 💡 Explanation: + +- The reason why intransitive equality didn't hold among `dictionary`, `ordered_dict` and `another_ordered_dict` is because of the way `__eq__` method is implemented in `OrderedDict` class. From the [docs](https://docs.python.org/3/library/collections.html#ordereddict-objects) + > Equality tests between OrderedDict objects are order-sensitive and are implemented as `list(od1.items())==list(od2.items())`. Equality tests between `OrderedDict` objects and other Mapping objects are order-insensitive like regular dictionaries. +- The reason for this equality is behavior is that it allows `OrderedDict` objects to be directly substituted anywhere a regular dictionary is used. +- Okay, so why did changing the order affect the lenght of the generated `set` object? The answer is the lack of intransitive equality only. Since sets are "unordered" collections of unique elements, the order in which elements are inserted shouldn't matter. But in this case, it does matter. Let's break it down a bit, + ```py + >>> some_set = set() + >>> some_set.add(dictionary) # these are the mapping objects from the snippets above + >>> ordered_dict in some_set + True + >>> some_set.add(ordered_dict) + >>> len(some_set) + 1 + >>> another_ordered_dict in some_set + True + >>> some_set.add(another_ordered_dict) + >>> len(some_set) + 1 + + >>> another_set = set() + >>> another_set.add(ordered_dict) + >>> another_ordered_dict in another_set + False + >>> another_set.add(another_ordered_dict) + >>> len(another_set) + 2 + >>> dictionary in another_set + True + >>> another_set.add(another_ordered_dict) + >>> len(another_set) + 2 + ``` + So the inconsistency is due to `another_ordered_dict in another_set` being False because `ordered_dict` was already present in `another_set` and as observed before, `ordered_dict == another_ordered_dict` is `False`. + + --- ### ▶ Keep trying? * @@ -858,8 +963,6 @@ True 'wt\\"f' >>> print("\n") - ``` - >>> print(r"\\n") '\\\\n' From c6e0d795ff712032144c4d795d6eaa8a5b0df7d0 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 10 Jun 2019 23:22:08 +0530 Subject: [PATCH 20/29] Some minor changes, and add assertion example --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 27995d3..6283d3e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Python, being a beautifully designed high-level and interpreter-based programmin 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 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! 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: @@ -104,10 +104,10 @@ All the examples are structured like below: > # Preparation for the magic... > ``` > -> **Output (Python version):** +> **Output (Python version(s)):** > ```py > >>> triggering_statement -> Probably unexpected output +> Some unexpected output > ``` > (Optional): One line describing the unexpected output. > @@ -118,7 +118,7 @@ All the examples are structured like below: > ```py > Setting up examples for clarification (if necessary) > ``` -> **Output:** +> **Output Output (Python version(s)):** > ```py > >>> trigger # some example that makes it easy to unveil the magic > # some justified output @@ -128,15 +128,15 @@ All the examples are structured like below: # Usage -A nice way to get the most out of these examples, in my opinion, will be just to read the examples chronologically, and for every example: +A nice way to get the most out of these examples, in my opinion, will be to just read them chronologically, and for every example: - Carefully read the initial code for setting up the example. If you're an experienced Python programmer, most of the times you will successfully anticipate what's going to happen next. - 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 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)). - 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 WTFPython at the command line. There's a pypi package and an npm package (which supports colored formatting) for the same. To install the npm package [`wtfpython`](https://www.npmjs.com/package/wtfpython) ```sh @@ -2185,7 +2185,10 @@ class SomeClass: --- -### ▶ Needle in a Haystack + +--- + +### ▶ Needles in a Haystack 1\. ```py @@ -2224,13 +2227,45 @@ e tuple() ``` +3\. Not asserting strongly enough + +```py +a = "python" +b = "javascript" +``` +**Output:** +```py +# An assert statement with an assertion failure message. +>>> assert(a == b, "Both languages are different") +# No AssertionError is raised +``` + #### 💡 Explanation: * For 1, the correct statement for expected behavior is `x, y = (0, 1) if True else (None, None)`. * For 2, the correct statement for expected behavior is `t = ('one',)` or `t = 'one',` (missing comma) otherwise the interpreter considers `t` to be a `str` and iterates over it character by character. * `()` is a special token and denotes empty `tuple`. +* For 3, no `AssertionError` was raised because we're asserting entire tuple, instead of asserting the individual expression `a == b`. The following snippet will clear things up, + ```py + >>> a = "python" + >>> b = "javascript" + >>> assert a == b + Traceback (most recent call last): + File "", line 1, in + AssertionError + + >>> assert (a == b, "Values are not equal") + :1: SyntaxWarning: assertion is always true, perhaps remove parentheses? + + >>> assert a == b, "Values are not equal" + Traceback (most recent call last): + File "", line 1, in + AssertionError: Values aren not equal + ``` --- + + --- @@ -2658,6 +2693,8 @@ Let's increase the number of iterations by a factor of 10. >>> %timeit -n100 add_string_with_plus(10000) # Quadratic increase in execution time 9 ms ± 298 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) ``` +- So many ways to format and create a giant string are somewhat in contrast to the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), according to which, + > There should be one-- and preferably only one --obvious way to do it. --- From 8b4bbb219658477480474198a6b462b49aa73c8f Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Tue, 11 Jun 2019 00:43:32 +0530 Subject: [PATCH 21/29] Add new example: Wild imports --- README.md | 115 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 6283d3e..841d40a 100644 --- a/README.md +++ b/README.md @@ -1856,21 +1856,22 @@ print(x, ': x in global') ``` 3\. -``` -x = 1 -print([x for x in range(5)]) -print(x, ': x in global') -``` -**Output (on Python 2.x):** +**Output (Python 2.x):** ``` +>>> x = 1 +>>> print([x for x in range(5)]) [0, 1, 2, 3, 4] +>>> print(x, ': x in global') (4, ': x in global') ``` -**Output (on Python 3.x):** +**Output (Python 3.x):** ``` +>>> x = 1 +>>> print([x for x in range(5)]) [0, 1, 2, 3, 4] +>>> print(x, ': x in global') 1 : x in global ``` @@ -2183,12 +2184,10 @@ class SomeClass: - A generator expression has its own scope. - Starting from Python 3.X, list comprehensions also have their own scope. ---- - --- -### ▶ Needles in a Haystack +### ▶ Needles in a Haystack ^ 1\. ```py @@ -2227,7 +2226,31 @@ e tuple() ``` -3\. Not asserting strongly enough +3\. + +``` +ten_words_list = [ + "some", + "very", + "big", + "list", + "that" + "consists", + "of", + "exactly", + "ten", + "words" +] +``` + +**Output** + +```py +>>> len(ten_words_list) +9 +``` + +4\. Not asserting strongly enough ```py a = "python" @@ -2244,7 +2267,12 @@ b = "javascript" * For 1, the correct statement for expected behavior is `x, y = (0, 1) if True else (None, None)`. * For 2, the correct statement for expected behavior is `t = ('one',)` or `t = 'one',` (missing comma) otherwise the interpreter considers `t` to be a `str` and iterates over it character by character. * `()` is a special token and denotes empty `tuple`. -* For 3, no `AssertionError` was raised because we're asserting entire tuple, instead of asserting the individual expression `a == b`. The following snippet will clear things up, +* In 3, as you might have already figured out, there's a missing comma after 5th element (`"that"`) in the list. So by implicit string literal concatenation, + ```py + >>> ten_words_list + ['some', 'very', 'big', 'list', 'thatconsists', 'of', 'exactly', 'ten', 'words'] + ``` +* No `AssertionError` was raised in 4th snippet because instead of asserting the individual expression `a == b`, we're asserting entire tuple. The following snippet will clear things up, ```py >>> a = "python" >>> b = "javascript" @@ -2264,7 +2292,60 @@ b = "javascript" --- +### ▶ Wild imports +```py +# File: module.py + +def some_weird_name_func_(): + print("works!") + +def _another_weird_name_func(): + print("works!") + +``` + +**Output** + +```py +>>> from module import * +>>> some_weird_name_func_() +"works!" +>>> _another_weird_name_func() +Traceback (most recent call last): + File "", line 1, in +NameError: name '_another_weird_name_func' is not defined +``` + +#### 💡 Explanation: + +- It if often adivsable to not use wildcard imports. In wildcard imports, the names with leading underscore are be imported. This may lead to errors in runtime. +- Had we used `from ... import a, b, c` syntax, the above `NameError` won't have occurred. + ```py + >>> from module import some_weird_name_func_, _another_weird_name_func + >>> _another_weird_name_func() + works! + ``` +- If you really want to use wildcard imports, then you'd have to define the list `__all__` in your module that will contain a list of public objects that'll be available when we do wildcard imports. +```py +__all__ = ['_another_weird_name_func'] + +def some_weird_name_func_(): + print("works!") + +def _another_weird_name_func(): + print("works!") +``` +**Output** + +```py +>>> _another_weird_name_func() +"works!" +>>> some_weird_name_func_() +Traceback (most recent call last): + File "", line 1, in +NameError: name 'some_weird_name_func_' is not defined +``` --- @@ -2561,12 +2642,12 @@ The spelling is intended. Please, don't submit a patch for this. class Yo(object): def __init__(self): self.__honey = True - self.bitch = True + self.bro = True ``` **Output:** ```py ->>> Yo().bitch +>>> Yo().bro True >>> Yo().__honey AttributeError: 'Yo' object has no attribute '__honey' @@ -2574,7 +2655,7 @@ AttributeError: 'Yo' object has no attribute '__honey' True ``` -Why did `Yo()._Yo__honey` work? Only Indian readers would understand. +Why did `Yo()._Yo__honey` work? #### 💡 Explanation: @@ -2584,6 +2665,10 @@ Why did `Yo()._Yo__honey` work? Only Indian readers would understand. --- + + + + --- ## Section: Miscellaneous From 96455422efc903d14c4c35818d231d7a5cfc5c86 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 7 Jul 2019 22:17:33 +0530 Subject: [PATCH 22/29] Update the booleans example Fix some technically incorrect terminologies and snippets. --- README.md | 96 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 841d40a..f29fd25 100644 --- a/README.md +++ b/README.md @@ -1057,7 +1057,7 @@ Before Python 3.5, the boolean value for `datetime.time` object was considered t 1\. ```py -# A simple example to count the number of boolean and +# A simple example to count the number of booleans and # integers in an iterable of mixed data types. mixed_list = [False, 1.0, "some_string", 3, True, [], False] integers_found_so_far = 0 @@ -1078,33 +1078,27 @@ for item in mixed_list: 0 ``` + 2\. ```py -another_dict = {} -another_dict[True] = "JavaScript" -another_dict[1] = "Ruby" -another_dict[1.0] = "Python" -``` - -**Output:** -```py ->>> another_dict[True] -"Python" -``` - -3\. -```py >>> some_bool = True ->>> "wtf"*some_bool +>>> "wtf" * some_bool 'wtf' >>> some_bool = False ->>> "wtf"*some_bool +>>> "wtf" * some_bool '' ``` #### 💡 Explanation: -* Booleans are a subclass of `int` +* `bool` is a subclass of `int` in Python + ```py + >>> issubclass(bool, int) + True + >>> issubclass(int, bool) + False + ``` +* And thus, `True` and `False` are instances of `int` ```py >>> isinstance(True, int) True @@ -1114,8 +1108,10 @@ another_dict[1.0] = "Python" * The integer value of `True` is `1` and that of `False` is `0`. ```py - >>> True == 1 == 1.0 and False == 0 == 0.0 - True + >>> int(True) + 1 + >>> int(False) + 0 ``` * See this StackOverflow [answer](https://stackoverflow.com/a/8169049/4354153) for the rationale behind it. @@ -1718,7 +1714,7 @@ Yes, it runs for exactly **eight** times and stops. --- -### ▶ Stubborn `del` operator * +### ▶ Stubborn `del` operation * ```py class SomeClass: @@ -2319,7 +2315,7 @@ NameError: name '_another_weird_name_func' is not defined #### 💡 Explanation: -- It if often adivsable to not use wildcard imports. In wildcard imports, the names with leading underscore are be imported. This may lead to errors in runtime. +- It if often adivsable to not use wildcard imports. The first obvious reason for this is In wildcard imports, the names with leading underscore are be imported. This may lead to errors in runtime. - Had we used `from ... import a, b, c` syntax, the above `NameError` won't have occurred. ```py >>> from module import some_weird_name_func_, _another_weird_name_func @@ -2636,8 +2632,9 @@ The spelling is intended. Please, don't submit a patch for this. --- -### ▶ Mangling time! * +### ▶ Let's mangle ^ +1\. ```py class Yo(object): def __init__(self): @@ -2655,13 +2652,62 @@ AttributeError: 'Yo' object has no attribute '__honey' True ``` +2\. +```py +class Yo(object): + def __init__(self): + # Let's try something symmetrical this time + self.__honey__ = True + self.bro = True +``` + +**Output:** +```py +>>> Yo().bro +True + +>>> Yo()._Yo__honey__ +Traceback (most recent call last): + File "", line 1, in +AttributeError: 'Yo' object has no attribute '_Yo__honey__' +``` + Why did `Yo()._Yo__honey` work? +2\. + +```py +_A__variable = "Some value" + +class A(object): + def some_func(self): + return __variable # not initiatlized anywhere yet +``` + +**Output:** +```py +Traceback (most recent call last): + File "", line 1, in +AttributeError: 'A' object has no attribute '__variable' + +>>> >>> A().some_func() +'Some value' +``` + +3\. + +```py + +``` + + #### 💡 Explanation: * [Name Mangling](https://en.wikipedia.org/wiki/Name_mangling) is used to avoid naming collisions between different namespaces. -* In Python, the interpreter modifies (mangles) the class member names starting with `__` (double underscore) and not ending with more than one trailing underscore by adding `_NameOfTheClass` in front. -* So, to access `__honey` attribute, we are required to append `_Yo` to the front which would prevent conflicts with the same name attribute defined in any other class. +* In Python, the interpreter modifies (mangles) the class member names starting with `__` (double underscore a.k.a "dunder") and not ending with more than one trailing underscore by adding `_NameOfTheClass` in front. +* So, to access `__honey` attribute in first snippet, we had to append `_Yo` to the front which would prevent conflicts with the same name attribute defined in any other class. +* But then why didn't it work in the second snippet? Because name mangling excludes the names ending with double underscores. +* The third snippet was also a consequence of name mangling. The name `__variable` in the statement `return __variable` was mangled to `_A__variable` which also happens to be the name of the variable we declared in outer scope. --- From 4d8e5795a324c10dfd9f78e017c4e2a1550ddb3d Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 7 Jul 2019 22:18:35 +0530 Subject: [PATCH 23/29] Update TOC --- README.md | 167 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 115 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index f29fd25..0152d13 100644 --- a/README.md +++ b/README.md @@ -28,66 +28,129 @@ So, here we go... - [Usage](#usage) - [👀 Examples](#-examples) - [Section: Strain your brain!](#section-strain-your-brain) - - [▶ Strings can be tricky sometimes *](#-strings-can-be-tricky-sometimes-) - - [▶ Time for some hash brownies!](#-time-for-some-hash-brownies) - - [▶ Return return everywhere!](#-return-return-everywhere) - - [▶ Deep down, we're all the same. *](#-deep-down-were-all-the-same-) - - [▶ For what?](#-for-what) - - [▶ Evaluation time discrepancy](#-evaluation-time-discrepancy) - - [▶ `is` is not what it is!](#-is-is-not-what-it-is) - - [▶ A tic-tac-toe where X wins in the first attempt!](#-a-tic-tac-toe-where-x-wins-in-the-first-attempt) - - [▶ The sticky output function](#-the-sticky-output-function) - - [▶ `is not ...` is not `is (not ...)`](#-is-not--is-not-is-not-) - - [▶ The surprising comma](#-the-surprising-comma) - - [▶ Backslashes at the end of string](#-backslashes-at-the-end-of-string) - - [▶ not knot!](#-not-knot) - - [▶ Half triple-quoted strings](#-half-triple-quoted-strings) - - [▶ Midnight time doesn't exist?](#-midnight-time-doesnt-exist) - - [▶ What's wrong with booleans?](#-whats-wrong-with-booleans) - - [▶ Class attributes and instance attributes](#-class-attributes-and-instance-attributes) - - [▶ yielding None](#-yielding-none) - - [▶ Mutating the immutable!](#-mutating-the-immutable) - - [▶ The disappearing variable from outer scope](#-the-disappearing-variable-from-outer-scope) - - [▶ When True is actually False](#-when-true-is-actually-false) - - [▶ From filled to None in one instruction...](#-from-filled-to-none-in-one-instruction) - - [▶ Subclass relationships *](#-subclass-relationships-) - - [▶ The mysterious key type conversion *](#-the-mysterious-key-type-conversion-) - - [▶ Let's see if you can guess this?](#-lets-see-if-you-can-guess-this) + - [▶ Strings can be tricky sometimes *](#%E2%96%B6-strings-can-be-tricky-sometimes-) + - [💡 Explanation:](#-explanation) + - [▶ Splitsies ^](#%E2%96%B6-splitsies-%5E) + - [💡 Explanation:](#-explanation-1) + - [▶ Time for some hash brownies!](#%E2%96%B6-time-for-some-hash-brownies) + - [💡 Explanation](#-explanation) + - [▶ The disorder within order ^](#%E2%96%B6-the-disorder-within-order-%5E) + - [💡 Explanation:](#-explanation-2) + - [▶ Keep trying? *](#%E2%96%B6-keep-trying-) + - [💡 Explanation:](#-explanation-3) + - [▶ Deep down, we're all the same. *](#%E2%96%B6-deep-down-were-all-the-same-) + - [💡 Explanation:](#-explanation-4) + - [▶ For what?](#%E2%96%B6-for-what) + - [💡 Explanation:](#-explanation-5) + - [▶ Evaluation time discrepancy ^](#%E2%96%B6-evaluation-time-discrepancy-%5E) + - [💡 Explanation](#-explanation-1) + - [▶ Messing around with `is` operator^](#%E2%96%B6-messing-around-with-is-operator%5E) + - [💡 Explanation:](#-explanation-6) + - [▶ A tic-tac-toe where X wins in the first attempt!](#%E2%96%B6-a-tic-tac-toe-where-x-wins-in-the-first-attempt) + - [💡 Explanation:](#-explanation-7) + - [▶ The sticky output function](#%E2%96%B6-the-sticky-output-function) + - [💡 Explanation](#-explanation-2) + - [▶ The chicken-egg problem ^](#%E2%96%B6-the-chicken-egg-problem-%5E) + - [💡 Explanation](#-explanation-3) + - [▶ `is not ...` is not `is (not ...)`](#%E2%96%B6-is-not--is-not-is-not-) + - [💡 Explanation](#-explanation-4) + - [▶ The surprising comma](#%E2%96%B6-the-surprising-comma) + - [💡 Explanation:](#-explanation-8) + - [▶ Strings and the backslashes\ ^](#%E2%96%B6-strings-and-the-backslashes%5C-%5E) + - [💡 Explanation](#-explanation-5) + - [▶ not knot!](#%E2%96%B6-not-knot) + - [💡 Explanation:](#-explanation-9) + - [▶ Half triple-quoted strings](#%E2%96%B6-half-triple-quoted-strings) + - [💡 Explanation:](#-explanation-10) + - [▶ Midnight time doesn't exist?](#%E2%96%B6-midnight-time-doesnt-exist) + - [💡 Explanation:](#-explanation-11) + - [▶ What's wrong with booleans?](#%E2%96%B6-whats-wrong-with-booleans) + - [💡 Explanation:](#-explanation-12) + - [▶ Class attributes and instance attributes](#%E2%96%B6-class-attributes-and-instance-attributes) + - [💡 Explanation:](#-explanation-13) + - [▶ yielding None](#%E2%96%B6-yielding-none) + - [💡 Explanation:](#-explanation-14) + - [▶ Mutating the immutable!](#%E2%96%B6-mutating-the-immutable) + - [💡 Explanation:](#-explanation-15) + - [▶ The disappearing variable from outer scope](#%E2%96%B6-the-disappearing-variable-from-outer-scope) + - [💡 Explanation:](#-explanation-16) + - [▶ When True is actually False](#%E2%96%B6-when-true-is-actually-false) + - [💡 Explanation:](#-explanation-17) + - [▶ Lossy zip of iterators](#%E2%96%B6-lossy-zip-of-iterators) + - [💡 Explanation:](#-explanation-18) + - [▶ From filled to None in one instruction...](#%E2%96%B6-from-filled-to-none-in-one-instruction) + - [💡 Explanation](#-explanation-6) + - [▶ Subclass relationships *](#%E2%96%B6-subclass-relationships-) + - [💡 Explanation:](#-explanation-19) + - [▶ The mysterious key type conversion *](#%E2%96%B6-the-mysterious-key-type-conversion-) + - [💡 Explanation:](#-explanation-20) + - [▶ Let's see if you can guess this?](#%E2%96%B6-lets-see-if-you-can-guess-this) + - [💡 Explanation:](#-explanation-21) - [Section: Appearances are deceptive!](#section-appearances-are-deceptive) - - [▶ Skipping lines?](#-skipping-lines) - - [▶ Teleportation *](#-teleportation-) - - [▶ Well, something is fishy...](#-well-something-is-fishy) + - [▶ Skipping lines?](#%E2%96%B6-skipping-lines) + - [💡 Explanation](#-explanation-7) + - [▶ Teleportation *](#%E2%96%B6-teleportation-) + - [💡 Explanation:](#-explanation-22) + - [▶ Well, something is fishy...](#%E2%96%B6-well-something-is-fishy) + - [💡 Explanation](#-explanation-8) - [Section: Watch out for the landmines!](#section-watch-out-for-the-landmines) - - [▶ Modifying a dictionary while iterating over it](#-modifying-a-dictionary-while-iterating-over-it) - - [▶ Stubborn `del` operator *](#-stubborn-del-operator-) - - [▶ Deleting a list item while iterating](#-deleting-a-list-item-while-iterating) - - [▶ Loop variables leaking out!](#-loop-variables-leaking-out) - - [▶ Beware of default mutable arguments!](#-beware-of-default-mutable-arguments) - - [▶ Catching the Exceptions](#-catching-the-exceptions) - - [▶ Same operands, different story!](#-same-operands-different-story) - - [▶ The out of scope variable](#-the-out-of-scope-variable) - - [▶ Be careful with chained operations](#-be-careful-with-chained-operations) - - [▶ Name resolution ignoring class scope](#-name-resolution-ignoring-class-scope) - - [▶ Needle in a Haystack](#-needle-in-a-haystack) + - [▶ Modifying a dictionary while iterating over it](#%E2%96%B6-modifying-a-dictionary-while-iterating-over-it) + - [💡 Explanation:](#-explanation-23) + - [▶ Stubborn `del` operation *](#%E2%96%B6-stubborn-del-operation-) + - [💡 Explanation:](#-explanation-24) + - [▶ Deleting a list item while iterating](#%E2%96%B6-deleting-a-list-item-while-iterating) + - [💡 Explanation:](#-explanation-25) + - [▶ Loop variables leaking out!](#%E2%96%B6-loop-variables-leaking-out) + - [💡 Explanation:](#-explanation-26) + - [▶ Beware of default mutable arguments!](#%E2%96%B6-beware-of-default-mutable-arguments) + - [💡 Explanation:](#-explanation-27) + - [▶ Catching the Exceptions](#%E2%96%B6-catching-the-exceptions) + - [💡 Explanation](#-explanation-9) + - [▶ Same operands, different story!](#%E2%96%B6-same-operands-different-story) + - [💡 Explanation:](#-explanation-28) + - [▶ The out of scope variable](#%E2%96%B6-the-out-of-scope-variable) + - [💡 Explanation:](#-explanation-29) + - [▶ Be careful with chained operations](#%E2%96%B6-be-careful-with-chained-operations) + - [💡 Explanation:](#-explanation-30) + - [▶ Name resolution ignoring class scope](#%E2%96%B6-name-resolution-ignoring-class-scope) + - [💡 Explanation](#-explanation-10) + - [▶ Needles in a Haystack ^](#%E2%96%B6-needles-in-a-haystack-%5E) + - [💡 Explanation:](#-explanation-31) + - [▶ Wild imports](#%E2%96%B6-wild-imports) + - [💡 Explanation:](#-explanation-32) - [Section: The Hidden treasures!](#section-the-hidden-treasures) - - [▶ Okay Python, Can you make me fly? *](#-okay-python-can-you-make-me-fly-) - - [▶ `goto`, but why? *](#-goto-but-why-) - - [▶ Brace yourself! *](#-brace-yourself-) - - [▶ Let's meet Friendly Language Uncle For Life *](#-lets-meet-friendly-language-uncle-for-life-) - - [▶ Even Python understands that love is complicated *](#-even-python-understands-that-love-is-complicated-) - - [▶ Yes, it exists!](#-yes-it-exists) - - [▶ Inpinity *](#-inpinity-) - - [▶ Mangling time! *](#-mangling-time-) + - [▶ Okay Python, Can you make me fly? *](#%E2%96%B6-okay-python-can-you-make-me-fly-) + - [💡 Explanation:](#-explanation-33) + - [▶ `goto`, but why? *](#%E2%96%B6-goto-but-why-) + - [💡 Explanation:](#-explanation-34) + - [▶ Brace yourself! *](#%E2%96%B6-brace-yourself-) + - [💡 Explanation:](#-explanation-35) + - [▶ Let's meet Friendly Language Uncle For Life ^](#%E2%96%B6-lets-meet-friendly-language-uncle-for-life-%5E) + - [💡 Explanation:](#-explanation-36) + - [▶ Even Python understands that love is complicated *](#%E2%96%B6-even-python-understands-that-love-is-complicated-) + - [💡 Explanation:](#-explanation-37) + - [▶ Yes, it exists!](#%E2%96%B6-yes-it-exists) + - [💡 Explanation:](#-explanation-38) + - [▶ Ellipsis ^](#%E2%96%B6-ellipsis-%5E) + - [💡 Explanation](#-explanation-11) + - [▶ Inpinity *](#%E2%96%B6-inpinity-) + - [💡 Explanation:](#-explanation-39) + - [▶ Let's mangle ^](#%E2%96%B6-lets-mangle-%5E) + - [💡 Explanation:](#-explanation-40) - [Section: Miscellaneous](#section-miscellaneous) - - [▶ `+=` is faster](#--is-faster) - - [▶ Let's make a giant string!](#-lets-make-a-giant-string) - - [▶ Explicit typecast of strings](#-explicit-typecast-of-strings) - - [▶ Minor Ones](#-minor-ones) + - [▶ `+=` is faster](#%E2%96%B6--is-faster) + - [💡 Explanation:](#-explanation-41) + - [▶ Let's make a giant string!](#%E2%96%B6-lets-make-a-giant-string) + - [💡 Explanation](#-explanation-12) + - [▶ Explicit typecast of strings](#%E2%96%B6-explicit-typecast-of-strings) + - [💡 Explanation:](#-explanation-42) + - [▶ Minor Ones](#%E2%96%B6-minor-ones) - [Contributing](#contributing) - [Acknowledgements](#acknowledgements) + - [Some nice Links!](#some-nice-links) - [🎓 License](#-license) - [Help](#help) - - [Want to share wtfpython with friends?](#want-to-share-wtfpython-with-friends) + - [Surprise your geeky pythonist friends?](#surprise-your-geeky-pythonist-friends) - [Need a pdf version?](#need-a-pdf-version) From 98701351c617aff53c2ad24c109867ff7ab80550 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 8 Jul 2019 02:54:49 +0530 Subject: [PATCH 24/29] Add uuids to examples --- README.md | 114 +++++++++++++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 0326d05..c6833f3 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ Now, just run `wtfpython` at the command line which will open this collection in ## Section: Strain your brain! ### ▶ Strings can be tricky sometimes * - + 1\. ```py >>> a = "some_string" @@ -271,7 +271,7 @@ Makes sense, right? --- ### ▶ Splitsies ^ - + ```py >>> 'a'.split() ['a'] @@ -308,7 +308,7 @@ Makes sense, right? ### ▶ Time for some hash brownies! - + 1\. ```py some_dict = {} @@ -353,7 +353,7 @@ So, why is Python all over the place? --- ### ▶ The disorder within order ^ - + ```py from collections import OrderedDict @@ -451,7 +451,7 @@ What is going on here? --- ### ▶ Keep trying? * - + ```py def some_func(): try: @@ -511,7 +511,7 @@ Iteration 0 --- ### ▶ Deep down, we're all the same. * - + ```py class WTF: pass @@ -561,7 +561,7 @@ True --- ### ▶ For what? - + ```py some_string = "wtf" some_dict = {} @@ -614,7 +614,7 @@ for i, some_dict[i] in enumerate(some_string): --- ### ▶ Evaluation time discrepancy ^ - + 1\. ```py array = [1, 8, 15] @@ -680,7 +680,7 @@ array_4 = [400, 500, 600] --- ### ▶ Messing around with `is` operator^ - + The following is a very famous example present all over the internet. 1\. @@ -798,7 +798,7 @@ Similar optimization applies to other **immutable** objects like empty tuples as --- ### ▶ A tic-tac-toe where X wins in the first attempt! - + ```py # Let's initialize a row row = [""]*3 #row i['', '', ''] @@ -843,7 +843,7 @@ We can avoid this scenario here by not using `row` variable to generate `board`. --- ### ▶ The sticky output function - + ```py funcs = [] results = [] @@ -897,7 +897,7 @@ Even when the values of `x` were different in every iteration prior to appending --- ### ▶ The chicken-egg problem ^ - + 1\. ```py >>> isinstance(3, int) @@ -945,7 +945,7 @@ False --- ### ▶ `is not ...` is not `is (not ...)` - + ```py >>> 'something' is not None True @@ -961,7 +961,7 @@ False --- ### ▶ The surprising comma - + **Output:** ```py >>> def f(x, y,): @@ -991,7 +991,7 @@ SyntaxError: invalid syntax --- ### ▶ Strings and the backslashes\ ^ - + **Output:** ```py >>> print("\"") @@ -1034,7 +1034,7 @@ True --- ### ▶ not knot! - + ```py x = True y = False @@ -1061,7 +1061,7 @@ SyntaxError: invalid syntax --- ### ▶ Half triple-quoted strings - + **Output:** ```py >>> print('wtfpython''') @@ -1086,7 +1086,7 @@ wtfpython --- ### ▶ Midnight time doesn't exist? - + ```py from datetime import datetime @@ -1116,7 +1116,7 @@ Before Python 3.5, the boolean value for `datetime.time` object was considered t --- ### ▶ What's wrong with booleans? - + 1\. ```py # A simple example to count the number of booleans and @@ -1181,7 +1181,7 @@ for item in mixed_list: --- ### ▶ Class attributes and instance attributes - + 1\. ```py class A: @@ -1251,7 +1251,7 @@ True --- ### ▶ yielding None - + ```py some_iterable = ('a', 'b') @@ -1280,7 +1280,7 @@ def some_func(val): --- ### ▶ Mutating the immutable! - + ```py some_tuple = ("A", "tuple", "with", "values") another_tuple = ([1, 2], [3, 4], [5, 6]) @@ -1313,7 +1313,7 @@ But I thought tuples were immutable... --- ### ▶ The disappearing variable from outer scope - + ```py e = 7 try: @@ -1393,7 +1393,7 @@ NameError: name 'e' is not defined --- ### ▶ When True is actually False - + ```py True = False if True == False: @@ -1413,7 +1413,7 @@ I've lost faith in truth! --- ### ▶ Lossy zip of iterators - + ```py >>> numbers = list(range(7)) >>> numbers @@ -1462,7 +1462,7 @@ Where did element `3` go from the `numbers` list? --- ### ▶ From filled to None in one instruction... - + ```py some_list = [1, 2, 3] some_dict = { @@ -1490,7 +1490,7 @@ Most methods that modify the items of sequence/mapping objects like `list.append --- ### ▶ Subclass relationships * - + **Output:** ```py >>> from collections import Hashable @@ -1514,7 +1514,7 @@ The Subclass relationships were expected to be transitive, right? (i.e., if `A` --- ### ▶ The mysterious key type conversion * - + ```py class SomeClass(str): pass @@ -1570,7 +1570,7 @@ str --- ### ▶ Let's see if you can guess this? - + ```py a, b = a[b] = {}, 5 ``` @@ -1631,7 +1631,7 @@ a, b = a[b] = {}, 5 ## Section: Appearances are deceptive! ### ▶ Skipping lines? - + **Output:** ```py >>> value = 11 @@ -1667,7 +1667,7 @@ The built-in `ord()` function returns a character's Unicode [code point](https:/ --- ### ▶ Teleportation * - + ```py import numpy as np @@ -1697,7 +1697,7 @@ Where's the Nobel Prize? --- ### ▶ Well, something is fishy... - + ```py def square(x): """ @@ -1742,7 +1742,7 @@ Shouldn't that be 100? ### ▶ Modifying a dictionary while iterating over it - + ```py x = {0: None} @@ -1777,7 +1777,7 @@ Yes, it runs for exactly **eight** times and stops. --- ### ▶ Stubborn `del` operation * - + ```py class SomeClass: def __del__(self): @@ -1820,7 +1820,7 @@ Okay, now it's deleted :confused: --- ### ▶ Deleting a list item while iterating - + ```py list_1 = [1, 2, 3, 4] list_2 = [1, 2, 3, 4] @@ -1880,7 +1880,7 @@ Can you guess why the output is `[2, 4]`? --- ### ▶ Loop variables leaking out! - + 1\. ```py for x in range(7): @@ -1944,7 +1944,7 @@ print(x, ': x in global') --- ### ▶ Beware of default mutable arguments! - + ```py def some_func(default_arg=[]): default_arg.append("some_string") @@ -2001,7 +2001,7 @@ def some_func(default_arg=[]): --- ### ▶ Catching the Exceptions - + ```py some_list = [1, 2, 3] try: @@ -2076,7 +2076,7 @@ SyntaxError: invalid syntax --- ### ▶ Same operands, different story! - + 1\. ```py a = [1, 2, 3, 4] @@ -2118,7 +2118,7 @@ a += [5, 6, 7, 8] --- ### ▶ The out of scope variable - + ```py a = 1 def some_func(): @@ -2157,7 +2157,7 @@ UnboundLocalError: local variable 'a' referenced before assignment --- ### ▶ Be careful with chained operations - + ```py >>> (False == False) in [False] # makes sense False @@ -2202,7 +2202,7 @@ While such behavior might seem silly to you in the above examples, it's fantasti --- ### ▶ Name resolution ignoring class scope - + 1\. ```py x = 5 @@ -2246,7 +2246,7 @@ class SomeClass: --- ### ▶ Needles in a Haystack ^ - + 1\. ```py x, y = (0, 1) if True else None, None @@ -2351,7 +2351,7 @@ b = "javascript" --- ### ▶ Wild imports - + ```py # File: module.py @@ -2413,7 +2413,7 @@ NameError: name 'some_weird_name_func_' is not defined This section contains few of the lesser-known interesting things about Python that most beginners like me are unaware of (well, not anymore). ### ▶ Okay Python, Can you make me fly? * - + Well, here you go ```py @@ -2431,7 +2431,7 @@ Sshh.. It's a super secret. --- ### ▶ `goto`, but why? * - + ```py from goto import goto, label for i in range(9): @@ -2459,7 +2459,7 @@ Freedom! --- ### ▶ Brace yourself! * - + If you are one of the people who doesn't like using whitespace in Python to denote scopes, you can use the C-style {} by importing, ```py @@ -2484,7 +2484,7 @@ Braces? No way! If you think that's disappointing, use Java. Okay, another surpr --- ### ▶ Let's meet Friendly Language Uncle For Life ^ - + **Output (Python 3.x)** ```py >>> from __future__ import barry_as_FLUFL @@ -2515,7 +2515,7 @@ There we go. --- ### ▶ Even Python understands that love is complicated * - + ```py import this ``` @@ -2572,7 +2572,7 @@ True --- ### ▶ Yes, it exists! - + **The `else` clause for loops.** One typical example might be: ```py @@ -2616,7 +2616,7 @@ Try block executed successfully... --- ### ▶ Ellipsis ^ - + ```py def some_func(): Ellipsis @@ -2676,7 +2676,7 @@ Ellipsis --- ### ▶ Inpinity * - + The spelling is intended. Please, don't submit a patch for this. **Output (Python 3.x):** @@ -2695,7 +2695,7 @@ The spelling is intended. Please, don't submit a patch for this. --- ### ▶ Let's mangle ^ - + 1\. ```py class Yo(object): @@ -2783,7 +2783,7 @@ AttributeError: 'A' object has no attribute '__variable' ### ▶ `+=` is faster - + ```py # using "+", three strings: >>> timeit.timeit("s1 = s1 + s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100) @@ -2799,7 +2799,7 @@ AttributeError: 'A' object has no attribute '__variable' --- ### ▶ Let's make a giant string! - + ```py def add_string_with_plus(iters): s = "" @@ -2892,7 +2892,7 @@ Let's increase the number of iterations by a factor of 10. --- ### ▶ Explicit typecast of strings - + ```py a = float('inf') b = float('nan') @@ -2931,7 +2931,7 @@ nan --- ### ▶ Minor Ones - + * `join()` is a string operation instead of list operation. (sort of counter-intuitive at first usage) **💡 Explanation:** From 863e1f4512fd91ae0dde79833377c629c21dee77 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 8 Jul 2019 03:00:33 +0530 Subject: [PATCH 25/29] Fix uuid comments --- README.md | 114 +++++++++++++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index c6833f3..8128119 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ Now, just run `wtfpython` at the command line which will open this collection in ## Section: Strain your brain! ### ▶ Strings can be tricky sometimes * - + 1\. ```py >>> a = "some_string" @@ -271,7 +271,7 @@ Makes sense, right? --- ### ▶ Splitsies ^ - + ```py >>> 'a'.split() ['a'] @@ -308,7 +308,7 @@ Makes sense, right? ### ▶ Time for some hash brownies! - + 1\. ```py some_dict = {} @@ -353,7 +353,7 @@ So, why is Python all over the place? --- ### ▶ The disorder within order ^ - + ```py from collections import OrderedDict @@ -451,7 +451,7 @@ What is going on here? --- ### ▶ Keep trying? * - + ```py def some_func(): try: @@ -511,7 +511,7 @@ Iteration 0 --- ### ▶ Deep down, we're all the same. * - + ```py class WTF: pass @@ -561,7 +561,7 @@ True --- ### ▶ For what? - + ```py some_string = "wtf" some_dict = {} @@ -614,7 +614,7 @@ for i, some_dict[i] in enumerate(some_string): --- ### ▶ Evaluation time discrepancy ^ - + 1\. ```py array = [1, 8, 15] @@ -680,7 +680,7 @@ array_4 = [400, 500, 600] --- ### ▶ Messing around with `is` operator^ - + The following is a very famous example present all over the internet. 1\. @@ -798,7 +798,7 @@ Similar optimization applies to other **immutable** objects like empty tuples as --- ### ▶ A tic-tac-toe where X wins in the first attempt! - + ```py # Let's initialize a row row = [""]*3 #row i['', '', ''] @@ -843,7 +843,7 @@ We can avoid this scenario here by not using `row` variable to generate `board`. --- ### ▶ The sticky output function - + ```py funcs = [] results = [] @@ -897,7 +897,7 @@ Even when the values of `x` were different in every iteration prior to appending --- ### ▶ The chicken-egg problem ^ - + 1\. ```py >>> isinstance(3, int) @@ -945,7 +945,7 @@ False --- ### ▶ `is not ...` is not `is (not ...)` - + ```py >>> 'something' is not None True @@ -961,7 +961,7 @@ False --- ### ▶ The surprising comma - + **Output:** ```py >>> def f(x, y,): @@ -991,7 +991,7 @@ SyntaxError: invalid syntax --- ### ▶ Strings and the backslashes\ ^ - + **Output:** ```py >>> print("\"") @@ -1034,7 +1034,7 @@ True --- ### ▶ not knot! - + ```py x = True y = False @@ -1061,7 +1061,7 @@ SyntaxError: invalid syntax --- ### ▶ Half triple-quoted strings - + **Output:** ```py >>> print('wtfpython''') @@ -1086,7 +1086,7 @@ wtfpython --- ### ▶ Midnight time doesn't exist? - + ```py from datetime import datetime @@ -1116,7 +1116,7 @@ Before Python 3.5, the boolean value for `datetime.time` object was considered t --- ### ▶ What's wrong with booleans? - + 1\. ```py # A simple example to count the number of booleans and @@ -1181,7 +1181,7 @@ for item in mixed_list: --- ### ▶ Class attributes and instance attributes - + 1\. ```py class A: @@ -1251,7 +1251,7 @@ True --- ### ▶ yielding None - + ```py some_iterable = ('a', 'b') @@ -1280,7 +1280,7 @@ def some_func(val): --- ### ▶ Mutating the immutable! - + ```py some_tuple = ("A", "tuple", "with", "values") another_tuple = ([1, 2], [3, 4], [5, 6]) @@ -1313,7 +1313,7 @@ But I thought tuples were immutable... --- ### ▶ The disappearing variable from outer scope - + ```py e = 7 try: @@ -1393,7 +1393,7 @@ NameError: name 'e' is not defined --- ### ▶ When True is actually False - + ```py True = False if True == False: @@ -1413,7 +1413,7 @@ I've lost faith in truth! --- ### ▶ Lossy zip of iterators - + ```py >>> numbers = list(range(7)) >>> numbers @@ -1462,7 +1462,7 @@ Where did element `3` go from the `numbers` list? --- ### ▶ From filled to None in one instruction... - + ```py some_list = [1, 2, 3] some_dict = { @@ -1490,7 +1490,7 @@ Most methods that modify the items of sequence/mapping objects like `list.append --- ### ▶ Subclass relationships * - + **Output:** ```py >>> from collections import Hashable @@ -1514,7 +1514,7 @@ The Subclass relationships were expected to be transitive, right? (i.e., if `A` --- ### ▶ The mysterious key type conversion * - + ```py class SomeClass(str): pass @@ -1570,7 +1570,7 @@ str --- ### ▶ Let's see if you can guess this? - + ```py a, b = a[b] = {}, 5 ``` @@ -1631,7 +1631,7 @@ a, b = a[b] = {}, 5 ## Section: Appearances are deceptive! ### ▶ Skipping lines? - + **Output:** ```py >>> value = 11 @@ -1667,7 +1667,7 @@ The built-in `ord()` function returns a character's Unicode [code point](https:/ --- ### ▶ Teleportation * - + ```py import numpy as np @@ -1697,7 +1697,7 @@ Where's the Nobel Prize? --- ### ▶ Well, something is fishy... - + ```py def square(x): """ @@ -1742,7 +1742,7 @@ Shouldn't that be 100? ### ▶ Modifying a dictionary while iterating over it - + ```py x = {0: None} @@ -1777,7 +1777,7 @@ Yes, it runs for exactly **eight** times and stops. --- ### ▶ Stubborn `del` operation * - + ```py class SomeClass: def __del__(self): @@ -1820,7 +1820,7 @@ Okay, now it's deleted :confused: --- ### ▶ Deleting a list item while iterating - + ```py list_1 = [1, 2, 3, 4] list_2 = [1, 2, 3, 4] @@ -1880,7 +1880,7 @@ Can you guess why the output is `[2, 4]`? --- ### ▶ Loop variables leaking out! - + 1\. ```py for x in range(7): @@ -1944,7 +1944,7 @@ print(x, ': x in global') --- ### ▶ Beware of default mutable arguments! - + ```py def some_func(default_arg=[]): default_arg.append("some_string") @@ -2001,7 +2001,7 @@ def some_func(default_arg=[]): --- ### ▶ Catching the Exceptions - + ```py some_list = [1, 2, 3] try: @@ -2076,7 +2076,7 @@ SyntaxError: invalid syntax --- ### ▶ Same operands, different story! - + 1\. ```py a = [1, 2, 3, 4] @@ -2118,7 +2118,7 @@ a += [5, 6, 7, 8] --- ### ▶ The out of scope variable - + ```py a = 1 def some_func(): @@ -2157,7 +2157,7 @@ UnboundLocalError: local variable 'a' referenced before assignment --- ### ▶ Be careful with chained operations - + ```py >>> (False == False) in [False] # makes sense False @@ -2202,7 +2202,7 @@ While such behavior might seem silly to you in the above examples, it's fantasti --- ### ▶ Name resolution ignoring class scope - + 1\. ```py x = 5 @@ -2246,7 +2246,7 @@ class SomeClass: --- ### ▶ Needles in a Haystack ^ - + 1\. ```py x, y = (0, 1) if True else None, None @@ -2351,7 +2351,7 @@ b = "javascript" --- ### ▶ Wild imports - + ```py # File: module.py @@ -2413,7 +2413,7 @@ NameError: name 'some_weird_name_func_' is not defined This section contains few of the lesser-known interesting things about Python that most beginners like me are unaware of (well, not anymore). ### ▶ Okay Python, Can you make me fly? * - + Well, here you go ```py @@ -2431,7 +2431,7 @@ Sshh.. It's a super secret. --- ### ▶ `goto`, but why? * - + ```py from goto import goto, label for i in range(9): @@ -2459,7 +2459,7 @@ Freedom! --- ### ▶ Brace yourself! * - + If you are one of the people who doesn't like using whitespace in Python to denote scopes, you can use the C-style {} by importing, ```py @@ -2484,7 +2484,7 @@ Braces? No way! If you think that's disappointing, use Java. Okay, another surpr --- ### ▶ Let's meet Friendly Language Uncle For Life ^ - + **Output (Python 3.x)** ```py >>> from __future__ import barry_as_FLUFL @@ -2515,7 +2515,7 @@ There we go. --- ### ▶ Even Python understands that love is complicated * - + ```py import this ``` @@ -2572,7 +2572,7 @@ True --- ### ▶ Yes, it exists! - + **The `else` clause for loops.** One typical example might be: ```py @@ -2616,7 +2616,7 @@ Try block executed successfully... --- ### ▶ Ellipsis ^ - + ```py def some_func(): Ellipsis @@ -2676,7 +2676,7 @@ Ellipsis --- ### ▶ Inpinity * - + The spelling is intended. Please, don't submit a patch for this. **Output (Python 3.x):** @@ -2695,7 +2695,7 @@ The spelling is intended. Please, don't submit a patch for this. --- ### ▶ Let's mangle ^ - + 1\. ```py class Yo(object): @@ -2783,7 +2783,7 @@ AttributeError: 'A' object has no attribute '__variable' ### ▶ `+=` is faster - + ```py # using "+", three strings: >>> timeit.timeit("s1 = s1 + s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100) @@ -2799,7 +2799,7 @@ AttributeError: 'A' object has no attribute '__variable' --- ### ▶ Let's make a giant string! - + ```py def add_string_with_plus(iters): s = "" @@ -2892,7 +2892,7 @@ Let's increase the number of iterations by a factor of 10. --- ### ▶ Explicit typecast of strings - + ```py a = float('inf') b = float('nan') @@ -2931,7 +2931,7 @@ nan --- ### ▶ Minor Ones - + * `join()` is a string operation instead of list operation. (sort of counter-intuitive at first usage) **💡 Explanation:** From 4618bed5f6ef4cf02996ee0198ffc31488349cad Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 8 Jul 2019 22:01:41 +0530 Subject: [PATCH 26/29] Remove asterisks and carets from titles --- README.md | 146 +++++++++++++++++------------------------------------- 1 file changed, 45 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 8128119..4a7157f 100644 --- a/README.md +++ b/README.md @@ -28,126 +28,71 @@ So, here we go... - [Usage](#usage) - [👀 Examples](#-examples) - [Section: Strain your brain!](#section-strain-your-brain) - - [▶ Strings can be tricky sometimes *](#%E2%96%B6-strings-can-be-tricky-sometimes-) - - [💡 Explanation:](#-explanation) - - [▶ Splitsies ^](#%E2%96%B6-splitsies-%5E) - - [💡 Explanation:](#-explanation-1) + - [▶ Strings can be tricky sometimes](#%E2%96%B6-strings-can-be-tricky-sometimes) + - [▶ Splitsies](#%E2%96%B6-splitsies) +- [is same as](#is-same-as) +- [but](#but) +- [isn't the same as](#isnt-the-same-as) - [▶ Time for some hash brownies!](#%E2%96%B6-time-for-some-hash-brownies) - - [💡 Explanation](#-explanation) - - [▶ The disorder within order ^](#%E2%96%B6-the-disorder-within-order-%5E) - - [💡 Explanation:](#-explanation-2) - - [▶ Keep trying? *](#%E2%96%B6-keep-trying-) - - [💡 Explanation:](#-explanation-3) - - [▶ Deep down, we're all the same. *](#%E2%96%B6-deep-down-were-all-the-same-) - - [💡 Explanation:](#-explanation-4) + - [▶ The disorder within order](#%E2%96%B6-the-disorder-within-order) + - [▶ Keep trying?](#%E2%96%B6-keep-trying) + - [▶ Deep down, we're all the same](#%E2%96%B6-deep-down-were-all-the-same) - [▶ For what?](#%E2%96%B6-for-what) - - [💡 Explanation:](#-explanation-5) - - [▶ Evaluation time discrepancy ^](#%E2%96%B6-evaluation-time-discrepancy-%5E) - - [💡 Explanation](#-explanation-1) + - [▶ Evaluation time discrepancy](#%E2%96%B6-evaluation-time-discrepancy) - [▶ Messing around with `is` operator^](#%E2%96%B6-messing-around-with-is-operator%5E) - - [💡 Explanation:](#-explanation-6) - [▶ A tic-tac-toe where X wins in the first attempt!](#%E2%96%B6-a-tic-tac-toe-where-x-wins-in-the-first-attempt) - - [💡 Explanation:](#-explanation-7) - [▶ The sticky output function](#%E2%96%B6-the-sticky-output-function) - - [💡 Explanation](#-explanation-2) - - [▶ The chicken-egg problem ^](#%E2%96%B6-the-chicken-egg-problem-%5E) - - [💡 Explanation](#-explanation-3) + - [▶ The chicken-egg problem](#%E2%96%B6-the-chicken-egg-problem) - [▶ `is not ...` is not `is (not ...)`](#%E2%96%B6-is-not--is-not-is-not-) - - [💡 Explanation](#-explanation-4) - [▶ The surprising comma](#%E2%96%B6-the-surprising-comma) - - [💡 Explanation:](#-explanation-8) - - [▶ Strings and the backslashes\ ^](#%E2%96%B6-strings-and-the-backslashes%5C-%5E) - - [💡 Explanation](#-explanation-5) + - [▶ Strings and the backslashes](#%E2%96%B6-strings-and-the-backslashes) - [▶ not knot!](#%E2%96%B6-not-knot) - - [💡 Explanation:](#-explanation-9) - [▶ Half triple-quoted strings](#%E2%96%B6-half-triple-quoted-strings) - - [💡 Explanation:](#-explanation-10) - [▶ Midnight time doesn't exist?](#%E2%96%B6-midnight-time-doesnt-exist) - - [💡 Explanation:](#-explanation-11) - [▶ What's wrong with booleans?](#%E2%96%B6-whats-wrong-with-booleans) - - [💡 Explanation:](#-explanation-12) - [▶ Class attributes and instance attributes](#%E2%96%B6-class-attributes-and-instance-attributes) - - [💡 Explanation:](#-explanation-13) - [▶ yielding None](#%E2%96%B6-yielding-none) - - [💡 Explanation:](#-explanation-14) - [▶ Mutating the immutable!](#%E2%96%B6-mutating-the-immutable) - - [💡 Explanation:](#-explanation-15) - [▶ The disappearing variable from outer scope](#%E2%96%B6-the-disappearing-variable-from-outer-scope) - - [💡 Explanation:](#-explanation-16) - [▶ When True is actually False](#%E2%96%B6-when-true-is-actually-false) - - [💡 Explanation:](#-explanation-17) - [▶ Lossy zip of iterators](#%E2%96%B6-lossy-zip-of-iterators) - - [💡 Explanation:](#-explanation-18) - [▶ From filled to None in one instruction...](#%E2%96%B6-from-filled-to-none-in-one-instruction) - - [💡 Explanation](#-explanation-6) - - [▶ Subclass relationships *](#%E2%96%B6-subclass-relationships-) - - [💡 Explanation:](#-explanation-19) - - [▶ The mysterious key type conversion *](#%E2%96%B6-the-mysterious-key-type-conversion-) - - [💡 Explanation:](#-explanation-20) + - [▶ Subclass relationships](#%E2%96%B6-subclass-relationships) + - [▶ The mysterious key type conversion](#%E2%96%B6-the-mysterious-key-type-conversion) - [▶ Let's see if you can guess this?](#%E2%96%B6-lets-see-if-you-can-guess-this) - - [💡 Explanation:](#-explanation-21) - [Section: Appearances are deceptive!](#section-appearances-are-deceptive) - [▶ Skipping lines?](#%E2%96%B6-skipping-lines) - - [💡 Explanation](#-explanation-7) - - [▶ Teleportation *](#%E2%96%B6-teleportation-) - - [💡 Explanation:](#-explanation-22) + - [▶ Teleportation](#%E2%96%B6-teleportation) - [▶ Well, something is fishy...](#%E2%96%B6-well-something-is-fishy) - - [💡 Explanation](#-explanation-8) - [Section: Watch out for the landmines!](#section-watch-out-for-the-landmines) - [▶ Modifying a dictionary while iterating over it](#%E2%96%B6-modifying-a-dictionary-while-iterating-over-it) - - [💡 Explanation:](#-explanation-23) - - [▶ Stubborn `del` operation *](#%E2%96%B6-stubborn-del-operation-) - - [💡 Explanation:](#-explanation-24) - [▶ Deleting a list item while iterating](#%E2%96%B6-deleting-a-list-item-while-iterating) - - [💡 Explanation:](#-explanation-25) - [▶ Loop variables leaking out!](#%E2%96%B6-loop-variables-leaking-out) - - [💡 Explanation:](#-explanation-26) - [▶ Beware of default mutable arguments!](#%E2%96%B6-beware-of-default-mutable-arguments) - - [💡 Explanation:](#-explanation-27) - [▶ Catching the Exceptions](#%E2%96%B6-catching-the-exceptions) - - [💡 Explanation](#-explanation-9) - [▶ Same operands, different story!](#%E2%96%B6-same-operands-different-story) - - [💡 Explanation:](#-explanation-28) - [▶ The out of scope variable](#%E2%96%B6-the-out-of-scope-variable) - - [💡 Explanation:](#-explanation-29) - [▶ Be careful with chained operations](#%E2%96%B6-be-careful-with-chained-operations) - - [💡 Explanation:](#-explanation-30) - [▶ Name resolution ignoring class scope](#%E2%96%B6-name-resolution-ignoring-class-scope) - - [💡 Explanation](#-explanation-10) - - [▶ Needles in a Haystack ^](#%E2%96%B6-needles-in-a-haystack-%5E) - - [💡 Explanation:](#-explanation-31) + - [▶ Needles in a Haystack](#%E2%96%B6-needles-in-a-haystack) - [▶ Wild imports](#%E2%96%B6-wild-imports) - - [💡 Explanation:](#-explanation-32) - [Section: The Hidden treasures!](#section-the-hidden-treasures) - - [▶ Okay Python, Can you make me fly? *](#%E2%96%B6-okay-python-can-you-make-me-fly-) - - [💡 Explanation:](#-explanation-33) - - [▶ `goto`, but why? *](#%E2%96%B6-goto-but-why-) - - [💡 Explanation:](#-explanation-34) - - [▶ Brace yourself! *](#%E2%96%B6-brace-yourself-) - - [💡 Explanation:](#-explanation-35) - - [▶ Let's meet Friendly Language Uncle For Life ^](#%E2%96%B6-lets-meet-friendly-language-uncle-for-life-%5E) - - [💡 Explanation:](#-explanation-36) - - [▶ Even Python understands that love is complicated *](#%E2%96%B6-even-python-understands-that-love-is-complicated-) - - [💡 Explanation:](#-explanation-37) + - [▶ Okay Python, Can you make me fly?](#%E2%96%B6-okay-python-can-you-make-me-fly) + - [▶ `goto`, but why?](#%E2%96%B6-goto-but-why) + - [▶ Brace yourself!](#%E2%96%B6-brace-yourself) + - [▶ Let's meet Friendly Language Uncle For Life](#%E2%96%B6-lets-meet-friendly-language-uncle-for-life) + - [▶ Even Python understands that love is complicated](#%E2%96%B6-even-python-understands-that-love-is-complicated) - [▶ Yes, it exists!](#%E2%96%B6-yes-it-exists) - - [💡 Explanation:](#-explanation-38) - - [▶ Ellipsis ^](#%E2%96%B6-ellipsis-%5E) - - [💡 Explanation](#-explanation-11) - - [▶ Inpinity *](#%E2%96%B6-inpinity-) - - [💡 Explanation:](#-explanation-39) - - [▶ Let's mangle ^](#%E2%96%B6-lets-mangle-%5E) - - [💡 Explanation:](#-explanation-40) + - [▶ Ellipsis](#%E2%96%B6-ellipsis) + - [▶ Inpinity](#%E2%96%B6-inpinity) + - [▶ Let's mangle](#%E2%96%B6-lets-mangle) - [Section: Miscellaneous](#section-miscellaneous) - [▶ `+=` is faster](#%E2%96%B6--is-faster) - - [💡 Explanation:](#-explanation-41) - [▶ Let's make a giant string!](#%E2%96%B6-lets-make-a-giant-string) - - [💡 Explanation](#-explanation-12) - [▶ Explicit typecast of strings](#%E2%96%B6-explicit-typecast-of-strings) - - [💡 Explanation:](#-explanation-42) - [▶ Minor Ones](#%E2%96%B6-minor-ones) - [Contributing](#contributing) - [Acknowledgements](#acknowledgements) - - [Some nice Links!](#some-nice-links) - [🎓 License](#-license) - [Help](#help) - [Surprise your geeky pythonist friends?](#surprise-your-geeky-pythonist-friends) @@ -159,8 +104,7 @@ So, here we go... All the examples are structured like below: -> ### ▶ 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. +> ### ▶ Some fancy Title > > ```py > # Setting up the code. @@ -220,7 +164,7 @@ Now, just run `wtfpython` at the command line which will open this collection in ## Section: Strain your brain! -### ▶ Strings can be tricky sometimes * +### ▶ Strings can be tricky sometimes 1\. ```py @@ -270,7 +214,7 @@ Makes sense, right? --- -### ▶ Splitsies ^ +### ▶ Splitsies ```py >>> 'a'.split() @@ -352,7 +296,7 @@ So, why is Python all over the place? --- -### ▶ The disorder within order ^ +### ▶ The disorder within order ```py from collections import OrderedDict @@ -450,7 +394,7 @@ What is going on here? --- -### ▶ Keep trying? * +### ▶ Keep trying? ```py def some_func(): @@ -510,7 +454,7 @@ Iteration 0 --- -### ▶ Deep down, we're all the same. * +### ▶ Deep down, we're all the same ```py class WTF: @@ -613,7 +557,7 @@ for i, some_dict[i] in enumerate(some_string): --- -### ▶ Evaluation time discrepancy ^ +### ▶ Evaluation time discrepancy 1\. ```py @@ -896,7 +840,7 @@ Even when the values of `x` were different in every iteration prior to appending --- -### ▶ The chicken-egg problem ^ +### ▶ The chicken-egg problem 1\. ```py @@ -990,7 +934,7 @@ SyntaxError: invalid syntax --- -### ▶ Strings and the backslashes\ ^ +### ▶ Strings and the backslashes **Output:** ```py @@ -1489,7 +1433,7 @@ Most methods that modify the items of sequence/mapping objects like `list.append --- -### ▶ Subclass relationships * +### ▶ Subclass relationships **Output:** ```py @@ -1513,7 +1457,7 @@ The Subclass relationships were expected to be transitive, right? (i.e., if `A` --- -### ▶ The mysterious key type conversion * +### ▶ The mysterious key type conversion ```py class SomeClass(str): @@ -1666,7 +1610,7 @@ The built-in `ord()` function returns a character's Unicode [code point](https:/ --- -### ▶ Teleportation * +### ▶ Teleportation ```py import numpy as np @@ -1776,7 +1720,7 @@ Yes, it runs for exactly **eight** times and stops. --- -### ▶ Stubborn `del` operation * +### ▶ Stubborn `del` operation ```py class SomeClass: @@ -2245,7 +2189,7 @@ class SomeClass: --- -### ▶ Needles in a Haystack ^ +### ▶ Needles in a Haystack 1\. ```py @@ -2412,7 +2356,7 @@ NameError: name 'some_weird_name_func_' is not defined This section contains few of the lesser-known interesting things about Python that most beginners like me are unaware of (well, not anymore). -### ▶ Okay Python, Can you make me fly? * +### ▶ Okay Python, Can you make me fly? Well, here you go @@ -2430,7 +2374,7 @@ Sshh.. It's a super secret. --- -### ▶ `goto`, but why? * +### ▶ `goto`, but why? ```py from goto import goto, label @@ -2458,7 +2402,7 @@ Freedom! --- -### ▶ Brace yourself! * +### ▶ Brace yourself! If you are one of the people who doesn't like using whitespace in Python to denote scopes, you can use the C-style {} by importing, @@ -2483,7 +2427,7 @@ Braces? No way! If you think that's disappointing, use Java. Okay, another surpr --- -### ▶ Let's meet Friendly Language Uncle For Life ^ +### ▶ Let's meet Friendly Language Uncle For Life **Output (Python 3.x)** ```py @@ -2514,7 +2458,7 @@ There we go. --- -### ▶ Even Python understands that love is complicated * +### ▶ Even Python understands that love is complicated ```py import this @@ -2615,7 +2559,7 @@ Try block executed successfully... - `else` clause after try block is also called "completion clause" as reaching the `else` clause in a `try` statement means that the try block actually completed successfully. --- -### ▶ Ellipsis ^ +### ▶ Ellipsis ```py def some_func(): @@ -2675,7 +2619,7 @@ Ellipsis --- -### ▶ Inpinity * +### ▶ Inpinity The spelling is intended. Please, don't submit a patch for this. @@ -2694,7 +2638,7 @@ The spelling is intended. Please, don't submit a patch for this. --- -### ▶ Let's mangle ^ +### ▶ Let's mangle 1\. ```py From d3157d61a303e31082ac7a82ebfd5e39e5ae8eb9 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Mon, 8 Jul 2019 22:22:04 +0530 Subject: [PATCH 27/29] Fix toc --- README.md | 141 ++++++++++++++++++++++++++---------------------------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 4a7157f..5787e06 100644 --- a/README.md +++ b/README.md @@ -20,85 +20,82 @@ So, here we go... # Table of Contents - - - + - [Structure of the Examples](#structure-of-the-examples) + + [▶ Some fancy Title](#-some-fancy-title) - [Usage](#usage) - [👀 Examples](#-examples) - - [Section: Strain your brain!](#section-strain-your-brain) - - [▶ Strings can be tricky sometimes](#%E2%96%B6-strings-can-be-tricky-sometimes) - - [▶ Splitsies](#%E2%96%B6-splitsies) -- [is same as](#is-same-as) -- [but](#but) -- [isn't the same as](#isnt-the-same-as) - - [▶ Time for some hash brownies!](#%E2%96%B6-time-for-some-hash-brownies) - - [▶ The disorder within order](#%E2%96%B6-the-disorder-within-order) - - [▶ Keep trying?](#%E2%96%B6-keep-trying) - - [▶ Deep down, we're all the same](#%E2%96%B6-deep-down-were-all-the-same) - - [▶ For what?](#%E2%96%B6-for-what) - - [▶ Evaluation time discrepancy](#%E2%96%B6-evaluation-time-discrepancy) - - [▶ Messing around with `is` operator^](#%E2%96%B6-messing-around-with-is-operator%5E) - - [▶ A tic-tac-toe where X wins in the first attempt!](#%E2%96%B6-a-tic-tac-toe-where-x-wins-in-the-first-attempt) - - [▶ The sticky output function](#%E2%96%B6-the-sticky-output-function) - - [▶ The chicken-egg problem](#%E2%96%B6-the-chicken-egg-problem) - - [▶ `is not ...` is not `is (not ...)`](#%E2%96%B6-is-not--is-not-is-not-) - - [▶ The surprising comma](#%E2%96%B6-the-surprising-comma) - - [▶ Strings and the backslashes](#%E2%96%B6-strings-and-the-backslashes) - - [▶ not knot!](#%E2%96%B6-not-knot) - - [▶ Half triple-quoted strings](#%E2%96%B6-half-triple-quoted-strings) - - [▶ Midnight time doesn't exist?](#%E2%96%B6-midnight-time-doesnt-exist) - - [▶ What's wrong with booleans?](#%E2%96%B6-whats-wrong-with-booleans) - - [▶ Class attributes and instance attributes](#%E2%96%B6-class-attributes-and-instance-attributes) - - [▶ yielding None](#%E2%96%B6-yielding-none) - - [▶ Mutating the immutable!](#%E2%96%B6-mutating-the-immutable) - - [▶ The disappearing variable from outer scope](#%E2%96%B6-the-disappearing-variable-from-outer-scope) - - [▶ When True is actually False](#%E2%96%B6-when-true-is-actually-false) - - [▶ Lossy zip of iterators](#%E2%96%B6-lossy-zip-of-iterators) - - [▶ From filled to None in one instruction...](#%E2%96%B6-from-filled-to-none-in-one-instruction) - - [▶ Subclass relationships](#%E2%96%B6-subclass-relationships) - - [▶ The mysterious key type conversion](#%E2%96%B6-the-mysterious-key-type-conversion) - - [▶ Let's see if you can guess this?](#%E2%96%B6-lets-see-if-you-can-guess-this) - - [Section: Appearances are deceptive!](#section-appearances-are-deceptive) - - [▶ Skipping lines?](#%E2%96%B6-skipping-lines) - - [▶ Teleportation](#%E2%96%B6-teleportation) - - [▶ Well, something is fishy...](#%E2%96%B6-well-something-is-fishy) - - [Section: Watch out for the landmines!](#section-watch-out-for-the-landmines) - - [▶ Modifying a dictionary while iterating over it](#%E2%96%B6-modifying-a-dictionary-while-iterating-over-it) - - [▶ Deleting a list item while iterating](#%E2%96%B6-deleting-a-list-item-while-iterating) - - [▶ Loop variables leaking out!](#%E2%96%B6-loop-variables-leaking-out) - - [▶ Beware of default mutable arguments!](#%E2%96%B6-beware-of-default-mutable-arguments) - - [▶ Catching the Exceptions](#%E2%96%B6-catching-the-exceptions) - - [▶ Same operands, different story!](#%E2%96%B6-same-operands-different-story) - - [▶ The out of scope variable](#%E2%96%B6-the-out-of-scope-variable) - - [▶ Be careful with chained operations](#%E2%96%B6-be-careful-with-chained-operations) - - [▶ Name resolution ignoring class scope](#%E2%96%B6-name-resolution-ignoring-class-scope) - - [▶ Needles in a Haystack](#%E2%96%B6-needles-in-a-haystack) - - [▶ Wild imports](#%E2%96%B6-wild-imports) - - [Section: The Hidden treasures!](#section-the-hidden-treasures) - - [▶ Okay Python, Can you make me fly?](#%E2%96%B6-okay-python-can-you-make-me-fly) - - [▶ `goto`, but why?](#%E2%96%B6-goto-but-why) - - [▶ Brace yourself!](#%E2%96%B6-brace-yourself) - - [▶ Let's meet Friendly Language Uncle For Life](#%E2%96%B6-lets-meet-friendly-language-uncle-for-life) - - [▶ Even Python understands that love is complicated](#%E2%96%B6-even-python-understands-that-love-is-complicated) - - [▶ Yes, it exists!](#%E2%96%B6-yes-it-exists) - - [▶ Ellipsis](#%E2%96%B6-ellipsis) - - [▶ Inpinity](#%E2%96%B6-inpinity) - - [▶ Let's mangle](#%E2%96%B6-lets-mangle) - - [Section: Miscellaneous](#section-miscellaneous) - - [▶ `+=` is faster](#%E2%96%B6--is-faster) - - [▶ Let's make a giant string!](#%E2%96%B6-lets-make-a-giant-string) - - [▶ Explicit typecast of strings](#%E2%96%B6-explicit-typecast-of-strings) - - [▶ Minor Ones](#%E2%96%B6-minor-ones) + * [Section: Strain your brain!](#section-strain-your-brain) + + [▶ Strings can be tricky sometimes](#-strings-can-be-tricky-sometimes) + + [▶ Splitsies](#-splitsies) + + [▶ Time for some hash brownies!](#-time-for-some-hash-brownies) + + [▶ The disorder within order](#-the-disorder-within-order) + + [▶ Keep trying?](#-keep-trying) + + [▶ Deep down, we're all the same](#-deep-down-were-all-the-same) + + [▶ For what?](#-for-what) + + [▶ Evaluation time discrepancy](#-evaluation-time-discrepancy) + + [▶ Messing around with `is` operator](#-messing-around-with-is-operator) + + [▶ A tic-tac-toe where X wins in the first attempt!](#-a-tic-tac-toe-where-x-wins-in-the-first-attempt) + + [▶ The sticky output function](#-the-sticky-output-function) + + [▶ The chicken-egg problem](#-the-chicken-egg-problem) + + [▶ `is not ...` is not `is (not ...)`](#-is-not--is-not-is-not-) + + [▶ The surprising comma](#-the-surprising-comma) + + [▶ Strings and the backslashes](#-strings-and-the-backslashes) + + [▶ not knot!](#-not-knot) + + [▶ Half triple-quoted strings](#-half-triple-quoted-strings) + + [▶ Midnight time doesn't exist?](#-midnight-time-doesnt-exist) + + [▶ What's wrong with booleans?](#-whats-wrong-with-booleans) + + [▶ Class attributes and instance attributes](#-class-attributes-and-instance-attributes) + + [▶ yielding None](#-yielding-none) + + [▶ Mutating the immutable!](#-mutating-the-immutable) + + [▶ The disappearing variable from outer scope](#-the-disappearing-variable-from-outer-scope) + + [▶ When True is actually False](#-when-true-is-actually-false) + + [▶ Lossy zip of iterators](#-lossy-zip-of-iterators) + + [▶ From filled to None in one instruction...](#-from-filled-to-none-in-one-instruction) + + [▶ Subclass relationships](#-subclass-relationships) + + [▶ The mysterious key type conversion](#-the-mysterious-key-type-conversion) + + [▶ Let's see if you can guess this?](#-lets-see-if-you-can-guess-this) + * [Section: Appearances are deceptive!](#section-appearances-are-deceptive) + + [▶ Skipping lines?](#-skipping-lines) + + [▶ Teleportation](#-teleportation) + + [▶ Well, something is fishy...](#-well-something-is-fishy) + * [Section: Watch out for the landmines!](#section-watch-out-for-the-landmines) + + [▶ Modifying a dictionary while iterating over it](#-modifying-a-dictionary-while-iterating-over-it) + + [▶ Stubborn `del` operation](#-stubborn-del-operation) + + [▶ Deleting a list item while iterating](#-deleting-a-list-item-while-iterating) + + [▶ Loop variables leaking out!](#-loop-variables-leaking-out) + + [▶ Beware of default mutable arguments!](#-beware-of-default-mutable-arguments) + + [▶ Catching the Exceptions](#-catching-the-exceptions) + + [▶ Same operands, different story!](#-same-operands-different-story) + + [▶ The out of scope variable](#-the-out-of-scope-variable) + + [▶ Be careful with chained operations](#-be-careful-with-chained-operations) + + [▶ Name resolution ignoring class scope](#-name-resolution-ignoring-class-scope) + + [▶ Needles in a Haystack](#-needles-in-a-haystack) + + [▶ Wild imports](#-wild-imports) + * [Section: The Hidden treasures!](#section-the-hidden-treasures) + + [▶ Okay Python, Can you make me fly?](#-okay-python-can-you-make-me-fly) + + [▶ `goto`, but why?](#-goto-but-why) + + [▶ Brace yourself!](#-brace-yourself) + + [▶ Let's meet Friendly Language Uncle For Life](#-lets-meet-friendly-language-uncle-for-life) + + [▶ Even Python understands that love is complicated](#-even-python-understands-that-love-is-complicated) + + [▶ Yes, it exists!](#-yes-it-exists) + + [▶ Ellipsis](#-ellipsis) + + [▶ Inpinity](#-inpinity) + + [▶ Let's mangle](#-lets-mangle) + * [Section: Miscellaneous](#section-miscellaneous) + + [▶ `+=` is faster](#--is-faster) + + [▶ Let's make a giant string!](#-lets-make-a-giant-string) + + [▶ Explicit typecast of strings](#-explicit-typecast-of-strings) + + [▶ Minor Ones](#-minor-ones) - [Contributing](#contributing) - [Acknowledgements](#acknowledgements) - [🎓 License](#-license) - - [Help](#help) - - [Surprise your geeky pythonist friends?](#surprise-your-geeky-pythonist-friends) - - [Need a pdf version?](#need-a-pdf-version) + * [Help](#help) + * [Surprise your geeky pythonist friends?](#surprise-your-geeky-pythonist-friends) + * [Need a pdf version?](#need-a-pdf-version) - + # Structure of the Examples @@ -623,7 +620,7 @@ array_4 = [400, 500, 600] --- -### ▶ Messing around with `is` operator^ +### ▶ Messing around with `is` operator The following is a very famous example present all over the internet. From 772a96c36fbecfa74f314c3b60c3f58315b068a5 Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sat, 13 Jul 2019 22:41:27 +0530 Subject: [PATCH 28/29] change order --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5787e06..f140259 100644 --- a/README.md +++ b/README.md @@ -262,9 +262,9 @@ some_dict[5] = "Python" ```py >>> some_dict[5.5] "Ruby" ->>> some_dict[5.0] +>>> some_dict[5.0] # "Python" destroyed the existence of "JavaScript"? "Python" ->>> some_dict[5] # "Python" destroyed the existence of "JavaScript"? +>>> some_dict[5] "Python" >>> complex_five = 5 + 0j From ffe64385762b91d9548777b165a4120f3ff2eccd Mon Sep 17 00:00:00 2001 From: Satwik Kansal Date: Sun, 21 Jul 2019 18:57:01 +0530 Subject: [PATCH 29/29] Commit insert_ids script --- irrelevant/insert_ids.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 irrelevant/insert_ids.py diff --git a/irrelevant/insert_ids.py b/irrelevant/insert_ids.py new file mode 100644 index 0000000..a16bf5f --- /dev/null +++ b/irrelevant/insert_ids.py @@ -0,0 +1,24 @@ +import uuid + +new_file = [] +original_file = [] + +fname = "../README.md" + + +def generate_random_id_comment(): + random_id = uuid.uuid4() + return f"" + + +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))