diff --git a/irrelevant/crippled_wtf.ipynb b/irrelevant/crippled_wtf.ipynb new file mode 100644 index 00000000..30b65359 --- /dev/null +++ b/irrelevant/crippled_wtf.ipynb @@ -0,0 +1,4832 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What The F*** Python\n", + "reference: https://github.com/satwikkansal/wtfpython" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `Walrus` (:=)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a := 5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(a := 5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(a, b := 7, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(a := 6, 9) == ((a := 6), 9)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "x = (a := 696, 9)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "x[0] is a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def some_func():\n", + " return 5\n", + "if a := some_func():\n", + " print(a)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "qq = 0\n", + "def some_funcqq():\n", + " global qq\n", + " qq += 1\n", + " return qq\n", + "\n", + "while ha := some_funcqq():\n", + " if ha == 5:\n", + " print(ha)\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"end part 1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "jupyter": { + "outputs_hidden": true + }, + "tags": [] + }, + "source": [ + "### global vs nonlocal" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def level1():\n", + " v = 2\n", + " def closure():\n", + " v += 1\n", + " closure()\n", + " return v\n", + "\n", + "print(level1())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "v = 1\n", + "def level1():\n", + " v = 2\n", + " def closure():\n", + " nonlocal v\n", + " v += 1\n", + " closure()\n", + " return v\n", + "\n", + "print(level1())\n", + "print(v)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "v = 1\n", + "def level1():\n", + " v = 2\n", + " def closure():\n", + " global v\n", + " v += 1\n", + " closure()\n", + " return v\n", + "\n", + "print(level1())\n", + "print(v)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "v = 1\n", + "def level1():\n", + " v = 2\n", + " def closure():\n", + " nonlocal v\n", + " print(v)\n", + " closure()\n", + "level1()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "v = 1\n", + "def closure():\n", + " global v\n", + " print(v)\n", + "closure()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"end part 2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### tricky string" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = \"some_string\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "id(\"some\" + \"_\" + \"string\") == id(a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* CPython optimization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = \"wtf!\"\n", + "b = \"wtf!\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "id(a) == id(b)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a, b = \"wtf!\", \"wtf!\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a is b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = \"wtf!\"; b = \"wtf!\"\n", + "a is b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* depend on python shell / ipython / as a script" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Output (< Python3.7 )\n", + "\n", + "```\n", + ">>> 'a' * 20 is 'aaaaaaaaaaaaaaaaaaaa'\n", + "True\n", + ">>> 'a' * 21 is 'aaaaaaaaaaaaaaaaaaaaa'\n", + "False\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### chained operations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(False == False) in [False]\n", + "False == (False in [False])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "False == False in [False]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "True is False == False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "False is False is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "False is False is False is False is not True is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Comparisons" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "-1 < 0 < 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(1 > 0) < 1\n", + "1 > (0 < 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(int(True))\n", + "True + 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### don't use `is`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "[] is []" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "() is ()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a, b = 257, 257" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a is b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = 257\n", + "b = 257\n", + "print(a is b)\n", + "\n", + "a = 256\n", + "b = 256\n", + "a is b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When you start up python the numbers from -5 to 256 will be allocated. These numbers are used a lot, so it makes sense just to have them ready. \n", + "Quoting from https://docs.python.org/3/c-api/long.html \n", + "> The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behavior of Python, in this case, is undefined. :-)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The optimization depends on OS, platform, version of python, interpreter, file or not, compiling, etc. \n", + "**don't count on these operations !!**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Hash brownies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict = {}\n", + "some_dict[5.5] = \"JavaScript\"\n", + "some_dict[5.0] = \"Ruby\"\n", + "some_dict[5] = \"Python\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict[5.5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict[5.0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict[5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "complex_five = 5 + 0j\n", + "type(complex_five)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict[complex_five]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "key of dict is **equivalence, not identity**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(5 == 5.0 == 5 + 0j)\n", + "print(hash(5) == hash(5.0) == hash(5 + 0j))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* python occupies 5.0 instead of 5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deep down, we're all the same." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class WTF: ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print( WTF() == WTF() )\n", + "print( WTF() is WTF() )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> tow different instance could not be equal" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hash(WTF()) == hash(WTF())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class WTF2(object):\n", + " def __init__(self): print(\"I\")\n", + " def __del__(self): print(\"D\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "id(WTF2()) == id(WTF2())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**lifetime**\n", + "1. create WTF2 and pass it to id\n", + "2. id get the reference number and return it\n", + "3. the scope of first WTF2 is out, so it would be destroyed\n", + "4. the next WTF2 would allocate from the same memory !!!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Disorder within order" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from collections import OrderedDict\n", + "\n", + "dictionary = dict()\n", + "dictionary[1] = 'a'; dictionary[2] = 'b';\n", + "\n", + "ordered_dict = OrderedDict()\n", + "ordered_dict[1] = 'a'; ordered_dict[2] = 'b';\n", + "\n", + "another_ordered_dict = OrderedDict()\n", + "another_ordered_dict[2] = 'b'; another_ordered_dict[1] = 'a';\n", + "\n", + "class DictWithHash(dict):\n", + " \"\"\"\n", + " A dict that also implements __hash__ magic.\n", + " \"\"\"\n", + " __hash__ = lambda self: 0\n", + "\n", + "class OrderedDictWithHash(OrderedDict):\n", + " \"\"\"\n", + " An OrderedDict that also implements __hash__ magic.\n", + " \"\"\"\n", + " __hash__ = lambda self: 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dictionary == ordered_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dictionary == another_ordered_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ordered_dict == another_ordered_dict" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> Equality tests between OrderedDict objects are order-sensitive and are implemented as list(od1.items())==list(od2.items())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len({dictionary, ordered_dict, another_ordered_dict})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "set or dict would only accept hashable value to be the `key`\n", + "> only immutable objects are hashable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ordered_dict = OrderedDictWithHash(ordered_dict)\n", + "another_ordered_dict = OrderedDictWithHash(another_ordered_dict)\n", + "another_set = set()\n", + "another_set.add(ordered_dict)\n", + "another_ordered_dict in another_set" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Keep trying..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def some_func():\n", + " try:\n", + " return 'from_try'\n", + " finally:\n", + " return 'from_finally'\n", + "\n", + "def another_func(): \n", + " for _ in range(3):\n", + " try:\n", + " continue\n", + " finally:\n", + " print(\"Finally!\")\n", + "\n", + "def one_more_func():\n", + " try:\n", + " for i in range(3):\n", + " try:\n", + " 1 / i\n", + " except ZeroDivisionError:\n", + " # Let's throw it here and handle it outside for loop\n", + " raise ZeroDivisionError(\"A trivial divide by zero error\")\n", + " finally:\n", + " print(\"Iteration\", i)\n", + " break\n", + " except ZeroDivisionError as e:\n", + " print(f\"Zero division error occurred: {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_func()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_func()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "1 / 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "one_more_func()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. finally woule run out of `return, break or continue`\n", + "2. The caveat here is, if the finally clause executes a return or break statement, the temporarily saved exception is discarded." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " for i in range(3):\n", + " try:\n", + " 1 / i\n", + " except ZeroDivisionError:\n", + " raise ZeroDivisionError(\"A trivial divide by zero error\")\n", + " finally:\n", + " print(\"Iteration\", i)\n", + "except ZeroDivisionError as e:\n", + " print(f\"Zero division error occurred: {e}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### For what?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_string = \"wtf\"\n", + "some_dict = {}\n", + "for i, some_dict[i] in enumerate(some_string):\n", + " i = 10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "del i\n", + "# the for-loop would be like\n", + "i, some_dict[i] = (0, 'w')\n", + "i, some_dict[i] = (1, 't')\n", + "i, some_dict[i] = (2, 'f')\n", + "some_dict" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Evaluation time discrepancy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import collections\n", + "def fun():\n", + " for i in range(4):\n", + " yield i\n", + "\n", + "print(type(fun))\n", + "print(type(fun()))\n", + "\n", + "gen = fun()\n", + "\n", + "print(f\"get item from generator: {list(gen)}\")\n", + "print(f\"after giving the number to list: {list(gen)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "array = [1, 8, 15]\n", + "# A typical generator expression\n", + "gen = (x for x in array if array.count(x) > 0)\n", + "array = [2, 8, 22]\n", + "print(f\"should be {array} but is {list(gen)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "array_1 = [1,2,3,4]\n", + "print(id(array_1))\n", + "gen_1 = (x for x in array_1)\n", + "array_1 = [1,2,3,4,5]\n", + "print(id(array_1))\n", + "\n", + "print(list(gen_1))\n", + "\n", + "array_2 = [1,2,3,4]\n", + "print(id(array_2))\n", + "gen_2 = (x for x in array_2)\n", + "array_2[:] = [1,2,3,4,5]\n", + "print(id(array_2))\n", + "\n", + "print(list(gen_2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "array_3 = [1, 2, 3]\n", + "array_4 = [10, 20, 30]\n", + "gen = (i + j for i in array_3 for j in array_4)\n", + "\n", + "array_5 = [4, 5, 6]\n", + "array_6 = [400, 500, 600]\n", + "\n", + "print(list(gen))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "first array_3 + second array_4" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. In a generator expression, the in clause is evaluated at declaration time, but the conditional clause is evaluated at runtime.\n", + "2. [PEP-289](https://www.python.org/dev/peps/pep-0289/#the-details) \n", + " > Only the outermost for-expression is evaluated immediately, the other expressions are deferred until the generator is run.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "array = [1, 8, 15]\n", + "# A typical generator expression\n", + "gen = (x for x in array if print(f\"{array} has {x} ?\"))\n", + "array = [2, 8, 22]\n", + "list(gen)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### is not ... is not is (not ...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "'something' is not None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "'something' is (not None)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### A tic-tac-toe where X wins in the first attempt!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "row = [\"\"] * 3\n", + "board = [row] * 3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(board)\n", + "print(board[0])\n", + "print(board[0][0])\n", + "board[0][0] = \"X\"\n", + "board" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "board = [['']*3 for _ in range(3)]\n", + "board[0][0] = \"X\"\n", + "print(board)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* string is immutable and the row, list, is mutable.\n", + "* use comprehensions to initialize list, dict, etc." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Schrödinger's variable *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "funcs = []\n", + "results = []\n", + "for x in range(7):\n", + " def some_func():\n", + " return x\n", + " funcs.append(some_func)\n", + " results.append(some_func()) # note the function call here\n", + "\n", + "funcs_results = [func() for func in funcs]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "funcs_results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "powers_of_x = [lambda x: x**i for i in range(10)]\n", + "[f(2) for f in powers_of_x]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import inspect\n", + "inspect.getclosurevars(funcs[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 42\n", + "[func() for func in funcs]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "inspect.getclosurevars(funcs[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "funcs = []\n", + "for x in range(7):\n", + " def some_func(x=x):\n", + " return x\n", + " funcs.append(some_func)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "funcs_results = [func() for func in funcs]\n", + "funcs_results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "inspect.getclosurevars(funcs[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The chicken-egg problem " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(3, int)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(type, object)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(object, type)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class A: ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(A, A)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(type, type)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(object, object)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "issubclass(int, object)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "issubclass(type, object)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "issubclass(object, type)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. everything is an object, which includes classes as well as their objects (instances)\n", + "2. type is metaclass\n", + "3. class type is the metaclass of class object, and every class (including type) has inherited directly or indirectly from object.\n", + "4. There is no real base class among object and type. The confusion in the above snippets is arising because we're thinking about 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,\n", + "\n", + " class A is an instance of class B, and class B is an instance of class A.\n", + " class A is an instance of itself.\n", + "\n", + "5. These relationships between object and type (both being instances of each other as well as themselves) exist in Python because of \"cheating\" at the implementation level." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Subclass relationships" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from collections.abc import Hashable\n", + "\n", + "issubclass(list, object)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "issubclass(object, Hashable)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "issubclass(list, Hashable)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> A -> B \n", + "> A > C \n", + "> B > C ?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list.__subclasscheck__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "object.__hash__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list.__hash__ == None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 3.4, refer to https://www.naftaliharris.com/blog/python-subclass-intransitivity/\n", + "class Hashable(metaclass=ABCMeta):\n", + "\n", + " __slots__ = ()\n", + "\n", + " @abstractmethod\n", + " def __hash__(self):\n", + " return 0\n", + "\n", + " @classmethod\n", + " def __subclasshook__(cls, C): \n", + " if cls is Hashable:\n", + " for B in C.__mro__:\n", + " if \"__hash__\" in B.__dict__:\n", + " if B.__dict__[\"__hash__\"]:\n", + " return True\n", + " break\n", + " return NotImplemented" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* involving metaclasses, abstract classes, subclasschecks, and subclasshooks." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Methods equality and identity" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class SomeClass:\n", + " def method(self):\n", + " pass\n", + "\n", + " @classmethod\n", + " def classm(cls):\n", + " pass\n", + "\n", + " @staticmethod\n", + " def staticm():\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(SomeClass.method is SomeClass.method)\n", + "print(SomeClass.classm is SomeClass.classm)\n", + "print(SomeClass.classm == SomeClass.classm)\n", + "print(SomeClass.staticm is SomeClass.staticm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "o1 = SomeClass()\n", + "o2 = SomeClass()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(o1.method == o2.method)\n", + "print(o1.method == o1.method)\n", + "print(o1.method is o1.method)\n", + "print(o1.classm is o1.classm)\n", + "print(o1.classm == o1.classm == o2.classm == SomeClass.classm)\n", + "print(o1.staticm is o1.staticm is o2.staticm is SomeClass.staticm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "o1.method is o1.method" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "o1.classm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Descriptor\n", + "\n", + "dic = [\"a\", \"b\", \"c\"]\n", + "\n", + "class DirectorySize:\n", + " def __get__(self, obj, objtype=None):\n", + " return len(obj.dirname)\n", + "\n", + "class Directory:\n", + "\n", + " size = DirectorySize() # Descriptor instance\n", + "\n", + " def __init__(self, dirname):\n", + " self.dirname = dirname # Regular instance attribute\n", + "g = Directory(dic)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g.size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dic.remove(\"b\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g.size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SomeClass.method" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "o1.classm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SomeClass.classm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "o1.staticm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SomeClass.staticm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> Having to create new \"method\" objects every time Python calls instance methods and having to modify the arguments every time in order to insert self affected performance badly. CPython 3.7 solved it by introducing new opcodes that deal with calling methods without creating the temporary method objects. This is used only when the accessed function is actually called, so the snippets here are not affected, and still generate methods :)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "o1_method1 = o1.method\n", + "o1_method2 = o1.method\n", + "print(id(o1_method1))\n", + "print(id(o1_method2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(o1.method.__func__ is SomeClass.method)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### All-true-ation " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all([True, True, True])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all([])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all([[]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all([[[]]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# like\n", + "def all(iterable):\n", + " for element in iterable:\n", + " if not element:\n", + " return False\n", + " return True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all([]) = > not check and return True\n", + "all([[]]) = > check [], not [] => False\n", + "all([[[]]]) = > check [[]], not [[]] = > True\n", + "all(" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "not [[]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all([[[[[[]]]]]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The surprising comma" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def f(x, y,):\n", + " print(x, y)\n", + "\n", + "def g(x=4, y=5,):\n", + " print(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def h(x, **kwargs,):\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def h(*args,):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "before 3.6 version, the tailing comma is syntax error in python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Strings and the backslashes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"\\\"\")\n", + "print(r\"\\\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(r\"\\\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r'\\'' == \"\\\\'\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. he backslash is used to escape special characters\n", + "2. use r'' to be `a raw string` literal\n", + "3. 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." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r\"\\\\\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r\"\\n\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(r\"\\\\\") # r\"\\\\\" would turn into \"\\\\\\\\\" and passe it to print" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### not knot!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = True\n", + "y = False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "not x == y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x == not y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. `==` operator has higher precedence than `not` operator in Python \n", + "2. `not x == y` is equivalent to `not (x == y)`\n", + "3. `x == not y` would be `(x == not) y`\n", + "4. The parser expected the `not` token to be a part of the `not in` operator\n", + "5. `not in` has same precedence as `==`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x not in == y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x not == y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x == not in y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Half triple-quoted strings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('wtfpython''')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"wtfpython\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"\"\"wtfpython\") # print('''wtfpython')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"wtfpython\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"ab\" \"cd\" # \"ab\" + \"cd\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What's wrong with booleans?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mixed_list = [False, 1.0, \"some_string\", 3, True, [], False]\n", + "integers_found_so_far = 0\n", + "booleans_found_so_far = 0\n", + "\n", + "for item in mixed_list:\n", + " if isinstance(item, int):\n", + " integers_found_so_far += 1\n", + " elif isinstance(item, bool):\n", + " booleans_found_so_far += 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "integers_found_so_far" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "booleans_found_so_far" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_bool = True\n", + "\"wtf\" * some_bool" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_bool = False\n", + "\"wtf\" * some_bool" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# < 3.*\n", + "def tell_truth():\n", + " True = False\n", + " if True == False:\n", + " print(\"I have lost faith in truth!\")\n", + "tell_truth()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Future~" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "import threading\n", + "import concurrent\n", + "import signal\n", + "import time\n", + "from enum import Enum, auto\n", + "\n", + "class action(Enum):\n", + " cancel = auto()\n", + " shutdown = auto()\n", + " signal = auto()\n", + "\n", + "def blocking():\n", + " print(f\"block: {threading.get_ident()}\")\n", + " time.sleep(5)\n", + " print(\"5s\")\n", + " time.sleep(8)\n", + " print(\"13s\")\n", + " time.sleep(2)\n", + " print(\"15s\")\n", + "\n", + "async def haha_proces(fut, exe, kill):\n", + " import subprocess\n", + " print(threading.get_ident())\n", + " print(\"haha go\")\n", + " await asyncio.sleep(10)\n", + " if kill == action.cancel:\n", + "\n", + " fut.cancel()\n", + " fut.set_exception(asyncio.TimeoutError)\n", + "\n", + " elif kill == action.shutdown:\n", + "\n", + " exe.shutdown(wait=True)\n", + "\n", + " elif kill == action.signal:\n", + "\n", + " for t in exe._threads:\n", + " print(f\"kill {t.ident}\")\n", + " signal.pthread_kill(t.ident, signal.SIGTERM)\n", + "\n", + " print(\"haha out\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "async def main(action: action):\n", + " print('Hello ...')\n", + " loop = asyncio.get_event_loop()\n", + " exe = concurrent.futures.ThreadPoolExecutor(max_workers=1, thread_name_prefix='asyncio')\n", + " future = loop.run_in_executor(exe, blocking)\n", + " task = loop.create_task(haha_proces(future, exe, action))\n", + " try:\n", + " await asyncio.gather(task, future, return_exceptions=True)\n", + " except Exception as e:\n", + " print(f\"{e}\")\n", + " print('End World!')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "await main(action.cancel)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "await main(action.shutdown)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "await main(action.signal)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def blocking():\n", + " def signal_handler(sig, frame):\n", + " import sys\n", + " sys.exit(0)\n", + " try:\n", + " signal.signal(signal.SIGINT, signal_handler)\n", + " except Exception as e:\n", + " print(f\"{e}\")\n", + " print(f\"block: {threading.get_ident()}\")\n", + " \n", + "await main(action.signal)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Colon in the List" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = [1,2,3,4,5,6,7,8]\n", + "a[0::3]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[0:3]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[0:5:2]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[-1::4]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[-1::-4]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[1::4]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[1:-3]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[::]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[1:]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[-3:-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[-3:-8]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[5:2]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Class attributes and instance attributes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class A:\n", + " x = 1\n", + "\n", + "class B(A):\n", + " pass\n", + "\n", + "class C(A):\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "A.x, B.x, C.x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "B.x = 2\n", + "A.x, B.x, C.x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(id(A.x), id(C.x))\n", + "A.x = 3\n", + "A.x, B.x, C.x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(id(A.x), id(C.x))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = A()\n", + "print(a.x is a.__class__.x)\n", + "a.x += 1\n", + "print(a.x is a.__class__.x)\n", + "a.x, A.x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class SomeClass:\n", + " some_var = 15\n", + " some_list = [5]\n", + " another_list = [5]\n", + " def __init__(self, x):\n", + " self.some_var = x + 1\n", + " self.some_list = self.some_list + [x]\n", + " self.another_list += [x]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_obj = SomeClass(420)\n", + "some_obj.some_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_obj.another_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_obj = SomeClass(111)\n", + "another_obj.some_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_obj.another_list is SomeClass.another_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_obj.another_list is some_obj.another_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_obj.another_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* reassigned some_list by `=`, but reused, in-place modification, list by `+=`\n", + "* if the variable is immutable, it would copy the value and modify it with to the new object under `+=` operator." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### yielding None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_iterable = ('a', 'b')\n", + "\n", + "def some_func(val):\n", + " return \"something\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "[(yield x) for x in some_iterable]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* 3.8+ does not allow yield inside list comprehension\n", + "* before 3.7, yield in list has bug and could not generate expected result." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Yielding from... return! *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def some_func(x):\n", + " if x == 3:\n", + " return [\"wtf\"]\n", + " else:\n", + " yield from range(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(some_func(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> `yield from` is used to generate a generator from another generator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def some_func(x):\n", + " if x == 3:\n", + " return [\"wtf\"]\n", + " else:\n", + " for i in range(x):\n", + " yield i" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(some_func(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> \"... return expr in a generator causes StopIteration(expr) to be raised upon exit from the generator.\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " next(some_func(3))\n", + "except StopIteration as e:\n", + " some_string = e.value\n", + "some_string" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Nan-reflexivity *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = float('inf')\n", + "b = float('nan')\n", + "c = float('-iNf') # These strings are case-insensitive\n", + "d = float('nan')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "c" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "float('some_other_string')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a == -c" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "None == None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b == d" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "50 / a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a / a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "23 + b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = float('nan')\n", + "y = x / x\n", + "y is y # identity holds" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "y == y # equality fails of y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "[y] == [y] # but the equality succeeds for the list containing y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `'inf'` and `'nan'` represent mathematical `\"infinity\"` and `\"not a number\"` respectively\n", + "![](https://i.stack.imgur.com/OZKP7.jpg)\n", + "* in IEEE 754, `nan` is not euqal to `nan`\n", + "* in collection like `list`, it would compare identity first and compare `value` or `__eq__` if the identity is not the same." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mutating the immutable!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_tuple = (\"A\", \"tuple\", \"with\", \"values\")\n", + "another_tuple = ([1, 2], [3, 4], [5, 6])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_tuple[2] = \"change this\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_tuple[2].append(1000)\n", + "another_tuple" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_tuple[2] += [99, 999]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_tuple" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> `__iadd__` is called by `+=` and it's like extend in list but it would return itself" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a_list = []\n", + "print(a_list.__iadd__([1,2]))\n", + "a_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b_list = []\n", + "print(b_list.extend([1,2]))\n", + "b_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In conclusion: \n", + "```\n", + " a += b \n", + " => a = a.__iadd__(b)\n", + "``` \n", + "It would use assign operator in low level " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The disappearing variable from outer scope" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "e = 7\n", + "try:\n", + " raise Exception()\n", + "except Exception as e:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(e)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " raise Exception()\n", + "except Exception as e:\n", + " try:\n", + " pass\n", + " finally:\n", + " del e" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* only delete in the scope" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def f(x):\n", + " del(x)\n", + " print(x)\n", + "\n", + "x = 5\n", + "y = [5, 4, 3]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f(x)\n", + "f(y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The mysterious key type conversion" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class SomeClass(str):\n", + " pass\n", + "\n", + "some_dict = {'s': 42}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(list(some_dict.keys())[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s = SomeClass('s')\n", + "some_dict[s] = 40\n", + "some_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(list(some_dict.keys())[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> SomeClass inherits all methods from `str`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class SomeClass(str):\n", + " def __eq__(self, other):\n", + " return (\n", + " type(self) is SomeClass\n", + " and type(other) is SomeClass\n", + " and super().__eq__(other)\n", + " )\n", + "\n", + " __hash__ = str.__hash__\n", + "\n", + "some_dict = {'s':42}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> When we define a custom `__eq__`, Python stops automatically inheriting the \n", + "> `__hash__` method, so we need to define it as well" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "s = SomeClass('s')\n", + "some_dict[s] = 40\n", + "some_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "keys = list(some_dict.keys())\n", + "type(keys[0]), type(keys[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> SomeClass' 's' is not equal str's 's'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's see if you can guess this?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a, b = a[b] = {}, 5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`(target_list \"=\")+ (expression_list | yield_expression)`\n", + "1. target_list is `a, b = a[b]` and expression_list is `{}, 5`\n", + "2. calculate expression_list and unpack it into target_list from left to right\n", + "3. `circular reference`: the `{...}` in the output refers to the same object that a is already referencing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a, b = {}, 5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "now, the target_list is a[b] and the expression_list is the result in the first manipulation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a[b] = (a, b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Section: Slippery Slopes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Modifying a dictionary while iterating over it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = {0: None}\n", + "\n", + "for i in x:\n", + " del x[i]\n", + " x[i+1] = None\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "before 3.5 and 2.*, the answer would be 0, 1, 2 ... n. The n depends on the dict resize implementation. \n", + "After 3.6+, python would raise a exception on it." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Stubborn del operation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class SomeClass:\n", + " def __del__(self):\n", + " print(\"Deleted!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = SomeClass()\n", + "y = x\n", + "del x; del y" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = SomeClass()\n", + "y = x\n", + "del x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "id_y = id(y)\n", + "y # ref += 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "del y\n", + "globals()['y']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from collections.abc import Iterable\n", + "key = list(globals().keys())\n", + "golbals = globals()\n", + "while key:\n", + " k = key.pop()\n", + " if id_y == id(golbals[k]):\n", + " print(f'{k}: {golbals[k]}')\n", + " del golbals[k]\n", + "del key; del golbals" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The out of scope variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = 1\n", + "def some_func():\n", + " return a\n", + "\n", + "def another_func():\n", + " # global a\n", + " a += 1\n", + " return a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def some_closure_func():\n", + " a = 1\n", + " def some_inner_func():\n", + " return a\n", + " return some_inner_func()\n", + "\n", + "def another_closure_func():\n", + " a = 1\n", + " def another_inner_func():\n", + " # nonlocal a\n", + " a += 1\n", + " return a\n", + " return another_inner_func()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_func()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_func()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_closure_func()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_closure_func()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deleting a list item while iterating" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_1 = [1, 2, 3, 4]\n", + "list_2 = [1, 2, 3, 4]\n", + "list_3 = [1, 2, 3, 4]\n", + "list_4 = [1, 2, 3, 4]\n", + "\n", + "for idx, item in enumerate(list_1):\n", + " del item\n", + "\n", + "for idx, item in enumerate(list_2):\n", + " list_2.remove(item)\n", + "\n", + "for idx, item in enumerate(list_3[:]):\n", + " list_3.remove(item)\n", + "\n", + "for idx, item in enumerate(list_4):\n", + " list_4.pop(idx)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list_4" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* del is remove reference, but list_1 and return of enumerate would refer to it. the reference count would be two.\n", + "* remove and pop could immediately remove item from list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for idx, item in enumerate(list_1):\n", + " list_1.remove(item)\n", + " print(list_1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```c\n", + "static PyObject *\n", + "enum_next(enumobject *en)\n", + "{\n", + " PyObject *next_index;\n", + " PyObject *next_item;\n", + " PyObject *result = en->en_result;\n", + " PyObject *it = en->en_sit;\n", + " PyObject *old_index;\n", + " PyObject *old_item;\n", + "\n", + " next_item = (*Py_TYPE(it)->tp_iternext)(it);\n", + " if (next_item == NULL)\n", + " return NULL;\n", + "\n", + " if (en->en_index == PY_SSIZE_T_MAX)\n", + " return enum_next_long(en, next_item);\n", + "\n", + " next_index = PyLong_FromSsize_t(en->en_index);\n", + " if (next_index == NULL) {\n", + " Py_DECREF(next_item);\n", + " return NULL;\n", + " }\n", + " en->en_index++;\n", + "\n", + " if (Py_REFCNT(result) == 1) {\n", + " Py_INCREF(result);\n", + " old_index = PyTuple_GET_ITEM(result, 0);\n", + " old_item = PyTuple_GET_ITEM(result, 1);\n", + " PyTuple_SET_ITEM(result, 0, next_index);\n", + " PyTuple_SET_ITEM(result, 1, next_item);\n", + " Py_DECREF(old_index);\n", + " Py_DECREF(old_item);\n", + " // bpo-42536: The GC may have untracked this result tuple. Since we're\n", + " // recycling it, make sure it's tracked again:\n", + " if (!_PyObject_GC_IS_TRACKED(result)) {\n", + " _PyObject_GC_TRACK(result);\n", + " }\n", + " return result;\n", + " }\n", + " result = PyTuple_New(2);\n", + " if (result == NULL) {\n", + " Py_DECREF(next_index);\n", + " Py_DECREF(next_item);\n", + " return NULL;\n", + " }\n", + " PyTuple_SET_ITEM(result, 0, next_index);\n", + " PyTuple_SET_ITEM(result, 1, next_item);\n", + " return result;\n", + "}\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Lossy zip of iterators *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = list(range(7))\n", + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "first_three, remaining = numbers[:3], numbers[3:]\n", + "first_three, remaining" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers_iter = iter(numbers)\n", + "list(zip(numbers_iter, first_three)) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(zip(numbers_iter, remaining))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```python\n", + "def zip(*iterables):\n", + " sentinel = object()\n", + " iterators = [iter(it) for it in iterables] # unpack the iterables into iter\n", + " while iterators:\n", + " result = []\n", + " for it in iterators: # loop all iterators\n", + " elem = next(it, sentinel) # get the next value of the iter\n", + " if elem is sentinel: return # end the zip if any iter is empty\n", + " result.append(elem)\n", + " yield tuple(result)\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = list(range(7))\n", + "numbers_iter = iter(numbers)\n", + "list(zip(first_three, numbers_iter))\n", + "list(zip(remaining, numbers_iter))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Loop variables leaking out!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for x in range(7):\n", + " if x == 6:\n", + " print(x, ': for x inside loop')\n", + "print(x, ': x in global')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This time let's initialize x first\n", + "x = -1\n", + "for x in range(7):\n", + " if x == 6:\n", + " print(x, ': for x inside loop')\n", + "print(x, ': x in global')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print([x for x in range(5)])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f\"python3 would got {x=}, but python2 would got 4 instead\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The differences in the output of Python 2.x and Python 3.x interpreters for list comprehension example can be explained by following change documented in What’s New In Python 3.0 changelog:\n", + "> \"List comprehensions no longer support the syntactic form [... for var in item1, item2, ...]. Use [... for var in (item1, item2, ...)] instead. Also, note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a list() constructor, and in particular, the loop control variables are no longer leaked into the surrounding scope.\"\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Name resolution ignoring class scope" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 5\n", + "class SomeClass:\n", + " x = 17\n", + " y = (x for i in range(10)) # generator\n", + " @classmethod\n", + " def get(cls):\n", + " return (cls.x for i in range(10))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(SomeClass.y)[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(SomeClass.get())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 5\n", + "class SomeClass:\n", + " x = 17\n", + " y = [x for i in range(10)] # comprehension\n", + " @staticmethod\n", + " def get():\n", + " return x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SomeClass.y[0] # python3 => 5, python2 => 17" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Starting from Python 3.X, list comprehensions also have their own scope.\n", + "* A generator expression has its own scope.\n", + "* Scopes nested inside class definition ignore names bound at the class level." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "SomeClass.get()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 5\n", + "def get():\n", + " x = 6\n", + " return x\n", + "print(get())\n", + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> [refer to global and nonlocal](crippled_wtf.ipynb#global-vs-nonlocal) and [out of scope](crippled_wtf.ipynb#The-out-of-scope-variable)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Rounding like a banker *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(1.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "0.5.__round__()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since Python 3.0, round() uses `banker's rounding` where .5 fractions are rounded to the nearest even number. Not follow `IEEE 754`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "2.675.__round__(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See [Floating Point Arithmetic: Issues and Limitations for more information](https://docs.python.org/3/tutorial/floatingpoint.html#tut-fp-issues)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "np.round_(2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "np.ceil(2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "np.floor(2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def round_(x: float):\n", + " ret = np.round_(x)\n", + " if ret < x and ret + 1/2 == x:\n", + " return np.ceil(x)\n", + " return ret" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round_(2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round_(1.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Needles in a Haystack *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "t = ('one', 'two')\n", + "for i in t:\n", + " print(i)\n", + "\n", + "t = ('one')\n", + "for i in t:\n", + " print(i)\n", + "\n", + "t = ()\n", + "print(t)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = \"python\"\n", + "b = \"javascript\"\n", + "assert a != b, \"Both languages are different\"\n", + "assert (a == b, \"Both languages are different\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> https://docs.python.org/3/reference/simple_stmts.html#assert \n", + "\n", + "the assert is statment, so do not use it like function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### The other cases are trivial, please check https://github.com/satwikkansal/wtfpython#-needles-in-a-haystack-" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Splitsies *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "''.split(' ')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "''.split()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "' a '.split(' ')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> 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 ['']." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Wild imports *" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "if import with wildcard `*`, the invisible naming would not be imported.\n", + "```python\n", + "from module import *\n", + "_abc()\n", + "Traceback (most recent call last):\n", + " File \"\", line 1, in \n", + "NameError: name '_abc' is not defined\n", + "``` \n", + "but we could import the name like\n", + "```python\n", + "from module import _abc\n", + "_abc()\n", + "``` " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "we also could use `__all__` to restrict the import members.\n", + "```python\n", + "# module.py\n", + "__all__ = [`_abc`]\n", + "def _abc(): ...\n", + "def abc(): ...\n", + "```\n", + "the import would like\n", + "```python\n", + "from module import *\n", + "_abc()\n", + "abc()\n", + "Traceback (most recent call last):\n", + " File \"\", line 1, in \n", + "NameError: name 'abc' is not defined\n", + "```\n", + "However, we still could use `module.abc()` or `from module import abc` to get `abc`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### All sorted? *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = 7, 8, 9 # tuple\n", + "sorted(x) == x" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "y = reversed(x)\n", + "sorted(y) == sorted(y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. be aware the return value\n", + " * reversed return iter object\n", + " * sorted return list object\n", + "2. list compares to tuple always return `False` in Python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Midnight time doesn't exist?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "\n", + "midnight = datetime(2018, 1, 1, 0, 0)\n", + "midnight_time = midnight.time()\n", + "\n", + "noon = datetime(2018, 1, 1, 12, 0)\n", + "noon_time = noon.time()\n", + "\n", + "if midnight_time:\n", + " print(\"Time at midnight is\", midnight_time)\n", + "\n", + "if noon_time:\n", + " print(\"Time at noon is\", noon_time)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "before 3.5, datetime(2018, 1, 1, 0, 0) would equal to empty object in if condition" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### goto, but why?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. `goto`: an April Fool's joke on 1st April 2004\n", + "2. `goto-label`: use new opcode and rearrage code flow\n", + "3. `goto-statement`: use new opcode and rearrage code flow \n", + "\n", + "the implementation is using frame control or opcode rewriten. \n", + "However, if you really want to use `goto`, the better way is using [`try-except`](https://docs.python.org/3/faq/design.html#why-is-there-no-goto) to do it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class label(Exception): pass # declare a label\n", + "\n", + "try:\n", + " if True: raise label() # goto label\n", + "except label: # where to goto\n", + " print('here')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import dis\n", + "def a(i):\n", + " print(i)\n", + " return i+1\n", + "q = dis.Bytecode(a)\n", + "print(q.dis())\n", + "# https://docs.python.org/3/library/dis.html" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Brace yourself!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "WTF, I like `C`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import braces" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's meet Friendly Language Uncle For Life" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import barry_as_FLUFL\n", + "# This is relevant to PEP-401 released on April 1, 2009 " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"Ruby\" != \"Python\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"a\" == \"b\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"Ruby\" <> \"Python\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It works well in an interactive environment, but could not run in file. Please use `eval` in file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import barry_as_FLUFL\n", + "print(eval('\"Ruby\" <> \"Python\"'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Even Python understands that love is complicated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import this" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "love = this" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "love is not True or False\n", + "# love (is not) True or False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Yes, it exists!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def does_exists_num(l, to_find):\n", + " for num in l:\n", + " if num == to_find:\n", + " print(\"Exists!\")\n", + " break\n", + " else:\n", + " print(\"Does not exist\")\n", + "does_exists_num([1, 2, 3, 4, 5], 4)\n", + "does_exists_num([1, 2, 3, 4, 5], 6)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " pass\n", + "except:\n", + " print(\"Exception occurred!!!\")\n", + "else:\n", + " print(\"Try block executed successfully...\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ellipsis *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Ellipsis == ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "three_dimensional_array = np.arange(8).reshape(2, 2, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "np.array_equal(three_dimensional_array[..., 1], three_dimensional_array[:,:,1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Tuple\n", + "def others(t: Tuple[str, ...]):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Inpinity" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "infinity = float('infinity')\n", + "print(hash(infinity))\n", + "\n", + "print(hash(float('-inf')))\n", + "print(hash(float('inf')))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Interestingly, the hash of float('-inf') is \"-10⁵ x π\" in Python 3, whereas \"-10⁵ x e\" in Python 2." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's mangle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Yo(object):\n", + " def __init__(self):\n", + " # Let's try something symmetrical this time\n", + " self.__honey = True\n", + " self.__honey__ = True\n", + " self.bro = True\n", + " def some_func(self):\n", + " return __variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obj = Yo()\n", + "for i in obj.__dict__:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obj.some_func()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_Yo__variable = \"Cool\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "obj.some_func()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> Also, if the mangled name is longer than 255 characters, truncation will happen." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Teleportation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "def energy_send(x):\n", + " # Initializing a numpy array\n", + " np.array([float(x)])\n", + "\n", + "def energy_receive():\n", + " # Return an empty numpy array\n", + " return np.empty((), dtype=np.float64).tolist()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "energy_send(123.456)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "energy_receive()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. numpy does not clear the memory before free\n", + "2. after creating empty array in numpy, it does not initial the memory.\n", + "> In `c`, this would teach us a lesson." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's make a giant string!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def add_string_with_plus_bad(iters):\n", + " s = \"\"\n", + " for i in range(iters):\n", + " s = s + \"x\" + \"y\" + \"z\"\n", + " assert len(s) == 3*iters\n", + "\n", + "def add_string_with_plus(iters):\n", + " s = \"\"\n", + " for i in range(iters):\n", + " s += \"xyz\"\n", + " assert len(s) == 3*iters\n", + "\n", + "def add_bytes_with_plus(iters):\n", + " s = b\"\"\n", + " for i in range(iters):\n", + " s += b\"xyz\"\n", + " assert len(s) == 3*iters\n", + "\n", + "def add_string_with_format(iters):\n", + " fs = \"{}\"*iters\n", + " s = fs.format(*([\"xyz\"]*iters))\n", + " assert len(s) == 3*iters\n", + "\n", + "def add_string_with_join(iters):\n", + " l = []\n", + " for i in range(iters):\n", + " l.append(\"xyz\")\n", + " s = \"\".join(l)\n", + " assert len(s) == 3*iters\n", + "\n", + "def convert_list_to_string(iters):\n", + " l = \"xyz\" * iters\n", + " s = \"\".join(l)\n", + " assert len(s) == 3*iters" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "use `%timeit` to get the runtime in ipython, and module `timeit` in python" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%timeit -n1000 add_string_with_plus_bad(1000)\n", + "%timeit -n1000 add_string_with_plus(1000)\n", + "%timeit -n1000 add_bytes_with_plus(1000)\n", + "%timeit -n1000 add_string_with_format(1000)\n", + "%timeit -n1000 add_string_with_join(1000)\n", + "%timeit -n1000 convert_list_to_string(1000)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Slowing down dict lookups *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict = {str(i): 1 for i in range(1_000_000)}\n", + "another_dict = {str(i): 1 for i in range(1_000_000)}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%timeit some_dict['5']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_dict[1] = 1\n", + "%timeit some_dict['5']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%timeit another_dict['5']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "another_dict[1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%timeit another_dict['5'] # before 3.x, it would be slower. not sure the x exactly would be." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* some operation would break the key-searching optimizing.\n", + " * \"str\" only would faster than generic search(`__eq__`)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bloating instance dicts *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "\n", + "class SomeClass:\n", + " def __init__(self):\n", + " self.some_attr1 = 1\n", + " self.some_attr2 = 2\n", + " self.some_attr3 = 3\n", + " self.some_attr4 = 4\n", + "\n", + "\n", + "def dict_size(o):\n", + " return sys.getsizeof(o.__dict__)\n", + "\n", + "o1 = SomeClass()\n", + "o2 = SomeClass()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print (dict_size(o1))\n", + "print (dict_size(o2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "del o1.some_attr1\n", + "print (dict_size(o1))\n", + "print (dict_size(o2))\n", + "dict_size(SomeClass())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "* CPython is able to reuse the same \"keys\" object in multiple dictionaries. This was added in PEP 412 with the motivation to reduce memory usage, specifically in dictionaries of instances - where keys (instance attributes) tend to be common to all instances.\n", + "* This optimization is entirely seamless for instance dictionaries, but it is disabled if certain assumptions are broken.\n", + "* Key-sharing dictionaries **do not support deletion**; if an instance attribute is deleted, the dictionary is \"unshared\", and key-sharing is **disabled** for all future instances of the same class.\n", + "* Additionaly, if the dictionary keys have been **resized** (because new keys are inserted), they are kept shared only if they are used by a exactly single dictionary (this allows adding many attributes in the `__init__` of the very first created instance, without causing an \"unshare\"). If **multiple instances exist when a resize happens**, key-sharing is **disabled** for all future instances of the same class: CPython can't tell if your instances are using the same set of attributes anymore, and decides to bail out on attempting to share their keys.\n", + "* **A small tip, if you aim to lower your program's memory footprint: don't delete instance attributes, and make sure to initialize all attributes in your `__init__`!**\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Minor Ones *" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "++4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "3 --0-- 5 == 3 + 5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "-- is + \n", + "-(-) => + \n", + "+(+) => + " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = 10\n", + "a -=- 1\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "False ** False == True # => 0**0 == 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `@` or `__matmul__` operator in 3.5+" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "np.array([2, 2, 2]) @ np.array([7, 8, 8])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python uses 2 bytes for local variable storage in functions. In theory, this means that only 65536 variables can be defined in a function. However, python has a handy solution built in that can be used to store more than 2^16 variable names. The following code demonstrates what happens in the stack when more than 65536 local variables are defined (Warning: This code prints around 2^18 lines of text, so be prepared!):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import dis\n", + "exec(\"\"\"\n", + "def f():\n", + " \"\"\" + \"\"\"\n", + " \"\"\".join([\"X\" + str(x) + \"=\" + str(x) for x in range(2)]))\n", + "\n", + "f()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# dis.dis(f) <-- call it would print a lot of ops\n", + "...\n", + "65334 521626 EXTENDED_ARG 255\n", + " 521628 LOAD_CONST 65332 (65331)\n", + " 521630 EXTENDED_ARG 255\n", + " 521632 STORE_FAST 65331 (X65331)\n", + " ...\n", + "65541 523292 EXTENDED_ARG 1\n", + " 523294 EXTENDED_ARG 256\n", + " 523296 LOAD_CONST 65539 (65538)\n", + " 523298 EXTENDED_ARG 1\n", + " 523300 EXTENDED_ARG 256\n", + " 523302 STORE_FAST 65538 (X65538)\n", + " 523304 LOAD_CONST 0 (None)\n", + " 523306 RETURN_VALUE" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import dis\n", + "exec(\"\"\"\n", + "def f():\n", + " \"\"\" + \"\"\"\n", + " \"\"\".join([\"X\" + str(x) + \"=\" + str(x) for x in range(5)]))\n", + "\n", + "f()\n", + "dis.dis(f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### use multiple processes instead of thread\n", + "**Global Interpreter Lock**\n", + "1. this lock would be called before every c-level call. The only way to disable it is call.\n", + "2. The library sould use python api to create thread or add GIL in the thread to lock with GIL.\n", + "\n", + "Luckily, many potentially blocking or long-running operations, such as I/O, image processing, and NumPy number crunching, happen outside the GIL. Therefore it is only in multithreaded programs that spend a lot of time inside the GIL, interpreting CPython bytecode, that the GIL becomes a bottleneck. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```c\n", + "PyObject* abc(void){\n", + " ...\n", + " Py_BEGIN_ALLOW_THREADS\n", + " // release the GIL\n", + " Py_END_ALLOW_THREADS\n", + " return\n", + "}\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In cython, it still has GIL but able disable it.\n", + "**cython only**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with nogil:\n", + " # release the GIL here\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# File some_file.py\n", + "import time\n", + "\n", + "print(\"wtfpython\", end=\"_\") # the buffer flush immediately only if it end with `/n`\n", + "time.sleep(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(\"wtfpython\", end=\"_\", flush=True)\n", + "time.sleep(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "some_str = \"wtfpython\"\n", + "some_list = ['w', 't', 'f', 'p', 'y', 't', 'h', 'o', 'n']\n", + "print(some_list is some_list[:]) # False expected because a new object is created.\n", + "print(some_str is some_str[:]) # True because strings are immutable, so making a new object is of not much use.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "'abc'.count('') == 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def count(s, sub):\n", + " result = 0\n", + " for i in range(len(s) + 1 - len(sub)):\n", + " result += (s[i:i + len(sub)] == sub)\n", + " return result" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}