{"id":19327814,"url":"https://github.com/kenshoo/python-style-guide","last_synced_at":"2025-10-19T19:19:59.890Z","repository":{"id":26166323,"uuid":"107534309","full_name":"kenshoo/python-style-guide","owner":"kenshoo","description":"Our 2 cents of python experience. Open for everyone","archived":false,"fork":false,"pushed_at":"2024-06-07T17:26:09.000Z","size":81,"stargazers_count":22,"open_issues_count":7,"forks_count":47,"subscribers_count":56,"default_branch":"master","last_synced_at":"2025-04-02T04:03:45.929Z","etag":null,"topics":["comprehension","generators","managedby-tec-devops-microservices","public","public-repo","public-repository","python","python-script","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kenshoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-19T10:53:36.000Z","updated_at":"2025-03-25T14:44:50.000Z","dependencies_parsed_at":"2022-08-25T23:31:22.977Z","dependency_job_id":null,"html_url":"https://github.com/kenshoo/python-style-guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpython-style-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpython-style-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpython-style-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenshoo%2Fpython-style-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kenshoo","download_url":"https://codeload.github.com/kenshoo/python-style-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250328093,"owners_count":21412558,"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":["comprehension","generators","managedby-tec-devops-microservices","public","public-repo","public-repository","python","python-script","style-guide","styleguide"],"created_at":"2024-11-10T02:18:51.899Z","updated_at":"2025-10-19T19:19:54.849Z","avatar_url":"https://github.com/kenshoo.png","language":null,"readme":"# python-style-guide\nThis is Kenshoo's Python Style Guide. Using this style guide we hope to acheive the following:\n\n1. Humbly share our knowledge \u0026 expeience with the world (and some of our pain too).\n1. Assist novice/intermediate developers.\n1. Generate healthy discussion which will benefit all.\n1. Admit and accept we may be wrong and change per the community's comments.\n\nWe **DO NOT** wish the following:\n1. Say/claim/Imply there is a \"right\" or \"wrong\" way to do it.\n1. Claim this style guide is inclusive.\n1. Introduce ourselves as some kind of language \"experts\".\n\nInspired by many style guides but mainly from [Airbnb](https://github.com/airbnb)'s Style-Guide \"style\" :-).\n\n### Why not just read [PEP 008](https://www.python.org/dev/peps/pep-0008)?\nYou SHOULD read python's PEP 008 as it contains the basic foundations for writing Python code.\n\nThis Style Guide aims to share our experience of writing with Python conisdering syle guides such as PEP 008.\nIt therefore reflects our own conclusions and preferences.\n\n## Table of Contents\n\n  1. [PEP 20](#pep-20)\n  1. [General](#general)\n  1. [Naming](#naming)\n  1. [Code Layout](#code-layout)\n     * [Indentation](#indentation)\n     * [White Spaces](#white-spaces)\n     * [New Lines](#new-lines)\n  1. [Conditional Expressions](#conditional-expressions)\n     * [Flow](#flow)\n     * [Truthy vs Falsey](#truthy-vs-falsey)\n     * [Single Line](#single-line)\n     * [Ternary](#ternary)\n     * [Forgiveness vs Permission](#forgiveness-vs-permission)\n  1. [Strings](#strings)\n     * [Double vs Single Quotes](#double-vs-single-quotes)\n     * [Concatenation](#concatenation)\n  1. [Methods, Functions, and Callables](#methodsfunctionscallables)\n     * [Arguments/Parameters](#argumentsparameters)\n     * [Parameter Passing](#parameter-passing)\n     * [Dynamic Parameter Expansion](#dynamic-parameter-expansion)\n     * [Lambdas and Nested Functions](#lambdas-and-nested-functions)\n     * [Recursion](#recursion)\n  1. [Collections/Iterables](#collectionsiterables)\n     * [Slice](#slice)\n     * [Comprehensions](#comprehensions)\n     * [Builtin Functions For Collections](#builtin-functions-for-collections)\n     * [Tuples](#tuples)\n     * [Tuple vs List](#tuple-vs-list)\n  1. [Imports](#imports)\n  1. [Classes](#classes)\n     * [Class Methods](#class-methods)\n  1. [Exceptions](#exceptions)\n     * [Catching Exceptions](#catching-exceptions)\n     * [EAFP](#eafp)\n     * [Custom Exceptions](#custom-exceptions)\n  1. [Regular Expressions](#regular-expressions)\n\n## PEP 20\n[The Zen Of Python](https://www.python.org/dev/peps/pep-0020/)\n\n## General\n\n * Don't explain code with comments\n\nInstead of a writing a comment explaining the code, place the code into a function whose name explains your intent.\n\n\n## Naming\n\n[PEP 8 naming convention](https://www.python.org/dev/peps/pep-0008/#id36)\n\nIn general:\n\n * Use snake_case for modules, methods and variables.\n * Use CamelCase for classes. (Keep acronyms like HTTP, RFC, XML uppercase.)\n * Use SCREAMING_SNAKE_CASE for other constants.\n * The names of predicate methods (methods that return a boolean value) should start with is, does, has, or the likes.\n * The name of predicates should also be positive. (i.e. is_alive, is_empty versus is_not_dead, is_not_empty)\n * Generators (especially comprehension generators), iterators, and other lazy loading objects names should not imply the underlying implementation,\n   but rather the result you expect.\n\n```python\n\n# bad\ngenerate_screaming_list = (word.upper() for word in list_of_words)\n\nfor word in generate_screaming_list:\n    print(word)\n\n# good\nscreaming_list = (word.upper() for word in list_of_words)\n\nfor word in screaming_list:\n    print(word)\n```\n\n\n * Throwaway variables should be named with an underscore (`_`).\n * If you feel the need to describe the variable, then name the variable using a leading underscore.\n\n```python\nfor _ in range(4):\n    print 'hi'\n\nfor _number in range(4):\n    print('hi')\n```\n\n\n## Code Layout\n\n### Indentation\n\n  * Use soft-tabs with a four space-indent\n  * Align function parameters either all on the same line or one per line.\n\n```python\n# bad\ndef create_translation(phrase_id, phrase_key, target_locale,\n                        value, user_id, do_xss_check, allow_verification)\n    ...\n\n# good\ndef create_translation(phrase_id,\n                        phrase_key,\n                        target_locale,\n                        value,\n                        user_id,\n                        do_xss_check,\n                        allow_verification)\n    ...\n\n# good\ndef create_translation(\n    phrase_id,\n    phrase_key,\n    target_locale,\n    value,\n    user_id,\n    do_xss_check,\n    allow_verification\n)\n    ...\n\n```\n\n### White Spaces\n\n * Avoid extraneous whitespaces within parentehses, brackets, and braces.\n * Preferred not to use multiple white spaces during assignment\n\n```python\n# bad\nlong_name = 'str'\na         = 9\nb         = 8\n\n# good\nlong_name = 'str'\na = 9\nb = 8\n```\n\n * Keyword arguments and default values should not contain whitespaces\n\n### New Lines\n\n * There should be two newlines after all your import statements.\n * Don’t include newlines between areas of different indentation (such as around class or method bodies).\n\n```python\n# bad\nclass Foo(object):\n\n    def bar(self):\n        # body omitted\n\n# good\nclass Foo(object):\n    def bar(self):\n        # body omitted\n```\n\n * Use a single empty line to break between statements to break up methods into logical paragraphs internally.\n\n```python\ndef transformorize(self, _car):\n  car = manufacture(options)\n  t = transformer(robot, disguise)\n\n  car.after_market_mod()\n  t.transform(car)\n  car.assign_cool_name()\n\n  fleet.add(car)\n  return car\n```\n\n * End each file with a newline. Don't include multiple newlines at the end of a file.\n\n## Conditional Expressions\n\n * Prefer to check positive statements vs negative statements\n\n### Flow\n\n * `if/else` blocks should handle the least amount of logic first\n\n```python\n# bad\nif reason:\n    x = get_value()\n    y = x * 10\n    # code\n    # code\n    # code\n    return x\nelse:\n    return 10\n\n# good\nif not reason:\n    return 10\nelse:\n    x = get_value()\n    y = x * 10\n    # code\n    # code\n    # code\n    return x\n```\n\n * So too, single line jumps/flow control disruption should not be handled in an `else` scope\n\n```python\n# bad\nfor i in items:\n    if condition:\n        # code\n        # code\n    else:\n        break /return\n\n# good\nfor i in items:\n    if condition:\n        break / return\n\n    # code\n    # code\n\n# bad\nif condition:\n    # code\n    # code\nelse:\n    raise Exception\n\n# good\nif condition:\n    raise Exception\n\n# code\n# code\n\n\n```\n\n * Avoid nesting `if` statements.\n * If nesting is needed never more than 2 levels of nesting.\n * Place `and` / `or` keywords at the end lines when using a line break.\n\n```python\n# bad\nif (name == 'bob'\n    and l_name == 'samuels'):\n    ...\n\n# good\nif (name == 'bob' and\n    l_name == 'samuels'):\n    ...\n```\n\n### Truthy vs Falsey\n\n * Prefer truthy/falsey checks vs comparing actual values.\n\n\"truthy\" values are all objects except\n\n1. `False`\n1. `None`\n1. `0`\n1. `[]` empty lists\n1. `()` empty tuples\n1. `{}` empty dictionaries\n1. an object whose magic method `__bool__` returns falsey(in python 3)\n1. an object whose magic method `__nonzero__` returns falsey(in python 2)\n\n\n```python\n# bad\nif value \u003e 0:\n    ...\n\n# good\nif value:\n    ...\n\n# bad\nif value is 0:\n    ...\n\n# good\nif not value:\n    ...\n\n# bad\nif len(a_list) \u003e 0:\n    ...\n\n# good\nif a_list:\n    ...\n\n# bad sometimes\nval = None\nif val is None:\n    ...\n\n# good\nval = None\nif not val:\n    ...\n```\n\n * If you need to check an object if it is `None` and not falsey use the `is` operator\n\n```python\n# bad\nif val == None:\n    ...\n\n# good\nif val is None:\n    ...\n```\n\n### Single Line\n\n * You can use single line if statements when they are short and simple (like checking truthy statements).\n * Don't use single lines when you using combined conditions.\n\n```python\n# bad\nif complex_object.with_a_long_name.some_value \u003e complex_object.target_value: return complex_object.with_a_long_name.some_value\n\n# bad\nif condition and another_condition: return value\n\n# good\nif satisfied: return tip\n\n# Acceptable switch case like conditioning\nif very_happy: return tip * 1.5\nif angry: return lip\nreturn tip\n```\n\n### Ternary\n\n * Avoid the ternary statement except in cases where all expressions are extremely trivial.\n * Avoid multiple conditions in ternaries.\n * Use the ternary operator over if/else/ constructs for single line conditionals.\n\n```python\n# bad\nreturn val if condition and other_condition else other_val\n\n# bad\nif some_condition:\n    return val\nelse:\n    return other_val\n\n# good\nreturn val if some_condition else other_val\n```\n\n * Do not nest ternary expressions.\n\nPrefer if/else constructs in those cases.\n\n```python\n# sinful\nx = val if some_condition else other_val if nested_condition else something_else\n\n# good\nif some_condition:\n    x = val\nelse:\n    x = other_val if nested_condition else something_else\n```\n\n### Forgiveness vs Permission\n\nIt is 'Easier to ask for forgiveness than permission'.\n\n * When writing code that may throw an exception do not check for that possibility.\n * Assume it will work and catch the exception.\n * Make sure to catch the expected exception and not all exceptions.\n\n```python\n# bad\nif some_dict.has_key('a_key'):\n    do_something(some_dict['a_key'])\nelse:\n    ...\n\n# good\ntry:\n    do_something(some_dict['a_key'])\nexception KeyError:\n    ...\n\n# bad\nif val != 0:\n    10 / val\n\n# good\ntry:\n   10 / val\nexcept ZeroDivisionError:\n    ...\n```\n\n## Strings\n\n * Do not compare strings with `is`.\n\n`is` is inconsistent when comparing strings.\n\n\n```python\n# bad\nname is 'bob'\n\n# good\nname == 'bob'\n```\n\n * It is acceptable to compare with `is` when a global object is expected.\n\n```python\nBOB = 'bob'\n\ndef get_name():\n    return BOB\n\nname = get_name()\n\n# acceptable\nif name is BOB:\n    ...\n```\n\n### Double vs single Quotes\n\n * Prefer double quotes when your string has multiple words or when formatting (`\"hi bob\"`).\n * If the string is a single word use single quotes (`'bob'`).\n\n### Concatenation\n\n * Prefer string formatting over string concatenation.\n * When formatting prefer fstrings over `str#format` (f\"hi {name}\")\n\nBesides for being cleaner this optimizes performance (less work for GC)\n\n```python\n# bad\nemail_with_name = user.name + ' \u003c' + user.email + '\u003e'\n\n# good\nemail_with_name = \"{} \u003c{}\u003e\".format(user.name, user.email)\n\n# better\nemail_with_name = \"{name} \u003c{mail}\u003e\".format(name=user.name, mail=user.email)\n\n# best\nemail_with_name = f\"{user.name} \u003c{user.mail}\u003e\"\n\n```\n\n * Avoid appending strings (`+=`) when you need to construct large data chunks. Instead, use `.join`.\n\n```python\n# bad\nstatement = ''\nfor i in a_list:\n    statement += i\n\n# good\n''.join(i for in a_list)\n```\n\n## Methods/Functions/Callables\n\nSee [class functions](#class-methods) as well.\n\nCheckout [Builtin Functions For Collections](#builtin-functions-for-collections) as well.\n\n * A callable's body should be no more than four statements.\n * Predicate methods are encouraged.\n\n### Arguments/Parameters\n\n * Your functions should have up to 3 parameters (not including self and kind).\n * Use packing syntax (\\*args, \\*\\*kwargs) in method declarations to reduce arguments.\n * When packing arguments, pack those who are most related to each other and provide a meaningful name.\n\n```python\n# Bad\ndef post_answer(**kwargs):\n    requests.post(kwargs['host'] + kwargs['path'], json=kwargs['body'])\n\n# Still not good\ndef post_answer(host, path, **kwargs):\n    requests.post(host + path, json=kwargs)\n\n# Good\ndef post_answer(host, path, **body):\n    requests.post(host + path, json=body)\n```\n\n * Parameters that are only received to pass to other functions should be reduced.\n\nIf you see a parameter untouched being passed to a few functions, try to put that data into an object or closure who will be passed instead.\n\n```python\n    # Bad\n    def funk1(container, key, erase=True):\n        # Do some other actions\n        funk2(\"value\", container, key, erase)\n\n    def funk2(funk2_param, container, key, erase=True):\n        # Do some other actions\n        funk3(container, key, erase)\n\n    def funk3(container, key, erase):\n        if erase:\n            try:\n                del container[key]\n            except KeyError:\n                print \"no key:{} to erase\".format(key)\n\n\n    funk1(container, key)\n\n    # Good\n    def funk1(container, key, erase=True):\n        # Do some other actions\n        def erase_later():\n            if erase: del container[key]\n        funk2(\"value\", erase_later)\n\n\n    def funk2(funk2_param, action):\n        # Do some other actions\n        funk3(action)\n\n\n    def funk3(action):\n        try:\n            action()\n        except KeyError:\n            print \"no key:{} to erase\".format(key)\n\n\n    funk1(container, key)\n\n    # Also good\n    def funk1(container, key, erase=True):\n        # Do some other actions\n        funk2(\"value\", container=container, key=key, erase=erase)\n\n    def funk2(funk2_param, **funk3_params):\n        # Do some other actions\n        funk3(**funk3_params)\n\n    def funk3(container, key, erase):\n        if erase:\n            try:\n                del container[key]\n            except KeyError:\n                print \"no key:{} to erase\".format(key)\n\n\n    funk1(container, key)\n```\n\n * Default arguments are encouraged, but DO NOT use mutable objects.\n\nDefault arguments are evaluated once only during module load time.\n\nSo the same original object will be constantly used and modified, when you probably intended a new object.\n\n```python\ndef action(a_list=[]):\n    a_list.append(1)\n    return a_list\n\naction() # =\u003e [1]\naction() # =\u003e returned [1,1] when you expected [1]\n```\n\n * Do not use a statement/expression (something to be evaluated) within an argument list\n\n\n```python\n# Very bad\ndef funktion(a, b=mod.get_list()):\n  pass\n```\n\n### Parameter Passing\n\n * It is best to be explicit when passing arguments.\n * Especially boolean options.\n\nThis adds readability.\n\n```python\ndef save_file(file_name, log):\n    if log:\n        print(\"Saving file {}\".format(file_name))\n    ...\n# Very Bad\nsave_file(\"myfile.txt\", True)\n\n# Good\nsave_file(\"myfile.txt\", log=True)\n\n# Better\nsave_file(file_name=\"myfile.txt\", log=True)\n```\n\n### Dynamic Parameter Expansion\n\n * You can pass a dynamic list of parameters by using unpacking syntax.\n\n`func_call(*args, **kwargs)`\n\n### Lambdas and Nested Functions\n\n * We encourage using closures to create partial applications and the like.\n\nThis is especially true when used to add readability to comprehension statements\n\n```python\n# bad\nusers_from_nyc = [create_user(user, 'nyc', paid=True)\n                  for user in users]\n\n# good\ncreate_user_from_nyc = lambda user: create_user(user, 'nyc', paid=True)\nusers_from_nyc = [create_user_from_nyc(user) for user in users]\n```\n\n * Use lambdas for one liners.\n * If a lambda expression is long (90+ chars) use a nested function.\n * Prefer existing standard library functions over lambdas.\n\n```python\n# bad\nmy_multiply = lambda a,b: a * b\nmy_multiply(5,5)\n\n# good\nfrom operator import mul\nmul(5,5)\n```\n\n### Recursion\n\n * Avoid recursion.\n\nThe for loop is, effectively, the same abstraction that recursion provides in functional programming languages.\n\n## Collections/Iterables\n\nThere are three major skills to apply to collections.\n\nslicing, comprehensions, and utilizing builtin functions.\n\nThe purpose of this writing is to automatically associate our collection solutions with\nthese three tools.\n\nPushing us away from using code as such:\n\n```python\n    # Bad\n    my_new_list = []\n\n    for i in old_list:\n        if some_check(i):\n            my_new_list.append(i)\n\n```\n\n### Slice\n\nslicing helps a lot, especially with strings.\n\n```python\n    # Slice structure\n    items[start:end:step]\n```\n\nA slice can have up to three attributes.\n\nstart - our starting index\n\nend - the index in which to stop (not including)\n\nstep - the stepping process\n\nstep is interesting because it controls how to jump between items including direction.\n\ni.e.\n\n```python\n    items[::-1] # list reversed\n    range(100)[2::2] # return all even numbers\n    items[4::-1] # starting at 5th position go backwards\n\n    def palindrome_check(s):\n        midway, is_odd = divmod(len(s), 2)\n\n        return s[midway - 1::-1] == s[midway + is_odd:]\n\n    palindrome_check('hello olleh') # =\u003e True\n    palindrome_check('noon') # =\u003e True\n    palindrome_check('joe') # =\u003e False\n```\n\n#### Using Slice\n\n * Use implicit values\n\n```python\n# bad\nend_of_list = len(a_list)\na_list[2:end_of_list]\n\n# good\na_list[2:]\n\n# bad\na_list[0:5]\n\n# good\na_list[:5]\n\n# bad\nreversed = a_list[0:end_of_list:-1]\n\n# good\nreversed = a_list[::-1]\n```\n\n * Do not compromise readability when using slice\n\nConsider using `slice` constructor\n\n```python\nnumbers = range(100)\n\n# bad\nnumbers[2::2] # =\u003e all even numbers starting from 2\n\n# good\neven_numbers = slice(2, None, 2)\n\nnumbers[even_numbers] # =\u003e all even numbers starting from 2\n```\n\n### Comprehensions\n\n#### list comprehension\n\nSyntax is:\n\n```python\n[i for i in numbers]\n```\n\n * Prefer to use list comprehension when mapping\n\n```python\n# bad\nresult = []\nfor i in numbers:\n    result.append(i * 2)\n\n# bad\nresult = map(lambda i: i * 2, numbers)\n\n# good\nresult = [i * 2 for i in numbers]\n```\n\n * Prefer to filter with list comprehension\n\n```python\n# bad\nodd_numbers = []\nfor i in numbers:\n    if i % 2 == 1:\n      odd_numbers.append(i)\n\n# not preferred\nis_odd = lambda i: i % 2 == 1\nodd_numbers = filter(is_odd, numbers)\n\n# good\nis_odd = lambda i: i % 2 == 1\nodd_numbers = [i for i in numbers if is_odd(i)]\n```\n\n * Keep logic out of comprehension statements (use callables)\n\n```python\n# bad\nodd_numbers = [i for i in numbers if i % 2 == 1]\n\n# good\nis_odd = lambda i: i % 2 == 1\nodd_numbers = [i for i in numbers if is_odd(i)]\n```\n\n#### Generator Statements aka lazy iteration\n\nsyntax is:\n\n```python\n(i for i in a_list)\n```\n\nGenerator statements allows filtering, chaining, and all the same results list comprehension can accomplish.\n\nHowever, generators are lazy. Meaning they only evaluate and return a value upon call.\n\n * Always prefer generator statements over temporary list construction\n\nMany times we construct lists just to produce another list in order to use that list.\n\nGenerators reduce how many times we iterate over the same object.\n\n```python\ngroups = [\n    [\"sara\", \"sally\", \"bob\"],\n    [\"todd\", \"gary\", \"jon\"],\n    [\"sue\", \"jerry\", \"bob\"]\n]\n\ndef invite_bobs_friends(people):\n    for person in people:\n        invite(person)\n\n# bad\npeople_who_know_bob = [person\n                       for group in groups\n                       if \"bob\" in group\n                       for person in group]\n\npeople_who_know_bob_excluding_bob = [person for person in people_who_know_bob if person != 'bob']\n\ninvite_bobs_friends(people_who_know_bob_excluding_bob)\n\n# good\npeople_who_know_bob = (person\n                       for group in groups\n                       if \"bob\" in group\n                       for person in group)\n\n\npeople_who_know_bob_excluding_bob = (person for person in people_who_know_bob if person != 'bob')\n\ninvite_bobs_friends(people_who_know_bob_excluding_bob)\n```\n\n#### Chaining Iterations\n\n * Use new lines to maintain readability\n * Avoid using over 2 statements within list comprehension\n\n```python\ngroups = [\n    [\"sara\", \"sally\", \"bob\"],\n    [\"todd\", \"gary\", \"jon\"],\n    [\"sue\", \"jerry\", \"bob\"]\n]\n\n# still bad\npeople = []\nfor group in groups:\n    for person in group:\n        people.append(person)\n\n# good\npeople = [person for group in groups for person in group]\n\n# better\npeople = [person for group in groups\n          for person in group]\n\n# hard to follow\npeople_who_know_bob = [person for group in groups if \"bob\" in group for person in group]\n\n# better but still hard to follow\npeople_who_know_bob = [person\n                       for group in groups\n                       if \"bob\" in group\n                       for person in group]\n```\n\n * Consider using generators to maintain readability\n\n```python\ngroups = [\n    [\"sara\", \"sally\", \"bob\"],\n    [\"todd\", \"gary\", \"jon\"],\n    [\"sue\", \"jerry\", \"bob\"]\n]\n\n# best\ngroups_with_bob = (group for group in groups if \"bob\" in group)\n\npeople_who_know_bob = [person for person in groups_with_bob]\n```\n\n### Builtin Functions For Collections\n\nThere are builtin functions that satisfy most use cases with collections (lists, tuples,\ndicts, sets, etc).\n\nActions on collections like sorting, value checking, and creating new unique lists can accomplished with builtins.\n\nHowever, Python's for loops where optimized for high performance (comprehension even more so).\n\nPython is not optimized for function calling and recursion.\n\nThis means a regular for loop may out perform a recursive solution.\n\nConsidering this, we prefer loops over functional methods like map and filter.\n\nThis rule is not iron clad.\n\nFunctional programming is greatly beneficial from a design standpoint.\n\nThere are many third party libraries dedicated to functional programming.\n\nThere are also many `builtins` that have the best of both, becasue they can accept comprehension statements.\n\nHere is a list of useful builtin functions:\n\n  1. all - checks if all values are truthy\n  1. any - checks if any value is truthy\n\n`all` and `any` check if all or any values in an iterable are truthy\n\n```python\n    all([1,2,0]) # =\u003e False\n    all([1,2,5]) # =\u003e True\n\n    any([0,9,0]) # =\u003e True\n    any([])  # =\u003e False\n\n    # Very useful with comprehension statement\n    any(i for i in numbers if is_even(i))\n\n```\n\n * Be careful when using `all`\n\nAll returns `True` when given an empty list or a generator that yields nothing.\n\n```python\nnumbers_over_4 = (i for i in range(4) if i \u003e 6) # =\u003e yields no values\nall(numbers_over_4) # =\u003e True\n```\n\n * When asserting values prefer not to use `all`. Instead use regular `for` loop.\n\n`assert all` hides which value failed.\n\n```python\n# sexy but bad\nassert all(value for value in a_list)\n\n# good\nfor value in a_list:\n    assert value\n\n# better\nfor value in a_list:\n    assert value, \"bonus descriptive message about {}\".format(value)\n```\n\n  1. enumerate - iterate with accompanying climbing value\n\n```python\nfor index, name in enumerate(['bob', 'joe', 'sam']):\n    print 'index:', index\n    print 'name:', name\n```\n\n  1. filter - filters a list\n\nIn python3 filter produces an iterator\n\n```python\nfilter(lambda i: i % 2, range(10))\n```\n\n  1. map - creates a list based on values of another list\n\nThere is no reason to use map unless you want to map two iterables with a function that would accept two parameters (one from each iterable).\n\nEven then you can probably get the same result using `zip`.\n\n```python\n# bad\nmap(lambda i: i * 10), numbers)\n\n# prefer\n[i * 10 for i in numbers]\n\n# acceptable\n# apply two lists to function that takes two parameters\nadd = lambda i,j: i + j\nmap(add, numbers1, numbers2)\n\n# consider\n[sum(values) for values in zip(numbers1, numbers2)]\n```\n\n  1. max | min - gets max or min value of list\n  1. sum - adds the values (should be int) of list\n  1. reduce - with logic creates a single value from list values\n  1. set - distinct list\n  1. sorted - sorts values of collection\n  1. in - finds item in collection (`in` is a operator)\n\nLook at the documentation [Python3 Builtins](https://docs.python.org/3/library/functions.html)\n\nMost of the these builtin functions and others accept a comprehension statement.\n\nRemember comprehension in python is optimized.\n\nAdd on that the ability to filter and map.\n\nTherefore, using comprehension syntax is always preferred.\n\n```python\n' '.join(word for word in speech if allowed(word))\n\nany(word for word in speech if allowed(word))\n```\n\nFor more tools used for manipulating iterables checkout [Itertools](https://docs.python.org/3.5/library/itertools.html)\n\n### Tuples\n\n * Don't use implied tuples\n\nIt isn't clear that implied tuples are tuples, so avoid them.\n\nUnless it's part of a DSL or multiple instantiation where our intentions are clear.\n\n```python\n# Bad\na_tuple = 9,\na_long_triple = 7, 8, 9\n\n# Good\na, b, c = 7, 8, 9\n\n# Also good\ndef return_triple():\n    return 7, 8, 9\n\na, b, c = return_triple()\n\n# Good DSL examples\nassert False, 'This test was suppose to fail'\n\nprint 'Put', 'spaces', 'between', 'these', 'words'\n```\n\n### Tuple vs list\n\n  * Prefer tuples over lists\n\nWhen deciding which collection type to use prefer tuple,\nespecially if that collection is not going to be changed.\n\n```python\n# Bad\ndef compare_interests(other_persons_interests):\n    interests = ['long walks on the beach', 'eating out', 'skateboarding']\n    in_common = 0\n\n    for interest in interests:\n        if interest in other_persons_interests:\n            in_common += 1\n\n    return in_common\n\n# Good\ndef compare_interests(other_persons_interests):\n    interests = ('long walks on the beach', 'eating out', 'skateboarding')\n    in_common = 0\n\n    for interest in interests:\n        if interest in other_persons_interests:\n            in_common += 1\n\n    return in_common\n\n# Even if you are manipulating the contents of a collection still try to use a\n# tuple.\n\n# Good\ndef compare_interests(other_persons_interests):\n    interests = ('long walks on the beach', 'eating out', 'skateboarding')\n    in_common = ()\n\n    for interest in interests:\n        if interest in other_persons_interests:\n            in_common += (interest,)\n\n    return in_common\n```\n\n## Imports\n\n * Imports should be at the top of a file\n * Imports should be grouped by standard library, third party libraries, and local application.\n\n```python\nimport os\nimport sys\n\nimport flask\nfrom retrying import retry\n\nfrom app.lib.klass import Klass\n```\n\nThere are several ways to import a module in python\n\n```python\n1) import package.a           # Absolute import\n2) import package.a as a_mod  # Absolute import bound to an alias\n3) from package import a      # Alternate absolute import\n4) import a                   # Implicit relative import (deprecated, py2 only)\n5) from . import a            # Explicit relative import\n```\n\n * Avoid implicit relative import\n\nYou shouldn't be using implicit relative import (4th syntax), since it only works in python2 and runs the risk of clashing with other 3rd party modules.\n\n * Import what you need not the entire module\n\nDoing so avoids unwanted side effects, especially when mocking in tests.\n\nIt also makes the current working module lighter, since it doesn't contain another whole module within itself.\n\nIf you want to avoid name clashing or the namespace's name adds to understanding intent, then use aliasing.\n\n\n```python\n# not the best\nimport os\n\nos.path(...)\n\n# good\nfrom os import path\n\npath(...)\n\n# super\nfrom os import path as os_path\n```\n\n * Refrain from using wild card, `*`, imports\n\nIt improves readability when we use explicit imports\n\n * Prefer single import statement with multiple objects when they come from the same module\n\n```python\n# not the best\nfrom os import path\nfrom os import environ\n\n# good\nfrom os import path, environ\n```\n\n * Do not import multiple unrelated modules in single import statement\n\n```python\n# bad\nimport os, sys\n\n# good\nimport os\nimport sys\n```\n\n## Classes\n\n * When defining a class who does not inherit pass `object` as it's inherit class.\n * Avoid class attributes. Attributes should be limited to the scope of instances.\n * Use conventions of encapsulation when defining a class (on methods and attributes).\n\n```python\nclass Klass(object):\n    # Public\n    def public_method(self):\n        self.public_attribute\n\n    # Protected\n    def _protected_method(self):\n        self._protected_attribute\n\n    # Private\n    def __private_method(self):\n        self.__private_attribute\n\n```\n\n * It is bad practice to add attributes to an instance from outside of that class.\n\n```python\n# bad\nclass Person(object): pass\n\npete = Person()\npete.name = 'Peter'\n\n# acceptable\nclass Person(object):\n    def give_name(self, name):\n        self.name = name\n\npete = Person()\npete.give_name('Peter')\n```\n\n * Private attributes are preferably defined with only one underscore instead of two.\n\nDouble underscores trigger name mangling. This makes debugging more difficult.\n\n```python\nclass Person(object):\n    def __init__(self, name):\n        self._private_name = name\n```\n\n### Class Methods\n\nAs mentioned [above](#classes), use conventions of encapsulation when defining class methods.\n\n * Prefer to define private static methods outside of the class in the surrounding scope.\n\nFunctions whom are just implementaion details should not be defined in the class. \n\nJust put the function in the surrounding scope.\n\n```python\n# bad\nclass Person(object):\n    def eat(self, food):\n        ...\n        self.calories = self._add(self.calories, food.calories)\n        ...\n\n    @staticmethod\n    def _add(a, b):\n        return a + b\n\npete = Person()\npete._add(11, 12) # =\u003e 23\n\n# good\nclass Person(object):\n    def eat(self, food):\n        ...\n        self.calories = _add(self.calories, food.calories)\n        ...\n\ndef _add(a,b):\n    return a + b\n\npete = Person()\npete._add(11, 12) # =\u003e AttributeError\n```\n\n## Exceptions\n\n### Catching Exceptions\n\n * Catch specific exceptions whenever possible\n * Do not use a bare `except:` clause.\n\n```python\n# bad\ntry:\n  # an exception occurs here\nexcept:\n  # exception handling\n\n# good\ntry:\n  # an exception occurs here\nexcept ValueError:\n  # exception handling\n\n# acceptable\ntry:\n  # an exception occurs here\nexcept Exception:\n  # exception handling\n```\n\n#### EAFP\n\n**EAFP** = Easier to ask forgiveness than permission.\n\nAs seen in the conditions section, exception catching is preferred over checking an object before using.\n\nPython's performance does not suffer when exception handling.\n\n * Do not to abuse this rule in your flow control.\n\nEven though exception handling has been optimized in python do not use exceptions for you flow control.\n\n```python\n# bad\ndef action():\n    for i in items:\n        if i == UNWANTED_ITEM:\n            raise EXCEPTION(\"contains unwanted item\")\n        else:\n            ...\n\n# good\ndef action():\n    for i in items:\n        if i == wanted:\n            return \"contains unwanted item\"\n        else:\n            ...\n```\n\n * Catch expected exceptions locally\n\n```python\n# bad\ndef has_wanted_key(a_dict, wanted_key):\n    a_dict[wanted_key]\n    return True\n\ntry:\n    has_wanted_key(a_dict, a_key)\nexcept KeyError:\n    ...\n\n# good\ndef has_wanted_key(a_dict, wanted_key):\n    try:\n        a_dict[wanted_key]\n        return True\n    except KeyError:\n        return False\n\nhas_wanted_key(a_dict, a_key)\n```\n\n### Custom Exceptions\n\n * Exception names should end with the word Error (`MyCustomError`)\n * Within a project define a base exception for all other custom exceptions to inherit from.\n * All exceptions should be grouped together in an exceptions module `app.lib.exceptions.*`\n\n## Regular Expressions\n\n * Use single quote raw strings (`r'.+'`) for regex values.\n\nRaw strings preserve escape characters.\n\nThis will save you from unexpected behavior.\n\n * Avoid star wild cards `*`, use plus `+` instead.\n\nBugs are caused because `*` also allows for zero occurrences.\n\n * Complex regex should be compiled before using.\n\nThe resulting variable's name should clearly define it's purpose.\n\n```python\n# Bad\npattern = re.match(r'(?:3[01])|(?:[0-2]\\d)-(?:1[0-2])|(?:0\\d)-\\d{4}$', '31-11-1985')\npattern.match('31-11-1985')\n\n# Good\nvalid_date_pattern = re.compile(r'(?:3[01])|(?:[0-2]\\d)-(?:1[0-2])|(?:0\\d)-\\d{4}$')\nvalid_date_pattern.match('31-11-1985')\n```\n\n### Usage\n\n * Regex is not preferable in python.\n\nThere are plenty of tools which are preferred (i.e. `in`, `startswith`, `endswith`, `isdigit`, `istitle`, and more).\n\n```python\n# Bad\nis_bob_pattern = re.compile(r'^Bob')\nis_bob_pattern.match(name)\n\nhas_a_qu_pattern = re.compile(r'qu', re.I)\nhas_a_qu_pattern.match(word)\n\n# Good\nname.startswith('Bob')\n\n'qu' in word.lowercase()\n```\n\n\n## Main Contributers:\n* [Avraf](https://github.com/avraf)\n* [Eyalstoler](https://github.com/eyalstoler)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkenshoo%2Fpython-style-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkenshoo%2Fpython-style-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkenshoo%2Fpython-style-guide/lists"}