Introducere

Prezentul este inspirat (mai mult tradus, de fapt) din PEP 8, ghidul de stil pentru Python. Orice sugestii cu privire la îmbunătăţirea acestei pagini sunt binevenite, dar probabil o să vrem să păstrăm majoritatea lucrurilor aşa cum sunt. În felul ăsta o să fim aliniaţi la ce se practică "out there".

Limba

Deşi toate discuţiile noastre sunt în română, ar fi de preferat ca limba în care scriem comentariile (şi numim variabilele, funcţiile, clasele, etc.) să fie engleză. În felul ăsta scăpăm de pacostea procesului de refactoring şi codul poate fi citit şi îmbunătăţit de alte persoane, chiar dacă asta nu intră explicit în planurile noastre momentan. În plus, e un exerciţiu util.

Excepţii

Atunci când folosirea standardelor ar face citirea codului mai greoaie, este acceptat să le încălcăm, dar numai după o evaluare atentă a situaţiei. În general, nu există nicio scuză pentru a numi identificatorii altfel decât conform standardelor.

Aşezarea codului

  • Folosim 4 spaţii pentru indentare. Setăm editoarele să înlocuiască tab-urile cu 4 spaţii.
  • Limităm toate liniile la 79 de caractere. Pentru "paragrafe" de comentarii, se recomandă limitarea la 72 de caractere.
  • Liniile le spargem folosind blackslash-uri sau aliniind linia nouă la ultima paranteză deschisă:
    if color == 'red' and emphasis == 'strong' and \
       highlight > 100:
        print "This is a test"
    
    if height == 0 and width == 0 and (color == 'red' or
                                       emphasis is None):
        raise ValueError("Another test")
    
  • Folosim 2 linii goale pentru a separa clasele şi funcţiile "top-level".
  • Folosim o linie goală pentru a separa funcţiile dintr-o clasă.
  • Putem folosi o linie goală pentru a separa bucăţile logice dintr-o funcţie mai lungă, dar acest lucru ar trebui evitat (oricum, funcţiile lungi ar trebui să fie rare).
  • Python recunoaşte caracterul ^L (Control-L) ca spaţiu alb. Multe editoare îl folosesc pentru a separa paginile.
  • Folosim setul de caractere ASCII pentru surse. Python 3.0 recomandă UTF-8, dar probabil o să folosim 2.x.

Declaraţiile import

  • Declaraţiile import le punem pe linii separate:

Da:

import os
import sys

Nu:

import os, sys
  • Dar atunci când importăm numai unii identificatori dintr-un modul putem scrie:
    from subprocess import Popen, PIPE
    
  • Declaraţiile import le punem întotdeauna la începutul fişierului, după declararea şi comentarea modulului, dar înainte de globale şi constante.
  • Importurile le împărţim în următoarele grupe, separate prin linii albe:
    1. importuri din biblioteca standard
    2. importuri din biblioteci third-party
    3. importuri de module specifice aplicaţiei noastre
  • Punem eventualele specificaţii __all__ după importuri. TODO: ce sunt specificaţiile __all__?
  • Folosim numai importuri absolute pentru modulele aparţinând pachetelor noastre.
  • Atunci când importăm o clasă dintr-un modul, putem să o importăm doar cu numele:
    from myclass import MyClass
    # Acum o putem folosi sub numele de MyClass
    
    
  • Dacă însă procedeul precedent cauzează conflicte de nume, le importăm calificate:
    import myclass
    # Acum o putem folosi ca myclass.MyClass
    
    

Spaţiul alb

Nu folosim spaţiu alb excesiv. În special, îl evităm în următoarele situaţii:

  • în interiorul imediat al parantezelor de orice fel:

Da: spam(ham[1], {eggs: 2})
Nu: spam( ham[ 1 ], { eggs: 2 } )

  • imediat înainte de o virgulă, punct-şi-virgulă, sau două-puncte:

Da: if x == 4: print x, y; x, y = y, x
Nu: if x == 4 : print x , y ; x , y = y , x

  • imediat înainte de o paranteză corespunzătoare unui apel de funcţie:

Da: spam(1)
Nu: spam (1)

  • imediat înaintea unei paranteze drepte corespunzătoare unei indexări sau unui slice:

Da: dict['key'] = list[index]
Nu: dict ['key] = list [index]

  • mai mult de un singur spaţiu în jurul unui operator:

Da:

a = 1
b = 2
long_var = 3

Nu:

a        = 1
b        = 2
long_var = 3

Înconjurăm întotdeauna cu un singur spaţiu următorii operatori: atribuire (=), atribuire augmentată (+=, -=, etc.), comparatori (<, <=, etc.), aritmetici (+, -, etc.).

Nu folosim spaţii în jurul semnului = atunci când indică valoarea unui argument de tip "keyword" sau valoarea implicită a unui parametru:

  • Da: return complex(r=real, i=imag)
  • Nu: return complex(r = real, i = imag)

Nu folosim declaraţii compuse (pe o singură linie).

Dacă o instrucţiune for, if, try, etc. este urmată de o singură instrucţiune scurtă, atunci aceasta poate fi pe aceeaşi linie.

Comentarii

  • Comentariile care contrazic codul sunt mai proaste decât lipsa de comentarii. Ar trebui să ţinem comentariile în concordanţă cu codul.
  • Lăsăm întotdeauna un spaţiu între caracterul de comentariu (#) şi text.
  • Comentăm folosind propoziţii complete, în engleză, cu punctuaţie şi capitalizare potrivită.
  • Dacă un comentariu e scurt, putem omite punctul de la finalul propoziţiei.
  • Indentăm caracterul de comentariu la fel ca blocul de cod în care se află.
  • Încercăm să evităm comentariile de la sfârşitul liniei; acestea tind să fie triviale.
  • Dacă totuşi folosim aceste comentarii "inline", separăm caracterul de comentariu de restul liniei prin cel puţin 2 spaţii; celelalte reguli se aplică.

Docstring-uri

Mai multe despre asta la wiki:Docstrings?. (Da, realizez că "docstring-uri" sună ca naiba.)

  • Scriem docstring-uri pentru toate modulele, clasele şi funcţiile publice. Ele nu sunt necesare pentru funcţii ne-publice, dar un comentariu ajută întotdeauna.
  • Convenţia spune că, atunci când un docstring are mai multe linii, ghilimelele de sfârşit sunt singure pe o linie, de obicei separate de restul docstring-ului printr-o linie goală:
    """Return a foobang
    
    Optional plotz says to frobnicate the bizbaz first.
    
    """
    
  • Pentru docstring-uri de o singură linie, ghilimelele de sfârşit pot fi pe aceeaşi linie.

Convenţii de nume

Notă: nu toate modulele Python respectă convenţiile de aici, dar ele sunt recomandate pentru modulele scrise de la zero, deci va trebui să le folosim.

Stiluri de identificatori

  • Prezentate aici pentru a putea fi referite liber mai târziu:
    • b (o singură literă mică)
    • B (o singură literă mare)
    • lowercase
    • lowercase_cu_underscores
    • UPPERCASE
    • UPPERCASE_CU_UNDERSCORES
    • CuvinteCapitalizate (sau CamelCase, pentru că literele mari seamănă cu cocoaşele unei cămile)
      • Notă: când într-un astfel de identificator apar prescurtări, e de preferat să scriem prescurtările cu toate literele mari. Deci HTTPServerError e de preferat faţă de HttpServerError.
    • mixedCase (diferă de CamelCase prin capitalizarea primului cuvânt)
    • Cuvinte_Capitalizate_Cu_Underscores (urât! [sic])
  • Sunt descurajate (deci nu folosim) prefixe înaintea numelor. O împărţire corectă în module e mult mai clară.
  • Se mai folosesc următoarele convenţii:
    • _single_leading_underscore - indicator slab de "uz intern". De exemplu, declaraţia from modul import * nu importă şi identificatorii care încep cu _.
    • single_trailing_underscore - folosit pentru a evita conflictele cu cuvintele cheie Python: Tkinter.Toplevel(master, class_='ClassName')
    • __double_leading_underscore - folosit la un identificator de clasă, invocă mecanismul de "name mangling" (TODO). Astfel, în clasa FooBar, identificatorul __boo devine _FooBar__boo.
    • __double_leading_and_trailing_underscore__ - obiecte sau atribute "magice". Le folosim întotdeauna pe cele documentate, nu inventăm niciodată altele noi. Exemple: __init__, __import__, __file__.

Convenţii propriu-zise

  • Evităm următorii identificatori de o singură literă: l (L mic), O (o mare), I (i mare), pentru că în unele fonturi pot fi uşor confundate.
  • Folosim nume de module şi pachete scurte, formate numai din litere mici, eventual din underscore-uri.
  • Numele de clase sunt CamelCase.
  • Excepţiile sunt tot clase, iar suplimentar se termină în Error, dacă, într-adevăr, sunt erori.
  • Variabilele aparţinând modulelor nu ar trebui exportate. Pentru aceasta este de preferat să folosim mecanismul cu __all__, sau convenţia mai veche de a le numi folosind _single_leading_underscore.
  • Numele de funcţii sunt lowercase_with_underscores.
  • Primul argument al unei funcţii de instanţă îl numim întotdeauna self.
  • Primul argument al unei funcţii de clasă ("statică") îl numim întotdeauna cls.
  • Dacă numele argumentului unei funcţii coincide cu un cuvânt cheie, îi adăugăm un underscore la sfârşit: print_, class_.

Specifice programării orientate pe obiecte

  • Metodele sunt numite după acelaşi standard ca funcţiile: lowercase_with_underscores.
  • Pentru metodele private şi pentru variabilele de instanţă folosim _single_leading_underscore.
  • Pentru a evita suprapuneri de nume cu subclasele unei clase, putem folosi __double_leading_underscores pentru a invoca mecanismul de name mangling. Metoda __a din clasa Foo nu poate fi accesată folosind Foo.__a (deşi un utilizator insistent ar putea folosi Foo._Foo__a).
  • Nu există atribute (metode şi câmpuri) private în Python. Cele care încep cu un underscore sunt considerate ne-publice şi pot fi schimbate oricând. Dacă avem îndoieli, facem atributele iniţial private şi le schimbăm pe măsură ce avem nevoie.

Recomandări legate de programare

  • Codul ar trebui scris astfel încât să nu dezavantajeze alte implementări de Python (PyPy, Jython, IronPython, Pyrex, Psyco). Notă: la noi nu ştiu dacă e cazul, mă îndoiesc că un motor grafic în IronPython ar fi suficient de rapid.
  • Comparările cu None le facem întotdeauna folosind operatorii is şi is not, şi nu egalitatea.
  • Folosim excepţii care sunt clase. Excepţiile de tip şir de caractere vor fi scoase din limbaj începând cu versiunea 2.6. Este de preferat să definim o clasă din care vom deriva toate excepţiile noastre, care la rândul ei este derivată din Exception.
  • Folosim sintaxa raise ValueError("message"), şi nu raise ValueError, "message".
  • În general, evităm declaraţiile de tip except: fără parametri, pentru că pot trata şi excepţii ca SystemExit sau KeyboardInterrupt, ceea ce de obicei nu dorim.
  • Limităm blocurile try la cât mai puţin cod. Dacă scriem multe instrucţiuni în try, acestea pot ridica mai multe tipuri de excepţii decât anticipat iniţial.
  • Folosim metodele clasei String, şi nu pe cele ale modulului string. Primele sunt aproape întotdeauna mai rapide.
  • Folosim ''.startswith() şi ''.endswith() în loc de slicing, pentru că sunt, în general, mai puţin supuse la erori.
  • Folosim funcţia isinstance() în loc de operatorul is.
  • Folosim faptul că secvenţele (şiruri de caractere, liste, tupluri) goale sunt false.