Python Style Guide

Conteúdo

  1. Tipos
  2. Referências
  3. Dicionários
  4. Listas
  5. Strings
  6. Funções
  7. Classes & Construtores
  8. Módulos
  9. Iteradores & Geradores
  10. Variáveis

Tipos

  • 1.1 Primitivos: Quando você acessa um tipo primitivo você trabalha direto com seu valor.

    • string
    • number
    • boolean
    • None

    foo = 1
    bar = foo
    
    bar = 9
    
    print(foo, bar) # => 1, 9

  • 1.2 Complexos: Quando você acessa um tipo complexo você trabalha com a sua referência.

    • dict
    • list
    • function

    foo = [1, 2]
    bar = foo
    
    bar[0] = 9
    
    print(foo[0], bar[0]) # => 9, 9

⬆ voltar ao topo

Referências

  • 2.1 Python não tem um tipo constant, então você precisa se atentar à convenção de sempre utilizar os nomes em UPPERCASE e nunca modificá-los.

    Por que? Isso garante que você não altere o valor da constante, evitando bugs e não dificultando a compreensão do código.

    # bad
    foo = 1
    bar = 2
    
    # good
    FOO = 1
    BAR = 2

⬆ voltar ao topo

Dicionários

  • 3.1 Use a sintaxe literal para declaração de um dicionário.

    # bad
    item = dict()
    
    # good
    item = {}

  • 3.2 Use spread ** ao invés de copy() para copiar e extender dicionários.

    # bad
    original = {'a': 1, 'b': 2}
    clone = original.copy()
    clone.update({'c': 3})
    
    # good
    original = {'a': 1, 'b': 2}
    clone = {**original, 'c': 3}
    
    # good
    original = {'a': 1, 'b': 2}
    original_2 = {'c': 3, 'd': 4}
    long_clone = {**original, **original_2, 'e': 5}

  • 3.3 Se o dicionário tiver mais de um item, utilize sempre uma quebra de linha antes e depois do conteúdo do dicionário.

    # bad
    single_map = {
        'a': 1,
    }
    
    # bad
    item_map = {
        'a': 1, 'b': 2, 'c': 3,
    }
    
    # good
    single_map = {'a': 1}
    
    item_map = {
        'a': 1,
        'b': 2,
        'c': 3,
    }

  • 3.4 Use dict.get(key) para acessar um item do dicionário.

    Por que? Usando dict[key] seu código irá quebrar ao tentar acessar uma key inexistente no dicionário, e isso requere validações desnecessárias ao tentar acessar um item.

    item_map = {
        'a': 1,
        'b': 2,
    }
    
    # bad - KeyError 'c' não existe
    item_map['c']
    
    # bad
    try:
        item_map['c']
    except KeyError:
        item_map['c'] = 3
        return item_map['c']
    
    # good
    item_map.get('c')
    
    # good
    item_map['c'] = item_map.get('c') or 3

⬆ voltar ao topo

Listas

  • 4.1 Usa a sintaxe literal para a declaração de uma lista.

    # bad
    items = list()
    
    # good
    items = []

  • 4.2 Use spreads * para copiar e extender listas.

    # bad
    items = ['a', 'b']
    clone = items.copy() + ['c']
    
    # good
    items = ['a', 'b']
    clone = [*items, 'c']
    
    # good
    items = ['a', 'b']
    items_2 = ['c', 'd']
    clone = [*items, *items2, 'e']

  • 4.3 Sempre utilizar quebra de linhas antes e depois do conteúdo da lista caso ela contenha mais de uma linha.

    # bad
    items = [
        [0, 1], [2, 3], [4, 5],
    ]
    
    # bad
    dict_list = [{
        'id': 1
    }, {
        'id': 2
    }]
    
    # bad
    number_list = [
        1, 2,
    ]
    
    # good
    items = [[0, 1], [2, 3], [4, 5]]
    
    # good
    dict_list = [
        {'id': 1},
        {'id': 2},
    ]
    
    # good
    number_list = [1, 2]
    
    # good
    number_list = [
        1,
        2,
    ]

  • 4.4 Utilizar list comprehension sempre que possível

    # bad
    my_input_numbers = [1, 2, 3, 4, 5, 6, 7, 8]
    for number in my_input_numbers:
        if number % 2 != 0:
            my_odd_numbers.append(number)
    
    #good
    my_input_numbers = [1, 2, 3, 4, 5, 6, 7, 8]
    my_odd_numbers = [x for x in my_input_numbers if x % 2 != 0]

⬆ voltar ao topo

Strings

  • 5.1 Use aspas simples '' para strings.

    Por que? Menos necessidade de "escapar" caracteres, código mais limpo e mais fácil de realizar buscas.

    # bad
    name = "Kabum Comércio Eletrônico S.A."
    
    # bad
    json_string = "{\"a\": 1}"
    
    # good
    name = 'Kabum Comércio Eletrônico S.A.'
    
    # good
    json_string = '{"a": 1}'

  • 5.2 Strings que ultrapassem 79 caracteres não devem conter quebra de linhas utilizando concatenação de strings.

    Por que? Strings com quebra de linha utilizando concatenação deixa o código mais poluído e dificulta a busca.

    # bad
    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.'
    
    # bad
    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.'
    
    # good
    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.'

  • 5.3 Quando construir strings programaticamente, utilizar format() ao invés de concatenação e do %s.

    # bad
    def hello(name):
        return 'Hello, ' + name + '!'
    
    # bad
    def hello(name):
        return ''.join(['Hello, ', name, '!'])
    
    # bad
    def hello(name):
        return 'Hello, %s!' % name
    
    # good
    def hello(name):
        return 'Hello, {}!'.format(name)
    a = 1
    b = 2
    c = 3
    
    'a: {} b: {} c: {}'.format(a, b, c)
    
    'a: {a} b: {b} c: {c}'.format(a=a, b=b, c=c)
    
    param = {'a': a, 'b': b, 'c': c}
    'a: {a} b: {b} c: {c}'.format(**param)

  • 5.5 Não "escape" strings desnecessariamente.

    Barras invertidas atrapalham a leitura do código, por isso só use quando for necessário.

    # bad
    foo = '\'this\' \i\s \"quoted\"'
    
    # good
    foo = '\'this\' is "quoted"'

⬆ voltar ao topo

Funções

  • 6.1 Use parâmetros com valor default ao invés de alterar o parâmetro da função

    # really bad
    def do_something(opt):
        # Não devemos alterar o valor dos parâmetros da função.
        opt = opt or 'foo'
        # ...
    
    # still bad
    def do_something(opt):
        if (opt is None):
            opt = 'foo'
        # ...
    
    # good
    def do_something(opt='foo'):
        # ...

  • 6.2 Não use tipos complexos como parâmetros default.

    Por que? Como vimos aqui, uma variável de tipo complexo é uma referência, ou seja, somente uma única instância dessa referência vai ser alterada.

    # bad
    def init_list(value, new_list=[]):
        new_list.append(value)
        return new_list
    
    init_list(1)
    # => [1]
    init_list(2)
    # => [1, 2]
    
    # good
    def init_list(value, new_list=None):
        if new_list is None:
            new_list = []
        new_list.append(value)
        return new_list
    
    init_list(1)
    # => [1]
    init_list(2)
    # => [2]

  • 6.3 Não utilizar espaços na assinatura da função

    Por quê? Código mais legível e facilita nas buscas.

    # bad
    def foo(a): print(a)
    def bar (b): print(b)
    
    # good
    def foo(a): print(a)
    def bar(b): print(b)

  • 6.4 Nunca altere o valor de um parâmetro de função.

    Por que? Alterar o valor de um parâmetro pode ocasionar bugs e comportamentos inesperados.

    # bad
    def fn_1(a):
        a = 1
        # ...
    
    # bad
    def fn_2(a):
        if (a > 10): a = 10
        # ...
    
    # good
    def fn_3(a):
        b = a or 10
        # ...
    
    # good
    def fn_3(a):
        b = a
        if (b > 1): b = 1
        # ...
    
    # good
    def fn_4(a=1):
        # ...

  • 6.5 Funções com assinatura em mais de uma linha devem ser indentadas como qualquer outra lista ou dicionário multilinha.

    # bad
    def some_fn(foo,
                bar,
                baz):
        # ...
    
    # bad
    def some_fn(foo,
      bar,
      baz):
        # ...
    
    # good
    def some_fn(
        foo,
        bar,
        baz,
    ):
        # ...

  • 6.6 Utilize o nome do parâmetro ao chamar a função.

    Por que? Fica mais claro qual o propósito daquela função e futuramente em uma necessidade de alterar a assinatura da função é menos provável que aconteça erros por utilizar "keyword arguments" ao invés de "normal arguments". Veja aqui

    def move(x, y, roll=False):
        # ...
    
    # bad - não é claro o que a função faz
    move(1, 0, True)
    
    # good
    move(x=1, y=0, roll=True)
    
    # futuramente fica mais fácil adicionar novos parâmetros
    def move(x, y, z=0, roll=False):
        # ...

⬆ voltar ao topo

Classes & Construtores

⬆ voltar ao topo

Módulos

⬆ voltar ao topo

Iteradores & Geradores

⬆ voltar ao topo

Variáveis

⬆ voltar ao topo