{"id":15521297,"url":"https://github.com/kengz/python","last_synced_at":"2025-04-23T03:51:20.854Z","repository":{"id":74754666,"uuid":"97320050","full_name":"kengz/python","owner":"kengz","description":"Python Style Guide","archived":false,"fork":false,"pushed_at":"2017-07-29T16:48:19.000Z","size":98,"stargazers_count":62,"open_issues_count":0,"forks_count":17,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-17T19:17:24.513Z","etag":null,"topics":["linting","naming-conventions","python","python3","style-guide","styleguide"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kengz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-15T14:11:53.000Z","updated_at":"2025-03-29T11:11:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"90e881ed-8b54-4085-b39e-4348a3a30f5e","html_url":"https://github.com/kengz/python","commit_stats":{"total_commits":38,"total_committers":1,"mean_commits":38.0,"dds":0.0,"last_synced_commit":"7cded574f56dea09ec5fdf7368c0bc90115b6d57"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kengz%2Fpython","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kengz%2Fpython/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kengz%2Fpython/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kengz%2Fpython/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kengz","download_url":"https://codeload.github.com/kengz/python/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250366685,"owners_count":21418768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["linting","naming-conventions","python","python3","style-guide","styleguide"],"created_at":"2024-10-02T10:33:39.277Z","updated_at":"2025-04-23T03:51:20.848Z","avatar_url":"https://github.com/kengz.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Python Style Guide\n\n*A mostly reasonable approach to Python*\n\nThis is a modern Python style guide born from numerous open source projects and collaborative discussions. You can use this concise guide on its own or with PEP8. This guide focuses on the cutting edge of Python 3.\n\nA style guide must constantly adapt to newer norms, and its basis is community. If you find this useful, please share. If you have updates to contribute, please submit a PR so that the community can adopt them.\n\nOther Style Guides\n\n  - [JavaScript](https://github.com/airbnb/javascript)\n  - [Ruby](https://github.com/airbnb/ruby)\n\n## Table of Contents\n\n  1. [Types](#types)\n  1. [References](#references)\n  1. [Dictionaries](#dictionaries)\n  1. [Lists](#lists)\n  1. [Destructuring](#destructuring)\n  1. [Strings](#strings)\n  1. [Functions](#functions)\n  1. [Classes \u0026 Constructors](#classes--constructors)\n  1. [Modules](#modules)\n  1. [Iterators and Generators](#iterators-and-generators)\n  1. [Variables](#variables)\n  1. [Comparison Operators \u0026 Equality](#comparison-operators--equality)\n  1. [Comments](#comments)\n  1. [Whitespace](#whitespace)\n  1. [Commas](#commas)\n  1. [Naming Conventions](#naming-conventions)\n  1. [Testing](#testing)\n  1. [Resources](#resources)\n  1. [Contributors](#contributors)\n\n## Types\n\n  \u003ca name=\"types--primitives\"\u003e\u003c/a\u003e\u003ca name=\"1.1\"\u003e\u003c/a\u003e\n  - [1.1](#types--primitives) **Primitives**: When you access a primitive type you work directly on its value.\n\n    - `string`\n    - `number`\n    - `boolean`\n    - `None`\n\n    ```python\n    foo = 1\n    bar = foo\n\n    bar = 9\n\n    print(foo, bar) # =\u003e 1, 9\n    ```\n\n  \u003ca name=\"types--complex\"\u003e\u003c/a\u003e\u003ca name=\"1.2\"\u003e\u003c/a\u003e\n  - [1.2](#types--complex)  **Complex**: When you access a complex type you work on a reference to its value.\n\n    - `dict`\n    - `list`\n    - `function`\n\n    ```python\n    foo = [1, 2]\n    bar = foo\n\n    bar[0] = 9\n\n    print(foo[0], bar[0]) # =\u003e 9, 9\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## References\n\n  \u003ca name=\"references--prefer-const\"\u003e\u003c/a\u003e\u003ca name=\"2.1\"\u003e\u003c/a\u003e\n  - [2.1](#references--prefer-const) Use `CONST` for all of your references; avoid using `var`. Python does not have `constant` type, so you need to observe the convention using UPPERCASE for constants and never modify them.\n\n    \u003e Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.\n\n    ```python\n    # bad\n    foo = 1\n    bar = 2\n\n    # good\n    FOO = 1\n    BAR = 2\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Dictionaries\n\n  \u003ca name=\"dictionaries--literals\"\u003e\u003c/a\u003e\u003ca name=\"3.1\"\u003e\u003c/a\u003e\n  - [3.1](#dictionaries--literals) Use the literal syntax for dictionary creation.\n\n    ```python\n    # bad\n    item = dict()\n\n    # good\n    item = {}\n    ```\n\n  \u003ca name=\"computed-key\"\u003e\u003c/a\u003e\u003ca name=\"3.2\"\u003e\u003c/a\u003e\n  - [3.2](#computed-key) Use computed key names when creating dictionaries with dynamic key names.\n\n    \u003e Why? They allow you to define all the key of a dictionary in one place.\n\n    ```python\n\n    def get_key(k):\n        return f'a key named {k}'\n\n    # bad\n    obj = {\n        'id': 5,\n        'name': 'San Francisco',\n    }\n    obj[get_key('enabled')] = True\n\n    # good\n    obj = {\n        'id': 5,\n        'name': 'San Francisco',\n        get_key('enabled'): True,\n    }\n    ```\n\n  \u003ca name=\"dictionaries--spread\"\u003e\u003c/a\u003e\u003ca name=\"3.3\"\u003e\u003c/a\u003e\n  - [3.3](#dictionaries--spread) Prefer the dictionary spread operator over `copy()` to shallow-copy and extend dictionaries.\n\n    ```python\n    # bad\n    original = {'a': 1, 'b': 2}\n    clone = original.copy()\n    clone.update({'c': 3})\n\n    # good\n    original = {'a': 1, 'b': 2}\n    clone = {**original, 'c': 3}\n\n    # good\n    original = {'a': 1, 'b': 2}\n    original_2 = {'c': 3, 'd': 4}\n    long_clone = {**original, **original_2, 'e': 5}\n    ```\n\n  \u003ca name=\"dictionaries--braces-newline\"\u003e\u003c/a\u003e\u003ca name=\"3.4\"\u003e\u003c/a\u003e\n  - [3.4](#dictionaries--braces-newline) Use line breaks after open and before close dictionary braces only if a dictionary has multiple lines.\n\n    ```python\n    # bad - single item will not exceed one line\n    single_map = {\n        'a': 1,\n    }\n\n    # bad - single line\n    item_map = {\n        'a': 1, 'b': 2, 'c': 3,\n    }\n\n    # good\n    single_map = {'a': 1}\n\n    item_map = {\n        'a': 1,\n        'b': 2,\n        'c': 3,\n    }\n    ```\n\n  \u003ca name=\"dictionaries--use-get\"\u003e\u003c/a\u003e\u003ca name=\"3.5\"\u003e\u003c/a\u003e\n  - [3.5](#dictionaries--use-get) Use `dict.get(key)` to get properties.\n\n    \u003e Why? Getting via `dict[key]` will break on missing key, and requires bloated code to guard against.\n\n    ```python\n    item_map = {\n        'a': 1,\n        'b': 2,\n    }\n\n    # bad - throws error\n    item_map['c']\n\n    # bad - bloated code\n    try:\n        item_map['c']\n    except KeyError:\n        item_map['c'] = 3\n        return item_map['c']\n\n    # good\n    item_map.get('c')\n\n    # good\n    item_map['c'] = item_map.get('c') or 3\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Lists\n\n  \u003ca name=\"lists--literals\"\u003e\u003c/a\u003e\u003ca name=\"4.1\"\u003e\u003c/a\u003e\n  - [4.1](#lists--literals) Use the literal syntax for list creation.\n\n    ```python\n    # bad\n    items = list()\n\n    # good\n    items = []\n    ```\n\n  \u003ca name=\"list--spreads\"\u003e\u003c/a\u003e\u003ca name=\"4.2\"\u003e\u003c/a\u003e\n  - [4.2](#list--spreads) Use list spreads `*` to copy and extend lists.\n\n    ```python\n    # bad\n    items = ['a', 'b']\n    clone = items.copy() + ['c']\n\n    # good\n    items = ['a', 'b']\n    clone = [*items, 'c']\n\n    # good\n    items = ['a', 'b']\n    items_2 = ['c', 'd']\n    clone = [*items, *items2, 'e']\n    ```\n\n  \u003ca name=\"lists--bracket-newline\"\u003e\u003c/a\u003e\u003ca name=\"4.3\"\u003e\u003c/a\u003e\n  - [4.3](#lists--bracket-newline) Use line breaks after open and before close list brackets only if a list has multiple lines.\n\n    ```python\n    # bad - single line\n    items = [\n        [0, 1], [2, 3], [4, 5],\n    ]\n\n    # bad - no line break after bracket\n    dict_list = [{\n        'id': 1\n    }, {\n        'id': 2\n    }]\n\n    number_list = [\n        1, 2,\n    ]\n\n    # good\n    items = [[0, 1], [2, 3], [4, 5]]\n\n    dict_list = [\n        {'id': 1},\n        {'id': 2},\n    ]\n\n    number_list = [\n        1,\n        2,\n    ]\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Destructuring\n\n  \u003ca name=\"destructuring--list\"\u003e\u003c/a\u003e\u003ca name=\"5.1\"\u003e\u003c/a\u003e\n  - [5.1](#destructuring--list) Use list destructuring.\n\n    ```python\n    items = [1, 2, 3, 4, 5]\n\n    # bad\n    first = items[0]\n    second = items[1]\n\n    # good\n    first, second, *tail = items\n    first, second, *rest, last = items\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Strings\n\n  \u003ca name=\"stringss--quotes\"\u003e\u003c/a\u003e\u003ca name=\"6.1\"\u003e\u003c/a\u003e\n  - [6.1](#strings--quotes) Use single quotes `''` for strings.\n\n    \u003e Why? Less escaping for double quote `\"\"`, less bloat, and makes code more searchable.\n\n    ```python\n    # bad\n    name = \"Capt. Janeway\"\n\n    # bad\n    json_string = \"{\\\"a\\\": 1}\"\n\n    # bad - f string should contain interpolation or newlines\n    name = f'Capt. Janeway'\n\n    # good\n    name = 'Capt. Janeway'\n\n    # good\n    json_string = '{\"a\": 1}'\n    ```\n\n  \u003ca name=\"strings--line-length\"\u003e\u003c/a\u003e\u003ca name=\"6.2\"\u003e\u003c/a\u003e\n  - [6.2](#strings--line-length) Strings that cause the line to go over 79 characters should not be written across multiple lines using string concatenation.\n\n    \u003e Why? Broken strings are painful to work with and make code less searchable.\n\n    ```python\n    # bad\n    error_msg = 'This is a super long error that was thrown because \\\n    of Batman. When you stop to think about how Batman had anything to do \\\n    with this, you would get nowhere \\\n    fast.'\n\n    # bad\n    error_msg = 'This is a super long error that was thrown because ' + \\\n        'of Batman. When you stop to think about how Batman had anything to do ' + \\\n        'with this, you would get nowhere fast.'\n\n    # good\n    error_msg = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'\n    ```\n\n  \u003ca name=\"template-literals\"\u003e\u003c/a\u003e\u003ca name=\"6.3\"\u003e\u003c/a\u003e\n  - [6.3](#template-literals) When programmatically building up strings, use template strings instead of concatenation.\n\n    \u003e Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.\n\n    ```python\n    # bad\n    def say_hi(name):\n        return 'How are you, ' + name + '?'\n\n    # bad\n    def say_hi(name):\n        return ''.join(['How are you, ', name, '?'])\n\n    # bad\n    def say_hi(name):\n        return f'How are you, { name }?'\n\n    # good\n    def say_hi(name):\n        return f'How are you, {name}?'\n    ```\n\n    \u003e For python \u003c 3.6, convert f-string to template string by `format()`:\n\n    ```python\n    a = 1\n    b = 2\n    c = 3\n\n    # python \u003e=3.6\n    f'a: {a} b: {b} c: {c}'\n\n    # python \u003c3.6\n    'a: {a} b: {b} c: {c}'.format(a=a, b=b, c=c)\n\n    param = {'a': a, 'b': b, 'c': c}\n    'a: {a} b: {b} c: {c}'.format(**param)\n\n    'a: {} b: {} c: {}'.format(a, b, c)\n    ```\n\n  \u003ca name=\"strings--eval\"\u003e\u003c/a\u003e\u003ca name=\"6.4\"\u003e\u003c/a\u003e\n  - [6.4](#strings--eval) Never use `eval()` on a string, it opens too many vulnerabilities.\n\n  \u003ca name=\"strings--escaping\"\u003e\u003c/a\u003e\u003ca name=\"6.5\"\u003e\u003c/a\u003e\n  - [6.5](#strings--escaping) Do not unnecessarily escape characters in strings.\n\n    \u003e Why? Backslashes harm readability, thus they should only be present when necessary.\n\n    ```python\n    # bad\n    foo = '\\'this\\' \\i\\s \\\"quoted\\\"'\n\n    # good\n    foo = '\\'this\\' is \"quoted\"'\n    foo = f'my name is \"{name}\"'\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Functions\n\n  \u003ca name=\"default-parameters\"\u003e\u003c/a\u003e\u003ca name=\"7.1\"\u003e\u003c/a\u003e\n  - [7.1](#default-parameters) Use default parameter syntax rather than mutating function arguments.\n\n    ```python\n    # really bad\n    def do_something(opt):\n        # No! We shouldn’t mutate function arguments.\n        # Double bad: if opt is falsy it'll be set to an object which may\n        # be what you want but it can introduce subtle bugs.\n        opt = opt or 'foo'\n        # ...\n\n    # still bad\n    def do_something(opt):\n        if (opt is None):\n            opt = 'foo'\n        # ...\n\n    # good\n    def do_something(opt='foo'):\n        # ...\n    ```\n\n  \u003ca name=\"no-complex-default-parameters\"\u003e\u003c/a\u003e\u003ca name=\"7.2\"\u003e\u003c/a\u003e\n  - [7.2](#no-complex-default-parameters) Do not use complex data type as default parameter.\n\n    \u003e Why? Variable to a complex type is a reference, and so the single instance will be modified.\n\n    ```python\n    # bad\n    def init_list(value, new_list=[]):\n        new_list.append(value)\n        return new_list\n\n    init_list(1)\n    # =\u003e [1]\n    init_list(2)\n    # =\u003e [1, 2], instead of the new init [2]\n\n    # good\n    def init_list(value, new_list=None):\n        if new_list is None:\n            new_list = []\n        new_list.append(value)\n        return new_list\n\n    init_list(1)\n    # =\u003e [1]\n    init_list(2)\n    # =\u003e [2]\n    ```\n\n  \u003ca name=\"functions--signature-no-spacing\"\u003e\u003c/a\u003e\u003ca name=\"7.3\"\u003e\u003c/a\u003e\n  - [7.3](#functions--signature-no-spacing) No spacing in a function signature.\n\n    \u003e Why? Consistency is good, and eases code search.\n\n    ```python\n    # bad\n    def foo(a): print(a)\n    def bar (b): print(b)\n\n    # good\n    def foo(a): print(a)\n    def bar(b): print(b)\n    ```\n\n  \u003ca name=\"functions--reassign-params\"\u003e\u003c/a\u003e\u003ca name=\"7.4\"\u003e\u003c/a\u003e\n  - [7.4](#functions--reassign-params) Never reassign parameters.\n\n    \u003e Why? Reassigning parameters can lead to unexpected behavior.\n\n    ```python\n    # bad\n    def fn_1(a):\n        a = 1\n        # ...\n\n    def fn_2(a):\n        if (!a): a = 1\n        # ...\n\n    # good\n    def fn_3(a):\n        b = a or 1\n        # ...\n\n    def fn_4(a=1):\n        # ...\n    ```\n\n  \u003ca name=\"functions--signature-invocation-indentation\"\u003e\u003c/a\u003e\u003ca name=\"7.5\"\u003e\u003c/a\u003e\n  - [7.5](#functions--signature-invocation-indentation) Functions with multiline signatures, or invocations, should be indented just like every other multiline list in this guide: with each item on a line by itself, with a trailing comma on the last item.\n\n    ```python\n    # bad\n    def some_fn(foo,\n                bar,\n                baz):\n        # ...\n\n    # good\n    def some_fn(\n        foo,\n        bar,\n        baz,\n    ):\n        # ...\n\n    # bad\n    some_fn(foo,\n      bar,\n      baz)\n\n    # good\n    some_fn(\n      foo,\n      bar,\n      baz,\n    )\n    ```\n\n  \u003ca name=\"functions--call-param-name\"\u003e\u003c/a\u003e\u003ca name=\"7.6\"\u003e\u003c/a\u003e\n  - [7.6](#functions--call-param-name) Call function with parameters by specifying their names.\n\n    \u003e Why? Clarity of parameters and future-proofing. When updating source code function parameters, it can be done reliably with minimal propagation.\n\n    ```python\n    def move(x, y, roll=False):\n        # ...\n\n    # bad - unclear what the params mean\n    move(1, 0, True)\n\n    # good\n    move(x=1, y=0, roll=True)\n\n    # later when updating method, no need to propagate function calls since they will auto-assume z=0 reliably\n    def move(x, y, z=0, roll=False):\n        # ...\n    ```\n\n  \u003ca name=\"functions--clear-logic\"\u003e\u003c/a\u003e\u003ca name=\"7.7\"\u003e\u003c/a\u003e\n  - [7.7](#functions--clear-logic) Break down code logic into digestible chunks, and refactor a lot.\n\n    \u003e Why? Other programmers and your future self will thank you for writing understandable code.\n\n    ```python\n    # bad - short but extremely confusing\n    def long_logic():\n        return [a for a in small_list for small_list in large_matrix if len(small_list) else ['replacement'] if len(a) \u003e 2]\n\n    # good - longer but very clear\n    def long_logic():\n        result = []\n        for small_list in large_matrix:\n            if len(small_list) \u003e 0:\n                used_list = small_list\n            else:\n                used_list = ['replacement']\n            for a in used_list:\n                if len(a) \u003e 2:\n                    result.append(a)\n        return result\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Classes \u0026 Constructors\n\n  \u003ca name=\"classes--no-duplicate-members\"\u003e\u003c/a\u003e\u003ca name=\"8.1\"\u003e\u003c/a\u003e\n  - [8.1](#classes--no-duplicate-members) Avoid duplicate class members.\n\n    \u003e Why? Duplicate class member declarations will silently prefer the last one - having duplicates is almost certainly a bug.\n\n    ```python\n    # bad\n    class Foo():\n        def bar(): return 1\n        def bar(): return 2\n\n    # good\n    class Foo():\n        def bar(): return 1\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Modules\n\n  \u003ca name=\"modules--no-wildcard\"\u003e\u003c/a\u003e\u003ca name=\"9.1\"\u003e\u003c/a\u003e\n  - [9.1](#modules--no-wildcard) Do not use wildcard imports.\n\n    \u003e Why? To prevent namespace pollution and conflicts, and to know which modules your variables or functions come from.\n\n    ```python\n    # bad\n    from common.util import *\n\n    # good\n    from common import util\n    ```\n\n\n  \u003ca name=\"modules--no-unused\"\u003e\u003c/a\u003e\u003ca name=\"9.2\"\u003e\u003c/a\u003e\n  - [9.2](#modules--no-unused) Do not import unused modules.\n    \u003e Why? Performance, reliability, containment. If a module breaks, your code that should be isolated from the module will break too. This causes more errors and makes it harder to debug.\n\n  \u003ca name=\"modules--no-duplicate-imports\"\u003e\u003c/a\u003e\u003ca name=\"9.3\"\u003e\u003c/a\u003e\n  - [9.3](#modules--no-duplicate-imports) Only import from a path in one place.\n    \u003e Why? Having multiple lines that import from the same path can make code harder to maintain.\n\n    ```python\n    # bad\n    from foo import bar\n    # ... some other imports\n    from foo import baz, qux\n\n    # good\n    from foo import bar, baz, qux\n\n    # good\n    from foo import (\n        bar,\n        baz,\n        qux,\n    )\n    ```\n\n  \u003ca name=\"modules--imports-first\"\u003e\u003c/a\u003e\u003ca name=\"9.4\"\u003e\u003c/a\u003e\n  - [9.4](#modules--imports-first) Put all `import`s above non-import statements.\n    \u003e Why? Since `import`s are hoisted, keeping them all at the top prevents surprising behavior.\n\n    ```python\n    # bad\n    import a_module\n    from b_module import foo\n    foo.init()\n\n    from c_module import bar\n\n    # good\n    import a_module\n    from b_module import foo\n    from c_module import bar\n\n    foo.init();\n    ```\n\n  \u003ca name=\"modules--imports-sorted\"\u003e\u003c/a\u003e\u003ca name=\"9.5\"\u003e\u003c/a\u003e\n  - [9.5](#modules--imports-sorted) Sort the `import`s by `import` then `from`, and sort alphabetically.\n    \u003e Why? `import` are often more generic that `from`; sort to ease manual inspection and for maintainability.\n\n    ```python\n    # bad\n    from a_module import foo\n    import e_module\n    import b_module\n    from c_module import c_fn, b_fn\n\n    # good\n    import b_module\n    import e_module\n    from a_module import foo\n    from c_module import b_fn, c_fn\n    ```\n\n  \u003ca name=\"modules--multiline-imports-over-newlines\"\u003e\u003c/a\u003e\u003ca name=\"9.6\"\u003e\u003c/a\u003e\n  - [9.6](#modules--multiline-imports-over-newlines) Multiline imports should be indented just like multiline list and dictionary literals.\n\n    \u003e Why? The parentheses follow the same indentation rules as every other bracket or brace block in the style guide, as do the trailing commas.\n\n    ```python\n    # bad\n    from a_module import long_name_a, long_name_b, long_name_c, long_name_d\n\n    # good\n    from a_module import (\n      long_name_a,\n      long_name_b,\n      long_name_c,\n      long_name_d,\n    )\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Iterators and Generators\n\n  (Pending)\n\n**[⬆ back to top](#table-of-contents)**\n\n## Variables\n\n  \u003ca name=\"variables--const\"\u003e\u003c/a\u003e\u003ca name=\"11.1\"\u003e\u003c/a\u003e\n  - [11.1](#variables--const) Use UPPERCASE to declare constants, and observe the convention - do not modify them in the program. Python has no `constant` type, so it must be observed manually.\n\n    ```python\n    # bad\n    a_constant = 1\n    os.environ['py_env'] = 'development'\n\n    # good\n    A_CONSTANT = 1\n    os.environ['PY_ENV'] = 'development'\n    ```\n\n  \u003ca name=\"variables--one-const\"\u003e\u003c/a\u003e\u003ca name=\"11.2\"\u003e\u003c/a\u003e\n  - [11.2](#variables--one-const) Declare one constant per line.\n\n    \u003e Why? For clarity, and it’s easier to add/remove declarations this way, and with minimal git-diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.\n\n    ```python\n    # bad\n    FOO, BAR, BAZ = 1, 2, 3\n\n    # good\n    FOO = 1\n    BAR = 2\n    BAZ = 3\n    ```\n\n  \u003ca name=\"variables--const-group\"\u003e\u003c/a\u003e\u003ca name=\"11.3\"\u003e\u003c/a\u003e\n  - [11.3](#variables--const-group) Group all your `CONST`s and then group all your `var`s.\n\n    \u003e Why? For clarity and ease of reference. This is also helpful when later on you might need to assign a variable depending on one of the previous assigned variables.\n\n    ```python\n    # bad\n    FOO = 1\n    counter = 0\n    BAR = 2\n    length = counter\n\n    # good\n    FOO = 1\n    BAR = 2\n\n    counter = 0\n    length = counter\n    ```\n\n  \u003ca name=\"variables--define-where-used\"\u003e\u003c/a\u003e\u003ca name=\"11.4\"\u003e\u003c/a\u003e\n  - [11.4](#variables--define-where-used) Assign variables with the minimally sufficient scope at where you need them, but place them in a reasonable place.\n\n    \u003e Why? Prevent variable scope-leak and conflicts\n\n    ```python\n    # bad - leak to sibling\n    counter = 0 # mean to count group_a only\n    for list_a in group_a:\n        counter += len(list_a)\n\n    for list_b in group_b:\n        counter += len(list_b)\n\n    # bad - leak into smaller scope\n    counter = 0 # mean to count within groups\n    for group in super_group:\n        counter += len(group)\n        for list in group:\n            counter += len(list)\n\n    # good\n    counter_a = 0\n    for list_a in group_a:\n        counter_a += len(list_a)\n\n    counter_b = 0\n    for list_b in group_b:\n        counter_b += len(list_b)\n\n    # good\n    group_counter = 0 # mean to count within groups\n    for group in super_group:\n        group_counter += len(group)\n        list_counter = 0 # mean to count within lists\n        for list in group:\n            list_counter += len(list)\n    ```\n\n  \u003ca name=\"variables--underscore-unused\"\u003e\u003c/a\u003e\u003ca name=\"11.5\"\u003e\u003c/a\u003e\n  - [11.5](#variables--underscore-unused) Prepend underscore `_` when naming variables that are unused. Also a part of PEP8.\n\n    \u003e Why? To be aware of data usage and side effects.\n\n    ```python\n    # bad\n    first, unused, last = [1, 2, 3]\n\n    # bad - finder is not used though expected to be\n    for finder, replacer in some_map.items():\n        do_something_without_key(replacer)\n\n    # bad - lose track of what the first key is\n    for _, replacer in some_map.items():\n        do_something_without_key(replacer)\n\n    # good\n    first, _unused, last = [1, 2, 3]\n\n    # good - we know what the variable is, and it is unused\n    for _finder, replacer in some_map.items():\n        do_something_without_key(replacer)\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Comparison Operators \u0026 Equality\n\n  \n  \u003ca name=\"comparison--concise\"\u003e\u003c/a\u003e\u003ca name=\"12.1\"\u003e\u003c/a\u003e\n  - [12.1](#comparison--concise) Use concise boolean conditionals, refactor long compound statements.\n\n    \u003e Why? Long boolean statements are hard to read and understand.\n\n    ```python\n    # bad\n    if (can_move_x() and can_move_y() or is_light() or is_dry() or has_high_drag()):\n        execute_operation_tumbleweed()\n    else:\n        execute_operation_cactus()\n\n    # good\n    can_move = can_move_x() and can_move_y()\n    movable = is_light() or is_dry() or has_high_drag()\n    if (can_move or movable):\n        execute_operation_tumbleweed()\n    else:\n        execute_operation_cactus()\n    ```\n\n  \u003ca name=\"comparison--direct\"\u003e\u003c/a\u003e\u003ca name=\"12.2\"\u003e\u003c/a\u003e\n  - [12.2](#comparison--direct) Be direct with booleans, avoid unnecessary negations.\n\n    \u003e Why? Negations are harder to understand and longer to write.\n\n    ```python\n    # bad\n    if not a_is_legal():\n        do_b()\n    else:\n        do_a()\n\n    # good\n    if a_is_legal():\n        do_a()\n    else:\n        do_b()\n    ```\n\n  \u003ca name=\"comparison--shortcuts\"\u003e\u003c/a\u003e\u003ca name=\"12.3\"\u003e\u003c/a\u003e\n  - [12.3](#comparison--shortcuts) Use shortcuts for booleans, but explicit comparisons for strings and numbers.\n\n    ```python\n    # bad\n    if is_valid == true:\n        # ...\n\n    # good\n    if is_valid:\n        # ...\n\n    # bad\n    if name:\n        # ...\n\n    # good\n    if name != '':\n        # ...\n\n    # bad\n    if len(a_list):\n        # ...\n\n    # good\n    if len(a_list) \u003e 0:\n        # ...\n    ```\n\n  \u003ca name=\"comparison--nested-ternaries\"\u003e\u003c/a\u003e\u003ca name=\"12.4\"\u003e\u003c/a\u003e\n  - [12.4](#comparison--nested-ternaries) Ternaries should not be nested and generally be single line expressions.\n\n    ```python\n    # bad\n    foo = 'bar' if maybe_1 \u003e maybe_2 else 'baz' if value_1 \u003e value_2 else None\n\n    # best\n    maybe_none = 'baz' if value1 \u003e value2 else None\n    foo = 'bar' if maybe1 \u003e maybe2 else maybe_none\n    ```\n\n  \u003ca name=\"comparison--unneeded-ternary\"\u003e\u003c/a\u003e\u003ca name=\"12.5\"\u003e\u003c/a\u003e\n  - [12.5](#comparison--unneeded-ternary) Avoid unneeded ternary statements.\n\n    ```python\n    # bad\n    foo = a if a else b\n    bar = True if c else False\n    baz = False if c else True\n\n    # good\n    foo = a or b\n    bar = c\n    baz = not c\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Comments\n\n  \u003ca name=\"comments--multiline\"\u003e\u003c/a\u003e\u003ca name=\"13.1\"\u003e\u003c/a\u003e\n  - [13.1](#comments--multiline) Use `'''...'''` for multi-line comments.\n\n    ```python\n    # bad\n    def make(tag):\n        # make() returns a new element\n        # based on the passed in tag name\n        #\n        # @param {string} tag\n        # @return {Element} element\n\n        # ...\n        return element\n\n    # good\n    def make(tag):\n        '''\n        make() returns a new element\n        based on the passed in tag name\n        \n        @param {string} tag\n        @return {Element} element\n        '''\n\n        # ...\n        return element\n    ```\n\n  \u003ca name=\"comments--singleline\"\u003e\u003c/a\u003e\u003ca name=\"13.2\"\u003e\u003c/a\u003e\n  - [13.2](#comments--singleline) Use `#` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s on the first line of a block.\n\n    ```python\n    # bad\n    active = True  # is current tab\n\n    # good\n    # is current tab\n    active = True\n\n    # bad\n    def get_type():\n        print('fetching type...')\n        # set the default type to 'no type'\n        type = self.type or 'no type'\n\n        return type\n\n    # good\n    def get_type():\n        print('fetching type...')\n\n        # set the default type to 'no type'\n        type = self.type or 'no type'\n\n        return type\n\n    # also good\n    def get_type():\n        # set the default type to 'no type'\n        type = self.type or 'no type'\n\n        return type\n    ```\n\n  \u003ca name=\"comments--spaces\"\u003e\u003c/a\u003e\u003ca name=\"13.3\"\u003e\u003c/a\u003e\n  - [13.3](#comments--spaces) Start all comments with a space to make it easier to read.\n\n    ```python\n    # bad\n    #is current tab\n    active = True\n\n    # good\n    # is current tab\n    active = True\n    ```\n\n  \u003ca name=\"comments--actionitems\"\u003e\u003c/a\u003e\u003ca name=\"13.4\"\u003e\u003c/a\u003e\n  - [13.4](#comments--actionitems) Prefixing your comments with `TODO` helps yourself and other developers be aware of items to revisit or implement. It keeps the issues visible and easy to find. These are different than regular comments because they are actionable.\n\n    ```python\n    def complex_calculator():\n        compute_basic()\n\n        # TODO figure out improvements to the logic\n        return compute_core_logic()\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Whitespace\n\n  \u003ca name=\"whitespace--spaces\"\u003e\u003c/a\u003e\u003ca name=\"14.1\"\u003e\u003c/a\u003e\n  - [14.1](#whitespace--spaces) Use soft tabs (space character) set to 4 spaces as per PEP8.\n\n    ```python\n    # bad\n    def foo():\n    ∙∙return bar\n\n    # bad\n    def foo():\n    ∙return bar\n\n    # good\n    def foo():\n    ∙∙∙∙return bar\n    ```\n\n  \u003ca name=\"whitespace--infix-ops\"\u003e\u003c/a\u003e\u003ca name=\"14.2\"\u003e\u003c/a\u003e\n  - [14.2](#whitespace--infix-ops) Set off operators with spaces.\n\n    ```python\n    # bad\n    x=y+5\n\n    # good\n    x = y + 5\n    ```\n\n  \u003ca name=\"whitespace--newline-at-end\"\u003e\u003c/a\u003e\u003ca name=\"14.3\"\u003e\u003c/a\u003e\n  - [14.3](#whitespace--newline-at-end) End files with a single newline character.\n\n    ```python\n    # bad\n    import util\n    # ...\n    def foo():\n        return bar\n    ```\n\n    ```python\n    # bad\n    import util\n    # ...\n    def foo():\n        return bar↵\n    ↵\n    ```\n\n    ```python\n    # bad\n    import util\n    # ...\n    def foo():\n        return bar↵\n    ```\n\n  \u003ca name=\"whitespace--after-blocks\"\u003e\u003c/a\u003e\u003ca name=\"14.4\"\u003e\u003c/a\u003e\n  - [14.4](#whitespace--after-blocks) Leave a blank line after blocks and before the next statement.\n\n    ```python\n    # bad\n    if foo:\n        return bar\n    return baz\n\n    # good\n    if foo:\n        return bar\n\n    return baz\n\n    # bad\n    results = [\n        fn_1(),\n        fn_2(),\n    ]\n    return results\n\n    # good\n    results = [\n        fn_1(),\n        fn_2(),\n    ]\n\n    return results\n    ```\n\n  \u003ca name=\"whitespace--padded-blocks\"\u003e\u003c/a\u003e\u003ca name=\"14.5\"\u003e\u003c/a\u003e\n  - [14.5](#whitespace--padded-blocks) Do not pad your blocks with blank lines.\n\n    ```python\n    # bad\n    def bar():\n\n        print(foo)\n\n    # bad\n    if (baz):\n\n        print(qux)\n    else:\n        print(foo)\n\n    # good\n    def bar():\n        print(foo)\n\n    # good\n    if (baz):\n        print(qux)\n    else:\n        print(foo)\n    ```\n\n  \u003ca name=\"whitespace--in-parens\"\u003e\u003c/a\u003e\u003ca name=\"14.6\"\u003e\u003c/a\u003e\n  - [14.6](#whitespace--in-parens) Do not add spaces inside parentheses.\n\n    ```python\n    # bad\n    def bar( foo ):\n        return foo\n\n    # good\n    def bar(foo):\n        return foo\n\n    # bad\n    if ( foo and fux ):\n        print(foo)\n\n    # good\n    if (foo and fux):\n        print(foo)\n    ```\n\n  \u003ca name=\"whitespace--in-brackets\"\u003e\u003c/a\u003e\u003ca name=\"14.7\"\u003e\u003c/a\u003e\n  - [14.7](#whitespace--in-brackets) Do not add spaces inside brackets or braces.\n\n    ```python\n    # bad\n    foo = [ 1, 2, 3 ]\n    print(foo[ 0 ])\n    bar = { 'a': 1 }\n\n    # good\n    foo = [1, 2, 3]\n    print(foo[0])\n    bar = {'a': 1}\n    ```\n\n  \u003ca name=\"whitespace--max-len\"\u003e\u003c/a\u003e\u003ca name=\"14.8\"\u003e\u003c/a\u003e\n  - [14.8](#whitespace--max-len) Avoid having lines of code that are longer than 79 characters (including whitespace) as per PEP8. Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up.\n\n    \u003e Why? This ensures readability and maintainability.\n\n    ```python\n    # bad\n    foo = nested_object and nested_object.foo and nested_object.foo.bar and nested_object.foo.bar.baz and nested_object.foo.bar.baz.quux and nested_object.foo.bar.baz.quux.xyzzy\n\n    # bad\n    http_call({'method': 'POST', 'url': 'https://airbnb.com/', 'data': {name: 'John', 'age': 20}})\n\n    # good\n    foo = (nested_object and\n        nested_object.foo and\n        nested_object.foo.bar and\n        nested_object.foo.bar.baz and\n        nested_object.foo.bar.baz.quux and\n        nested_object.foo.bar.baz.quux.xyzzy)\n\n    # good\n    http_call({\n        'method': 'POST',\n        'url': 'https://airbnb.com/',\n        'data': {name: 'John', 'age': 20},\n    })\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Commas\n\n  \u003ca name=\"commas--leading-trailing\"\u003e\u003c/a\u003e\u003ca name=\"15.1\"\u003e\u003c/a\u003e\n  - [15.1](#commas--leading-trailing) Leading commas: **Nope.**\n\n    ```python\n    # bad\n    story = [\n          once\n        , upon\n        , a_time\n    ]\n\n    # good\n    story = [\n        once,\n        upon,\n        a_time,\n    ]\n\n    # bad\n    hero = {\n        'first_name': 'Ada'\n        , 'last_name': 'Lovelace'\n        , 'birth_year': 1815\n        , 'super_power': 'computers'\n    }\n\n    # good\n    hero = {\n        'first_name': 'Ada',\n        'last_name': 'Lovelace',\n        'birth_year': 1815,\n        'super_power': 'computers',\n    }\n    ```\n\n  \u003ca name=\"commas--dangling\"\u003e\u003c/a\u003e\u003ca name=\"15.2\"\u003e\u003c/a\u003e\n  - [15.2](#commas--dangling) Additional trailing comma: **Yup.**\n\n    \u003e Why? This leads to cleaner git diffs during code change.\n\n    ```diff\n    # bad - git diff without trailing comma\n    hero = {\n         'first_name': 'Ada',\n    -    'last_name': 'Lovelace'\n    +    'last_name': 'Lovelace',\n    +    'super_power': 'computers',\n    }\n\n    # good - git diff with trailing comma\n    hero = {\n         'first_name': 'Ada',\n         'last_name': 'Lovelace',\n    +    'super_power': 'computers',\n    }\n    ```\n\n    ```python\n    # bad\n    hero = {\n        'first_name': 'Ada',\n        'last_name': 'Lovelace'\n    }\n\n    heroes = [\n        'Batman',\n        'Superman'\n    ]\n\n    # good\n    hero = {\n        'first_name': 'Ada',\n        'last_name': 'Lovelace',\n    }\n\n    heroes = [\n        'Batman',\n        'Superman',\n    ]\n\n    # bad\n    def create_hero(\n        first_name,\n        last_name,\n        superpower\n    ):\n        # ...\n\n    # good\n    def create_hero(\n        first_name,\n        last_name,\n        superpower,\n    ):\n        # ...\n\n    # good - note that a comma must not appear after a \"spread\" element\n    def create_hero(\n        first_name,\n        last_name,\n        superpower,\n        **kwargs\n    ):\n        # ...\n\n    # bad\n    create_hero(\n        first_name,\n        last_name,\n        superpower\n    )\n\n    # good\n    create_hero(\n        first_name,\n        last_name,\n        superpower,\n    )\n\n    # good - note that a comma must not appear after a \"spread\" element\n    create_hero(\n        first_name,\n        last_name,\n        superpower,\n        **kwargs\n    )\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Naming Conventions\n\n  \u003ca name=\"naming--snake_case\"\u003e\u003c/a\u003e\u003ca name=\"16.1\"\u003e\u003c/a\u003e\n  - [16.1](#naming--snake_case) Use snake_case when naming variables, functions, and instances. Use it for file names too as they will be used in imports.\n\n    ```python\n    # bad\n    import myModule\n    OBJEcttsssss = {}\n    thisIsMyObject = {}\n    def thisIsMyFunction():\n\n    # good\n    import my_module\n    objects = {}\n    this_is_my_object = {}\n    def this_is_my_function():\n    ```\n\n  \u003ca name=\"naming--PascalCase\"\u003e\u003c/a\u003e\u003ca name=\"16.2\"\u003e\u003c/a\u003e\n  - [16.2](#naming--PascalCase) Use PascalCase only when naming classes.\n\n    ```python\n    # bad\n    class prioritizedMemoryReplay():\n        # ...\n\n    memory = prioritizedMemoryReplay()\n\n    # good\n    class PrioritizedMemoryReplay():\n        # ...\n\n    memory = PrioritizedMemoryReplay()\n    ```\n\n  \u003ca name=\"naming--descriptive\"\u003e\u003c/a\u003e\u003ca name=\"16.3\"\u003e\u003c/a\u003e\n  - [16.3](#naming--descriptive) Avoid single letter names. Use descriptive and meaningful names - tell what the function does, or what data type an object is. Use `description_object` instead of `object_description`.\n\n    ```python\n    # bad\n    def q():\n        # ...\n\n    # good\n    def query():\n        # ...\n\n    # bad - no convention to know what data type it is\n    df_raw_data = pd.DataFrame(some_data)\n    id_map_num = {'a': 1, 'b': 2}\n\n    # good - convention to tell data type by the last term\n    raw_data_df = pd.DataFrame(some_data)\n    id_num_map = {'a': 1, 'b': 2}\n\n    # bad - meaningless names, lost context\n    LIST_1 = ['Jack', 'Alice', 'Emily']\n    # ... many lines of code later\n    for item in LIST_1:\n        register_human(item)\n\n    # good\n    NAME_LIST = ['Jack', 'Alice', 'Emily']\n    # ... many lines of code later\n    for name in NAME_LIST:\n        register_human(name)\n    ```\n\n  \u003ca name=\"naming--distance\"\u003e\u003c/a\u003e\u003ca name=\"16.4\"\u003e\u003c/a\u003e\n  - [16.4](#naming--distance) Avoid using close naming to prevent typo and confusion.\n\n    ```python\n    # bad\n    objects = ['rock', 'paper', 'scissors']\n    for object in objects:\n        register_item(object)\n    close_box(objects)\n\n    # okay\n    objects = ['rock', 'paper', 'scissors']\n    for obj in objects:\n        register_item(obj)\n    close_box(objects)\n\n    # best\n    object_list = ['rock', 'paper', 'scissors']\n    for object in object_list:\n        register_item(object)\n    close_box(object_list)\n    ```\n\n  \u003ca name=\"naming--singular\"\u003e\u003c/a\u003e\u003ca name=\"16.5\"\u003e\u003c/a\u003e\n  - [16.5](#naming--singular) Use singular or base words in naming; avoid using plural and instead append singular with the data type.\n\n    \u003e Why? To prevent inconsistencies and second-guesses when using variables. Also, plurals are 1 letter away from a typo, are hard to read, and are ambiguous on the data type.\n\n    ```python\n    # bad\n    def moves_object(x, y):\n        # ...\n\n    # good\n    def move_object(x, y):\n        # ...\n\n    # bad - inconsistent naming for same data type and usage\n    teacher = ['Michael']\n    students = ['Jack', 'Alice', 'Emily']\n    books = pd.DataFrame({'title': ['lorem', 'ipsum']})\n\n    for t in teacher:\n        register_human(t)\n\n    for student in students:\n        register_human(student)\n\n    for book in books:\n        register_item(book) # wrong; iterate column name instead of book\n\n    # good\n    teacher_list = ['Michael']\n    student_list = ['Jack', 'Alice', 'Emily']\n    book_df = pd.DataFrame({'title': ['lorem', 'ipsum']})\n\n    for teacher in teacher_list:\n        register_human(teacher)\n\n    for student in student_list:\n        register_human(student)\n\n    # naming as df suggests it shall be treated as a dataframe\n    for _idx, book in book_df.iterrow():\n        register_item(book)\n    ```\n\n  \u003ca name=\"naming--singular-module\"\u003e\u003c/a\u003e\u003ca name=\"16.6\"\u003e\u003c/a\u003e\n  - [16.6](#naming--singular-module) Use singular naming for modules and source files.\n\n    ```python\n    # bad\n    from commons import utils\n\n    utils.read_string()\n\n    # good\n    from common import util\n\n    util.read_string()\n    ```\n\n  \u003ca name=\"naming--abbreviations\"\u003e\u003c/a\u003e\u003ca name=\"16.7\"\u003e\u003c/a\u003e\n  - [16.7](#naming--abbreviations) Use abbreviations if they are clear and make for more readable and writable code.\n\n    \u003e Why? Names are for humans, so always make code readable and easy to spell.\n\n    ```python\n    # bad\n    flight_prerequisites_checklist = ['landing gear', 'engine', 'flaps']\n    initialize_flight(flight_prerequisites_checklist)\n\n    # good\n    flight_prereq_checklist = ['landing gear', 'engine', 'flaps']\n    init_flight(flight_prereq_checklist)\n    ```\n\n  \u003ca name=\"naming--short\"\u003e\u003c/a\u003e\u003ca name=\"16.8\"\u003e\u003c/a\u003e\n  - [16.8](#naming--short) Use simple, concise names over long, explicit ones.\n\n    \u003e Why? Names are for humans to read, and should make the code clean.\n\n    ```python\n    # bad - explicit Java-style naming clutters code and harms readability\n    poscode_to_city_name_map = {11223: 'brooklyn'}\n    poscode_to_city_name_to_state_name_map = map_city_to_state(poscode_to_city_name_map)\n    poscode_to_city_name_to_state_name_to_country_map = {}\n\n    # good - understandable and fast to read\n    poscode_city_map = {11223: 'brooklyn'}\n    poscode_state_map = map_city_to_state(poscode_city_map)\n    poscode_country_map = {}\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Testing\n\n  \u003ca name=\"testing--yup\"\u003e\u003c/a\u003e\u003ca name=\"17.1\"\u003e\u003c/a\u003e\n  - [17.1](#testing--yup) **Yup.**\n\n  \u003ca name=\"testing--for-real\"\u003e\u003c/a\u003e\u003ca name=\"17.2\"\u003e\u003c/a\u003e\n  - [17.2](#testing--for-real) **No, but seriously**:\n    - Whichever testing framework you use, you should be writing tests!\n    - Strive to write many small pure functions, and minimize where mutations occur.\n    - Write tests as you develop instead of piling them up for later. Fix problems early on, otherwise they will compound.\n    - Be cautious about stubs and mocks - they can make your tests more brittle.\n    - Avoid side effects to your development/production code/assets. Setup a separate database and set of assets for testing.\n    - 100% test coverage is a good goal to strive for, even if it’s not always practical to reach it.\n    - Whenever you fix a bug, _write a regression test_. A bug fixed without a regression test is almost certainly going to break again in the future.\n\n  \u003ca name=\"testing--direct-assertation\"\u003e\u003c/a\u003e\u003ca name=\"17.3\"\u003e\u003c/a\u003e\n  - [17.3](#testing--direct-assertation) Use direct assertations and explicit comparisons; avoid negations.\n\n    \u003e Why? Make the expected result for comparison explicit and avoid any implicit type conversion.\n\n    ```python\n    # bad - Other values can be falsy too: `[], 0, '', None`\n    assert not result\n    assert result_list\n\n    # good\n    assert result == False\n    assert len(result_list) \u003e 0\n    ```\n\n**[⬆ back to top](#table-of-contents)**\n\n## Resources\n\n**Learning Python**\n\n  - [Learn Python in 10 minutes](https://www.stavros.io/tutorials/python/)\n\n**Tools**\n\n  - Code Style Linters\n    - [Python PEP8 Autoformat](https://packagecontrol.io/packages/Python%20PEP8%20Autoformat)\n\n**Other Style Guides**\n\n  - [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)\n  - [Great example: spaCy source code](https://github.com/explosion/spaCy)\n\n**[⬆ back to top](#table-of-contents)**\n\n## Contributors\n\n  - [View Contributors](https://github.com/kengz/python/graphs/contributors)\n\n## Amendments\n\nWe encourage you to fork this guide and change the rules to fit your team’s style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkengz%2Fpython","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkengz%2Fpython","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkengz%2Fpython/lists"}