Python is the main dynamic language used at Google. This style guide is a listofdos and don’ts for Python programs.
To help you format code correctly, we’ve created asettings file for Vim. For Emacs, the default settings should be fine.
Many teams use theBlack orPyinkauto-formatter to avoid arguing over formatting.
Runpylint
over your code using thispylintrc.
pylint
is a tool for finding bugs and style problems in Python source code. It findsproblems that are typically caught by a compiler for less dynamic languages likeC and C++. Because of the dynamic nature of Python, somewarnings may be incorrect; however, spurious warnings should be fairlyinfrequent.
Catches easy-to-miss errors like typos, using-vars-before-assignment, etc.
pylint
isn’t perfect. To take advantage of it, sometimes we’ll need to write around it,suppress its warnings or fix it.
Make sure you runpylint
on your code.
Suppress warnings if they are inappropriate so that other issues are not hidden.To suppress warnings, you can set a line-level comment:
defdo_PUT(self):# WSGI name, so pylint: disable=invalid-name...
pylint
warnings are each identified by symbolic name (empty-docstring
)Google-specific warnings start withg-
.
If the reason for the suppression is not clear from the symbolic name, add anexplanation.
Suppressing in this way has the advantage that we can easily search forsuppressions and revisit them.
You can get a list ofpylint
warnings by doing:
pylint--list-msgs
To get more information on a particular message, use:
pylint--help-msg=invalid-name
Preferpylint: disable
to the deprecated older formpylint: disable-msg
.
Unused argument warnings can be suppressed by deleting the variables at thebeginning of the function. Always include a comment explaining why you aredeleting it. “Unused.” is sufficient. For example:
defviking_cafe_order(spam:str,beans:str,eggs:str|None=None)->str:delbeans,eggs# Unused by vikings.returnspam+spam+spam
Other common forms of suppressing this warning include using ‘_
’ as theidentifier for the unused argument or prefixing the argument name with‘unused_
’, or assigning them to ‘_
’. These forms are allowed but no longerencouraged. These break callers that pass arguments by name and do not enforcethat the arguments are actually unused.
Useimport
statements for packages and modules only, not for individual types,classes, or functions.
Reusability mechanism for sharing code from one module to another.
The namespace management convention is simple. The source of each identifier isindicated in a consistent way;x.Obj
says that objectObj
is defined inmodulex
.
Module names can still collide. Some module names are inconveniently long.
import x
for importing packages and modules.from x import y
wherex
is the package prefix andy
is the modulename with no prefix.from x import y as z
in any of the following circumstances:y
are to be imported.y
conflicts with a top-level name defined in the current module.y
conflicts with a common parameter name that is part of the publicAPI (e.g.,features
).y
is an inconveniently long name.y
is too generic in the context of your code (e.g.,fromstorage.file_system import options as fs_options
).import y as z
only whenz
is a standard abbreviation (e.g.,importnumpy as np
).For example the modulesound.effects.echo
may be imported as follows:
fromsound.effectsimportecho...echo.EchoFilter(input,output,delay=0.7,atten=4)
Do not use relative names in imports. Even if the module is in the same package,use the full package name. This helps prevent unintentionally importing apackage twice.
Exemptions from this rule:
Import each module using the full pathname location of the module.
Avoids conflicts in module names or incorrect imports due to the module searchpath not being what the author expected. Makes it easier to find modules.
Makes it harder to deploy code because you have to replicate the packagehierarchy. Not really a problem with modern deployment mechanisms.
All new code should import each module by its full package name.
Imports should be as follows:
Yes:# Reference absl.flags in code with the complete name (verbose).importabsl.flagsfromdoctor.whoimportjodie_FOO=absl.flags.DEFINE_string(...)
Yes:# Reference flags in code with just the module name (common).fromabslimportflagsfromdoctor.whoimportjodie_FOO=flags.DEFINE_string(...)
(assume this file lives indoctor/who/
wherejodie.py
also exists)
No:# Unclear what module the author wanted and what will be imported. The actual# import behavior depends on external factors controlling sys.path.# Which possible jodie module did the author intend to import?importjodie
The directory the main binary is located in should not be assumed to be insys.path
despite that happening in some environments. This being the case,code should assume thatimport jodie
refers to a third-party or top-levelpackage namedjodie
, not a localjodie.py
.
Exceptions are allowed but must be used carefully.
Exceptions are a means of breaking out of normal control flow to handle errorsor other exceptional conditions.
The control flow of normal operation code is not cluttered by error-handlingcode. It also allows the control flow to skip multiple frames when a certaincondition occurs, e.g., returning from N nested functions in one step instead ofhaving to plumb error codes through.
May cause the control flow to be confusing. Easy to miss error cases when makinglibrary calls.
Exceptions must follow certain conditions:
Make use of built-in exception classes when it makes sense. For example,raise aValueError
to indicate a programming mistake like a violatedprecondition, such as may happen when validating function arguments.
Do not useassert
statements in place of conditionals or validatingpreconditions. They must not be critical to the application logic. A litmustest would be that theassert
could be removed without breaking the code.assert
conditionals arenot guaranteedto be evaluated. Forpytest based tests,assert
isokay and expected to verify expectations. Forexample:
Yes:defconnect_to_next_port(self,minimum:int)->int:"""Connects to the next available port. Args: minimum: A port value greater or equal to 1024. Returns: The new minimum port. Raises: ConnectionError: If no available port is found. """ifminimum<1024:# Note that this raising of ValueError is not mentioned in the doc# string's "Raises:" section because it is not appropriate to# guarantee this specific behavioral reaction to API misuse.raiseValueError(f'Min. port must be at least 1024, not{minimum}.')port=self._find_next_open_port(minimum)ifportisNone:raiseConnectionError(f'Could not connect to service on port{minimum} or higher.')# The code does not depend on the result of this assert.assertport>=minimum,(f'Unexpected port{port} when minimum was{minimum}.')returnport
No:defconnect_to_next_port(self,minimum:int)->int:"""Connects to the next available port. Args: minimum: A port value greater or equal to 1024. Returns: The new minimum port. """assertminimum>=1024,'Minimum port must be at least 1024.'# The following code depends on the previous assert.port=self._find_next_open_port(minimum)assertportisnotNone# The type checking of the return statement relies on the assert.returnport
Libraries or packages may define their own exceptions. When doing so theymust inherit from an existing exception class. Exception names should end inError
and should not introduce repetition (foo.FooError
).
Never use catch-allexcept:
statements, or catchException
orStandardError
, unless you are
Python is very tolerant in this regard andexcept:
will really catcheverything including misspelled names, sys.exit() calls, Ctrl+C interrupts,unittest failures and all kinds of other exceptions that you simply don’twant to catch.
Minimize the amount of code in atry
/except
block. The larger the bodyof thetry
, the more likely that an exception will be raised by a line ofcode that you didn’t expect to raise an exception. In those cases, thetry
/except
block hides a real error.
Use thefinally
clause to execute code whether or not an exception israised in thetry
block. This is often useful for cleanup, i.e., closing afile.
Avoid mutable global state.
Module-level values or class attributes that can get mutated during programexecution.
Occasionally useful.
Breaks encapsulation: Such design can make it hard to achieve validobjectives. For example, if global state is used to manage a databaseconnection, then connecting to two different databases at the same time(such as for computing differences during a migration) becomes difficult.Similar problems easily arise with global registries.
Has the potential to change module behavior during the import, becauseassignments to global variables are done when the module is first imported.
Avoid mutable global state.
In those rare cases where using global state is warranted, mutable globalentities should be declared at the module level or as a class attribute and madeinternal by prepending an_
to the name. If necessary, external access tomutable global state must be done through public functions or class methods. SeeNaming below. Please explain the design reasons why mutableglobal state is being used in a comment or a doc linked to from a comment.
Module-level constants are permitted and encouraged. For example:_MAX_HOLY_HANDGRENADE_COUNT = 3
for an internal use constant orSIR_LANCELOTS_FAVORITE_COLOR = "blue"
for a public API constant. Constantsmust be named using all caps with underscores. SeeNamingbelow.
Nested local functions or classes are fine when used to close over a localvariable. Inner classes are fine.
A class can be defined inside of a method, function, or class. A function can bedefined inside a method or function. Nested functions have read-only access tovariables defined in enclosing scopes.
Allows definition of utility classes and functions that are only used inside ofa very limited scope. VeryADT-y. Commonly used forimplementing decorators.
Nested functions and classes cannot be directly tested. Nesting can make theouter function longer and less readable.
They are fine with some caveats. Avoid nested functions or classes except whenclosing over a local value other thanself
orcls
. Do not nest a functionjust to hide it from users of a module. Instead, prefix its name with an _ atthe module level so that it can still be accessed by tests.
Okay to use for simple cases.
List, Dict, and Set comprehensions as well as generator expressions provide aconcise and efficient way to create container types and iterators withoutresorting to the use of traditional loops,map()
,filter()
, orlambda
.
Simple comprehensions can be clearer and simpler than other dict, list, or setcreation techniques. Generator expressions can be very efficient, since theyavoid the creation of a list entirely.
Complicated comprehensions or generator expressions can be hard to read.
Comprehensions are allowed, however multiplefor
clauses or filter expressionsare not permitted. Optimize for readability, not conciseness.
Yes:result=[mapping_exprforvalueiniterableiffilter_expr]result=[is_valid(metric={'key':value})forvalueininteresting_iterableifa_longer_filter_expression(value)]descriptive_name=[transform({'key':key,'value':value},color='black')forkey,valueingenerate_iterable(some_input)ifcomplicated_condition_is_met(key,value)]result=[]forxinrange(10):foryinrange(5):ifx*y>10:result.append((x,y))return{x:complicated_transform(x)forxinlong_generator_function(parameter)ifxisnotNone}return(x**2forxinrange(10))unique_names={user.nameforuserinusersifuserisnotNone}
No:result=[(x,y)forxinrange(10)foryinrange(5)ifx*y>10]return((x,y,z)forxinrange(5)foryinrange(5)ifx!=yforzinrange(5)ify!=z)
Use default iterators and operators for types that support them, like lists,dictionaries, and files.
Container types, like dictionaries and lists, define default iterators andmembership test operators (“in” and “not in”).
The default iterators and operators are simple and efficient. They express theoperation directly, without extra method calls. A function that uses defaultoperators is generic. It can be used with any type that supports the operation.
You can’t tell the type of objects by reading the method names (unless thevariable has type annotations). This is also an advantage.
Use default iterators and operators for types that support them, like lists,dictionaries, and files. The built-in types define iterator methods, too. Preferthese methods to methods that return lists, except that you should not mutate acontainer while iterating over it.
Yes:forkeyinadict:...ifobjinalist:...forlineinafile:...fork,vinadict.items():...
No:forkeyinadict.keys():...forlineinafile.readlines():...
Use generators as needed.
A generator function returns an iterator that yields a value each time itexecutes a yield statement. After it yields a value, the runtime state of thegenerator function is suspended until the next value is needed.
Simpler code, because the state of local variables and control flow arepreserved for each call. A generator uses less memory than a function thatcreates an entire list of values at once.
Local variables in the generator will not be garbage collected until thegenerator is either consumed to exhaustion or itself garbage collected.
Fine. Use “Yields:” rather than “Returns:” in the docstring for generatorfunctions.
If the generator manages an expensive resource, make sure to force the clean up.
A good way to do the clean up is by wrapping the generator with a contextmanagerPEP-0533.
Okay for one-liners. Prefer generator expressions overmap()
orfilter()
with alambda
.
Lambdas define anonymous functions in an expression, as opposed to a statement.
Convenient.
Harder to read and debug than local functions. The lack of names means stacktraces are more difficult to understand. Expressiveness is limited because thefunction may only contain an expression.
Lambdas are allowed. If the code inside the lambda function spans multiple linesor is longer than 60-80 chars, it might be better to define it as a regularnested function.
For common operations like multiplication, use the functions from theoperator
module instead of lambda functions. For example, preferoperator.mul
tolambda x, y: x * y
.
Okay for simple cases.
Conditional expressions (sometimes called a “ternary operator”) are mechanismsthat provide a shorter syntax for if statements. For example:x = 1 if condelse 2
.
Shorter and more convenient than an if statement.
May be harder to read than an if statement. The condition may be difficult tolocate if the expression is long.
Okay to use for simple cases. Each portion must fit on one line:true-expression, if-expression, else-expression. Use a complete if statementwhen things get more complicated.
Yes:one_line='yes'ifpredicate(value)else'no'slightly_split=('yes'ifpredicate(value)else'no, nein, nyet')the_longest_ternary_style_that_can_be_done=('yes, true, affirmative, confirmed, correct'ifpredicate(value)else'no, false, negative, nay')
No:bad_line_breaking=('yes'ifpredicate(value)else'no')portion_too_long=('yes'ifsome_long_module.some_long_predicate_function(really_long_variable_name)else'no, false, negative, nay')
Okay in most cases.
You can specify values for variables at the end of a function’s parameter list,e.g.,def foo(a, b=0):
. Iffoo
is called with only one argument,b
is setto 0. If it is called with two arguments,b
has the value of the secondargument.
Often you have a function that uses lots of default values, but on rareoccasions you want to override the defaults. Default argument values provide aneasy way to do this, without having to define lots of functions for the rareexceptions. As Python does not support overloaded methods/functions, defaultarguments are an easy way of “faking” the overloading behavior.
Default arguments are evaluated once at module load time. This may causeproblems if the argument is a mutable object such as a list or a dictionary. Ifthe function modifies the object (e.g., by appending an item to a list), thedefault value is modified.
Okay to use with the following caveat:
Do not use mutable objects as default values in the function or methoddefinition.
Yes:deffoo(a,b=None):ifbisNone:b=[]Yes:deffoo(a,b:Sequence|None=None):ifbisNone:b=[]Yes:deffoo(a,b:Sequence=()):# Empty tuple OK since tuples are immutable....
fromabslimportflags_FOO=flags.DEFINE_string(...)No:deffoo(a,b=[]):...No:deffoo(a,b=time.time()):# Is `b` supposed to represent when this module was loaded?...No:deffoo(a,b=_FOO.value):# sys.argv has not yet been parsed......No:deffoo(a,b:Mapping={}):# Could still get passed to unchecked code....
Properties may be used to control getting or setting attributes that requiretrivial computations or logic. Property implementations must match the generalexpectations of regular attribute access: that they are cheap, straightforward,and unsurprising.
A way to wrap method calls for getting and setting an attribute as a standardattribute access.
Properties are allowed, but, like operator overloading, should only be used whennecessary and match the expectations of typical attribute access; follow thegetters and setters rules otherwise.
For example, using a property to simply both get and set an internal attributeisn’t allowed: there is no computation occurring, so the property is unnecessary(make the attribute public instead). In comparison,using a property to control attribute access or to calculate atriviallyderived value is allowed: the logic is simple and unsurprising.
Properties should be created with the@property
decorator. Manually implementing aproperty descriptor is considered apower feature.
Inheritance with properties can be non-obvious. Do not use properties toimplement computations a subclass may ever want to override and extend.
Use the “implicit” false if at all possible (with a few caveats).
Python evaluates certain values asFalse
when in a boolean context. A quick“rule of thumb” is that all “empty” values are considered false, so0, None,[], {}, ''
all evaluate as false in a boolean context.
Conditions using Python booleans are easier to read and less error-prone. Inmost cases, they’re also faster.
May look strange to C/C++ developers.
Use the “implicit” false if possible, e.g.,if foo:
rather thanif foo !=[]:
. There are a few caveats that you should keep in mind though:
Always useif foo is None:
(oris not None
) to check for aNone
value.E.g., when testing whether a variable or argument that defaults toNone
was set to some other value. The other value might be a value that’s falsein a boolean context!
Never compare a boolean variable toFalse
using==
. Useif not x:
instead. If you need to distinguishFalse
fromNone
then chain theexpressions, such asif not x and x is not None:
.
For sequences (strings, lists, tuples), use the fact that empty sequencesare false, soif seq:
andif not seq:
are preferable toif len(seq):
andif not len(seq):
respectively.
When handling integers, implicit false may involve more risk than benefit(i.e., accidentally handlingNone
as 0). You may compare a value which isknown to be an integer (and is not the result oflen()
) against theinteger 0.
Yes:ifnotusers:print('no users')ifi%10==0:self.handle_multiple_of_ten()deff(x=None):ifxisNone:x=[]
No:iflen(users)==0:print('no users')ifnoti%10:self.handle_multiple_of_ten()deff(x=None):x=xor[]
Note that'0'
(i.e.,0
as string) evaluates to true.
Note that Numpy arrays may raise an exception in an implicit booleancontext. Prefer the.size
attribute when testing emptiness of anp.array
(e.g.if not users.size
).
Okay to use.
A nested Python function can refer to variables defined in enclosing functions,but cannot assign to them. Variable bindings are resolved using lexical scoping,that is, based on the static program text. Any assignment to a name in a blockwill cause Python to treat all references to that name as a local variable, evenif the use precedes the assignment. If a global declaration occurs, the name istreated as a global variable.
An example of the use of this feature is:
defget_adder(summand1:float)->Callable[[float],float]:"""Returns a function that adds numbers to a given number."""defadder(summand2:float)->float:returnsummand1+summand2returnadder
Often results in clearer, more elegant code. Especially comforting toexperienced Lisp and Scheme (and Haskell and ML and …) programmers.
Can lead to confusing bugs, such as this example based onPEP-0227:
i=4deffoo(x:Iterable[int]):defbar():print(i,end='')# ...# A bunch of code here# ...foriinx:# Ah, i *is* local to foo, so this is what bar seesprint(i,end='')bar()
Sofoo([1, 2, 3])
will print1 2 3 3
,not1 2 3 4
.
Okay to use.
Use decorators judiciously when there is a clear advantage. Avoidstaticmethod
and limit use ofclassmethod
.
Decorators for Functions and Methods(a.k.a “the@
notation”). One common decorator is@property
, used forconverting ordinary methods into dynamically computed attributes. However, thedecorator syntax allows for user-defined decorators as well. Specifically, forsome functionmy_decorator
, this:
classC:@my_decoratordefmethod(self):# method body ...
is equivalent to:
classC:defmethod(self):# method body ...method=my_decorator(method)
Elegantly specifies some transformation on a method; the transformation mighteliminate some repetitive code, enforce invariants, etc.
Decorators can perform arbitrary operations on a function’s arguments or returnvalues, resulting in surprising implicit behavior. Additionally, decoratorsexecute at object definition time. For module-level objects (classes, modulefunctions, …) this happens at import time. Failures in decorator code arepretty much impossible to recover from.
Use decorators judiciously when there is a clear advantage. Decorators shouldfollow the same import and naming guidelines as functions. A decorator docstringshould clearly state that the function is a decorator. Write unit tests fordecorators.
Avoid external dependencies in the decorator itself (e.g. don’t rely on files,sockets, database connections, etc.), since they might not be available when thedecorator runs (at import time, perhaps frompydoc
or other tools). Adecorator that is called with valid parameters should (as much as possible) beguaranteed to succeed in all cases.
Decorators are a special case of “top-level code” - seemain formore discussion.
Never usestaticmethod
unless forced to in order to integrate with an APIdefined in an existing library. Write a module-level function instead.
Useclassmethod
only when writing a named constructor, or a class-specificroutine that modifies necessary global state such as a process-wide cache.
Do not rely on the atomicity of built-in types.
While Python’s built-in data types such as dictionaries appear to have atomicoperations, there are corner cases where they aren’t atomic (e.g. if__hash__
or__eq__
are implemented as Python methods) and their atomicity should not berelied upon. Neither should you rely on atomic variable assignment (since thisin turn depends on dictionaries).
Use thequeue
module’sQueue
data type as the preferred way to communicatedata between threads. Otherwise, use thethreading
module and its lockingprimitives. Prefer condition variables andthreading.Condition
instead ofusing lower-level locks.
Avoid these features.
Python is an extremely flexible language and gives you many fancy features suchas custom metaclasses, access to bytecode, on-the-fly compilation, dynamicinheritance, object reparenting, import hacks, reflection (e.g. some uses ofgetattr()
), modification of system internals,__del__
methods implementingcustomized cleanup, etc.
These are powerful language features. They can make your code more compact.
It’s very tempting to use these “cool” features when they’re not absolutelynecessary. It’s harder to read, understand, and debug code that’s using unusualfeatures underneath. It doesn’t seem that way at first (to the original author),but when revisiting the code, it tends to be more difficult than code that islonger but is straightforward.
Avoid these features in your code.
Standard library modules and classes that internally use these features are okayto use (for example,abc.ABCMeta
,dataclasses
, andenum
).
New language version semantic changes may be gated behind a special futureimport to enable them on a per-file basis within earlier runtimes.
Being able to turn on some of the more modern features viafrom __future__import
statements allows early use of features from expected future Pythonversions.
This has proven to make runtime version upgrades smoother as changes can be madeon a per-file basis while declaring compatibility and preventing regressionswithin those files. Modern code is more maintainable as it is less likely toaccumulate technical debt that will be problematic during future runtimeupgrades.
Such code may not work on very old interpreter versions prior to theintroduction of the needed future statement. The need for this is more common inprojects supporting an extremely wide variety of environments.
Use offrom __future__ import
statements is encouraged. It allows a givensource file to start using more modern Python syntax features today. Once you nolonger need to run on a version where the features are hidden behind a__future__
import, feel free to remove those lines.
In code that may execute on versions as old as 3.5 rather than >= 3.7, import:
from__future__importgenerator_stop
For more information read thePython future statement definitionsdocumentation.
Please don’t remove these imports until you are confident the code is only everused in a sufficiently modern environment. Even if you do not currently use thefeature a specific future import enables in your code today, keeping it in placein the file prevents later modifications of the code from inadvertentlydepending on the older behavior.
Use otherfrom __future__
import statements as you see fit.
You can annotate Python code withtype hints. Type-check the codeat build time with a type checking tool likepytype.In most cases, when feasible, type annotations are in source files. Forthird-party or extension modules, annotations can be instub.pyi
files.
Type annotations (or “type hints”) are for function or method arguments andreturn values:
deffunc(a:int)->list[int]:
You can also declare the type of a variable using similar syntax:
a:SomeType=some_func()
Type annotations improve the readability and maintainability of your code. Thetype checker will convert many runtime errors to build-time errors, and reduceyour ability to usePower Features.
You will have to keep the type declarations up to date.You might see type errors that you think arevalid code. Use of atype checkermay reduce your ability to usePower Features.
You are strongly encouraged to enable Python type analysis when updating code.When adding or modifying public APIs, include type annotations and enablechecking via pytype in the build system. As static analysis is relatively new toPython, we acknowledge that undesired side-effects (such aswronglyinferred types) may prevent adoption by some projects. In those situations,authors are encouraged to add a comment with a TODO or link to a bug describingthe issue(s) currently preventing type annotation adoption in the BUILD file orin the code itself as appropriate.
Do not terminate your lines with semicolons, and do not use semicolons to puttwo statements on the same line.
Maximum line length is80 characters.
Explicit exceptions to the 80 character limit:
# pylint: disable=invalid-name
)Do not use a backslash forexplicit line continuation.
Instead, make use of Python’simplicit line joining inside parentheses, brackets and braces.If necessary, you can add an extra pair of parentheses around an expression.
Note that this rule doesn’t prohibit backslash-escaped newlines within strings(seebelow).
Yes:foo_bar(self,width,height,color='black',design=None,x='foo',emphasis=None,highlight=0)
Yes:if(width==0andheight==0andcolor=='red'andemphasis=='strong'):(bridge_questions.clarification_on.average_airspeed_of.unladen_swallow)='African or European?'with(very_long_first_expression_function()asspam,very_long_second_expression_function()asbeans,third_thing()aseggs,):place_order(eggs,beans,spam,beans)
No:ifwidth==0andheight==0and \color=='red'andemphasis=='strong':bridge_questions.clarification_on \.average_airspeed_of.unladen_swallow='African or European?'withvery_long_first_expression_function()asspam, \very_long_second_expression_function()asbeans, \third_thing()aseggs:place_order(eggs,beans,spam,beans)
When a literal string won’t fit on a single line, use parentheses for implicitline joining.
x=('This will build a very long long ''long long long long long long string')
Prefer to break lines at the highest possible syntactic level. If you must breaka line twice, break it at the same syntactic level both times.
Yes:bridgekeeper.answer(name="Arthur",quest=questlib.find(owner="Arthur",perilous=True))answer=(a_long_line().of_chained_methods().that_eventually_provides().an_answer())if(configisNoneor'editor.language'notinconfigorconfig['editor.language'].use_spacesisFalse):use_tabs()
No:bridgekeeper.answer(name="Arthur",quest=questlib.find(owner="Arthur",perilous=True))answer=a_long_line().of_chained_methods().that_eventually_provides().an_answer()if(configisNoneor'editor.language'notinconfigorconfig['editor.language'].use_spacesisFalse):use_tabs()
Within comments, put long URLs on their own line if necessary.
Yes:# See details at# http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html
No:# See details at# http://www.example.com/us/developer/documentation/api/content/\# v2.0/csv_file_name_extension_full_specification.html
Make note of the indentation of the elements in the line continuation examplesabove; see theindentation section for explanation.
Docstring summary lines must remain within the 80 characterlimit.
In all other cases where a line exceeds 80 characters, and theBlack orPyinkauto-formatter does not help bring the line below the limit, the line is allowedto exceed this maximum. Authors are encouraged to manually break the line up perthe notes above when it is sensible.
Use parentheses sparingly.
It is fine, though not required, to use parentheses around tuples. Do not usethem in return statements or conditional statements unless using parentheses forimplied line continuation or to indicate a tuple.
Yes:iffoo:bar()whilex:x=bar()ifxandy:bar()ifnotx:bar()# For a 1 item tuple the ()s are more visually obvious than the comma.onesie=(foo,)returnfooreturnspam,beansreturn(spam,beans)for(x,y)indict.items():...
No:if(x):bar()ifnot(x):bar()return(foo)
Indent your code blocks with4 spaces.
Never use tabs. Implied line continuation should align wrapped elementsvertically (seeline length examples), or use a hanging4-space indent. Closing (round, square or curly) brackets can be placed at theend of the expression, or on separate lines, but then should be indented thesame as the line with the corresponding opening bracket.
Yes:# Aligned with opening delimiter.foo=long_function_name(var_one,var_two,var_three,var_four)meal=(spam,beans)# Aligned with opening delimiter in a dictionary.foo={'long_dictionary_key':value1+value2,...}# 4-space hanging indent; nothing on first line.foo=long_function_name(var_one,var_two,var_three,var_four)meal=(spam,beans)# 4-space hanging indent; nothing on first line,# closing parenthesis on a new line.foo=long_function_name(var_one,var_two,var_three,var_four)meal=(spam,beans,)# 4-space hanging indent in a dictionary.foo={'long_dictionary_key':long_dictionary_value,...}
No:# Stuff on first line forbidden.foo=long_function_name(var_one,var_two,var_three,var_four)meal=(spam,beans)# 2-space hanging indent forbidden.foo=long_function_name(var_one,var_two,var_three,var_four)# No hanging indent in a dictionary.foo={'long_dictionary_key':long_dictionary_value,...}
Trailing commas in sequences of items are recommended only when the closingcontainer token]
,)
, or}
does not appear on the same line as the finalelement, as well as for tuples with a single element. The presence of a trailingcomma is also used as a hint to our Python code auto-formatterBlack orPyinkto direct it to auto-format the container of items to one item per line when the,
after the final element is present.
Yes:golomb3=[0,1,3]golomb4=[0,1,4,6,]
No:golomb4=[0,1,4,6,]
Two blank lines between top-level definitions, be they function or classdefinitions. One blank line between method definitions and between the docstringof aclass
and the first method. No blank line following adef
line. Usesingle blank lines as you judge appropriate within functions or methods.
Blank lines need not be anchored to the definition. For example, relatedcomments immediately preceding function, class, and method definitions can makesense. Consider if your comment might be more useful as part of the docstring.
Follow standard typographic rules for the use of spaces around punctuation.
No whitespace inside parentheses, brackets or braces.
Yes:spam(ham[1],{'eggs':2},[])
No:spam(ham[1],{'eggs':2},[])
No whitespace before a comma, semicolon, or colon. Do use whitespace after acomma, semicolon, or colon, except at the end of the line.
Yes:ifx==4:print(x,y)x,y=y,x
No:ifx==4:print(x,y)x,y=y,x
No whitespace before the open paren/bracket that starts an argument list,indexing or slicing.
Yes:spam(1)
No:spam(1)
Yes:dict['key']=list[index]
No:dict['key']=list[index]
No trailing whitespace.
Surround binary operators with a single space on either side for assignment(=
), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not
), andBooleans (and, or, not
). Use your better judgment for the insertion of spacesaround arithmetic operators (+
,-
,*
,/
,//
,%
,**
,@
).
Yes:x==1
No:x<1
Never use spaces around=
when passing keyword arguments or defining a defaultparameter value, with one exception:when a type annotation is present,do use spacesaround the=
for the default parameter value.
Yes:defcomplex(real,imag=0.0):returnMagic(r=real,i=imag)Yes:defcomplex(real,imag:float=0.0):returnMagic(r=real,i=imag)
No:defcomplex(real,imag=0.0):returnMagic(r=real,i=imag)No:defcomplex(real,imag:float=0.0):returnMagic(r=real,i=imag)
Don’t use spaces to vertically align tokens on consecutive lines, since itbecomes a maintenance burden (applies to:
,#
,=
, etc.):
Yes:foo=1000# commentlong_name=2# comment that should not be aligneddictionary={'foo':1,'long_name':2,}
No:foo=1000# commentlong_name=2# comment that should not be aligneddictionary={'foo':1,'long_name':2,}
Most.py
files do not need to start with a#!
line. Start the main file of aprogram with#!/usr/bin/env python3
(to support virtualenvs) or#!/usr/bin/python3
perPEP-394.
This line is used by the kernel to find the Python interpreter, but is ignored by Python when importing modules. It is only necessary on a file intended to be executed directly.
Be sure to use the right style for module, function, method docstrings andinline comments.
Python usesdocstrings to document code. A docstring is a string that is thefirst statement in a package, module, class or function. These strings can beextracted automatically through the__doc__
member of the object and are usedbypydoc
.(Try runningpydoc
on your module to see how it looks.) Always use thethree-double-quote"""
format for docstrings (perPEP 257). A docstring should be organizedas a summary line (one physical line not exceeding 80 characters) terminated bya period, question mark, or exclamation point. When writing more (encouraged),this must be followed by a blank line, followed by the rest of the docstringstarting at the same cursor position as the first quote of the first line. Thereare more formatting guidelines for docstrings below.
Every file should contain license boilerplate. Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL).
Files should start with a docstring describing the contents and usage of themodule.
"""A one-line summary of the module or program, terminated by a period.Leave one blank line. The rest of this docstring should contain anoverall description of the module or program. Optionally, it may alsocontain a brief description of exported classes and functions and/or usageexamples.Typical usage example: foo = ClassFoo() bar = foo.function_bar()"""
Module-level docstrings for test files are not required. They should be includedonly when there is additional information that can be provided.
Examples include some specifics on how the test should be run, an explanation ofan unusual setup pattern, dependency on the external environment, and so on.
"""This blaze test uses golden files.You can update those files by running`blaze run //foo/bar:foo_test -- --update_golden_files` from the `google3`directory."""
Docstrings that do not provide any new information should not be used.
"""Tests for foo.bar."""
In this section, “function” means a method, function, generator, or property.
A docstring is mandatory for every function that has one or more of thefollowing properties:
A docstring should give enough information to write a call to the functionwithout reading the function’s code. The docstring should describe thefunction’s calling syntax and its semantics, but generally not itsimplementation details, unless those details are relevant to how the function isto be used. For example, a function that mutates one of its arguments as a sideeffect should note that in its docstring. Otherwise, subtle but importantdetails of a function’s implementation that are not relevant to the caller arebetter expressed as comments alongside the code than within the function’sdocstring.
The docstring may be descriptive-style ("""Fetches rows from a Bigtable."""
)or imperative-style ("""Fetch rows from a Bigtable."""
), but the style shouldbe consistent within a file. The docstring for a@property
data descriptorshould use the same style as the docstring for an attribute or afunction argument ("""The Bigtable path."""
,rather than"""Returns the Bigtable path."""
).
Certain aspects of a function should be documented in special sections, listedbelow. Each section begins with a heading line, which ends with a colon. Allsections other than the heading should maintain a hanging indent of two or fourspaces (be consistent within a file). These sections can be omitted in caseswhere the function’s name and signature are informative enough that it can beaptly described using a one-line docstring.
*foo
(variable length argument lists) and/or**bar
(arbitrary keyword arguments), they should be listed as*foo
and**bar
."""Returns rowfrom Bigtable as a tuple of strings."""
)and the opening sentence issufficient to describe the return value. Do not imitate older ‘NumPy style’(example),which frequently documented a tuple return value as if it were multiplereturn values with individual names (never mentioning the tuple). Instead,describe such a return value as: “Returns: A tuple (mat_a, mat_b), wheremat_a is …, and …”. The auxiliary names in the docstring need notnecessarily correspond to any internal names used in the function body (asthose are not part of the API). If the function usesyield
(is agenerator), theYields:
section should document the object returned bynext()
, instead of the generator object itself that the call evaluates to.deffetch_smalltable_rows(table_handle:smalltable.Table,keys:Sequence[bytes|str],require_all_keys:bool=False,)->Mapping[bytes,tuple[str,...]]:"""Fetches rows from a Smalltable. Retrieves rows pertaining to the given keys from the Table instance represented by table_handle. String keys will be UTF-8 encoded. Args: table_handle: An open smalltable.Table instance. keys: A sequence of strings representing the key of each table row to fetch. String keys will be UTF-8 encoded. require_all_keys: If True only rows with values set for all keys will be returned. Returns: A dict mapping keys to the corresponding table row data fetched. Each row is represented as a tuple of strings. For example: {b'Serak': ('Rigel VII', 'Preparer'), b'Zim': ('Irk', 'Invader'), b'Lrrr': ('Omicron Persei 8', 'Emperor')} Returned keys are always bytes. If a key from the keys argument is missing from the dictionary, then that row was not found in the table (and require_all_keys must have been False). Raises: IOError: An error occurred accessing the smalltable. """
Similarly, this variation onArgs:
with a line break is also allowed:
deffetch_smalltable_rows(table_handle:smalltable.Table,keys:Sequence[bytes|str],require_all_keys:bool=False,)->Mapping[bytes,tuple[str,...]]:"""Fetches rows from a Smalltable. Retrieves rows pertaining to the given keys from the Table instance represented by table_handle. String keys will be UTF-8 encoded. Args: table_handle: An open smalltable.Table instance. keys: A sequence of strings representing the key of each table row to fetch. String keys will be UTF-8 encoded. require_all_keys: If True only rows with values set for all keys will be returned. Returns: A dict mapping keys to the corresponding table row data fetched. Each row is represented as a tuple of strings. For example: {b'Serak': ('Rigel VII', 'Preparer'), b'Zim': ('Irk', 'Invader'), b'Lrrr': ('Omicron Persei 8', 'Emperor')} Returned keys are always bytes. If a key from the keys argument is missing from the dictionary, then that row was not found in the table (and require_all_keys must have been False). Raises: IOError: An error occurred accessing the smalltable. """
A method that overrides a method from a base class does not need a docstring ifit is explicitly decorated with@override
(fromtyping_extensions
ortyping
modules), unless the overriding method’sbehavior materially refines the base method’s contract, or details need to beprovided (e.g., documenting additional side effects), in which case a docstringwith at least those differences is required on the overriding method.
fromtyping_extensionsimportoverrideclassParent:defdo_something(self):"""Parent method, includes docstring."""# Child class, method annotated with override.classChild(Parent):@overridedefdo_something(self):pass
# Child class, but without @override decorator, a docstring is required.classChild(Parent):defdo_something(self):pass# Docstring is trivial, @override is sufficient to indicate that docs can be# found in the base class.classChild(Parent):@overridedefdo_something(self):"""See base class."""
Classes should have a docstring below the class definition describing the class.Public attributes, excludingproperties, should be documentedhere in anAttributes
section and follow the same formatting as afunction’sArgs
section.
classSampleClass:"""Summary of class here. Longer class information... Longer class information... Attributes: likes_spam: A boolean indicating if we like SPAM or not. eggs: An integer count of the eggs we have laid. """def__init__(self,likes_spam:bool=False):"""Initializes the instance based on spam preference. Args: likes_spam: Defines if instance exhibits this preference. """self.likes_spam=likes_spamself.eggs=0@propertydefbutter_sticks(self)->int:"""The number of butter sticks we have."""
All class docstrings should start with a one-line summary that describes whatthe class instance represents. This implies that subclasses ofException
should also describe what the exception represents, and not the context in whichit might occur. The class docstring should not repeat unnecessary information,such as that the class is a class.
# Yes:classCheeseShopAddress:"""The address of a cheese shop. ... """classOutOfCheeseError(Exception):"""No more cheese is available."""
# No:classCheeseShopAddress:"""Class that describes the address of a cheese shop. ... """classOutOfCheeseError(Exception):"""Raised when no more cheese is available."""
The final place to have comments is in tricky parts of the code. If you’re goingto have to explain it at the nextcode review,you should comment it now. Complicated operations get a few lines of commentsbefore the operations commence. Non-obvious ones get comments at the end of theline.
# We use a weighted dictionary search to find out where i is in# the array. We extrapolate position based on the largest num# in the array and the array size and then do binary search to# get the exact number.ifi&(i-1)==0:# True if i is 0 or a power of 2.
To improve legibility, these comments should start at least 2 spaces away fromthe code with the comment character#
, followed by at least one space beforethe text of the comment itself.
On the other hand, never describe the code. Assume the person reading the codeknows Python (though not what you’re trying to do) better than you do.
# BAD COMMENT: Now go through the b array and make sure whenever i occurs# the next element is i+1
Pay attention to punctuation, spelling, and grammar; it is easier to readwell-written comments than badly written ones.
Comments should be as readable as narrative text, with proper capitalization andpunctuation. In many cases, complete sentences are more readable than sentencefragments. Shorter comments, such as comments at the end of a line of code, cansometimes be less formal, but you should be consistent with your style.
Although it can be frustrating to have a code reviewer point out that you areusing a comma when you should be using a semicolon, it is very important thatsource code maintain a high level of clarity and readability. Properpunctuation, spelling, and grammar help with that goal.
Use anf-string,the%
operator, or theformat
method for formatting strings, even when theparameters are all strings. Use your best judgment to decide between stringformatting options. A single join with+
is okay but do not format with+
.
Yes:x=f'name:{name}; score:{n}'x='%s, %s!'%(imperative,expletive)x='{}, {}'.format(first,second)x='name: %s; score: %d'%(name,n)x='name: %(name)s; score: %(score)d'%{'name':name,'score':n}x='name: {}; score: {}'.format(name,n)x=a+b
No:x=first+', '+secondx='name: '+name+'; score: '+str(n)
Avoid using the+
and+=
operators to accumulate a string within a loop. Insome conditions, accumulating a string with addition can lead to quadraticrather than linear running time. Although common accumulations of this sort maybe optimized on CPython, that is an implementation detail. The conditions underwhich an optimization applies are not easy to predict and may change. Instead,add each substring to a list and''.join
the list after the loop terminates,or write each substring to anio.StringIO
buffer. These techniquesconsistently have amortized-linear run-time complexity.
Yes:items=['<table>']forlast_name,first_nameinemployee_list:items.append('<tr><td>%s, %s</td></tr>'%(last_name,first_name))items.append('</table>')employee_table=''.join(items)
No:employee_table='<table>'forlast_name,first_nameinemployee_list:employee_table+='<tr><td>%s, %s</td></tr>'%(last_name,first_name)employee_table+='</table>'
Be consistent with your choice of string quote character within a file. Pick'
or"
and stick with it. It is okay to use the other quote character on astring to avoid the need to backslash-escape quote characters within the string.
Yes:Python('Why are you hiding your eyes?')Gollum("I'm scared of lint errors.")Narrator('"Good!" thought a happy Python reviewer.')
No:Python("Why are you hiding your eyes?")Gollum('The lint. It burns. It burns us.')Gollum("Always the great lint. Watching. Watching.")
Prefer"""
for multi-line strings rather than'''
. Projects may choose touse'''
for all non-docstring multi-line strings if and only if they also use'
for regular strings. Docstrings must use"""
regardless.
Multi-line strings do not flow with the indentation of the rest of the program.If you need to avoid embedding extra space in the string, use eitherconcatenated single-line strings or a multi-line string withtextwrap.dedent()
to remove the initial space on each line:
No:long_string="""This is pretty ugly.Don't do this."""
Yes:long_string="""This is fine if your use case can accept extraneous leading spaces."""
Yes:long_string=("And this is fine if you cannot accept\n"+"extraneous leading spaces.")
Yes:long_string=("And this too is fine if you cannot accept\n""extraneous leading spaces.")
Yes:importtextwraplong_string=textwrap.dedent("""\ This is also fine, because textwrap.dedent() will collapse common leading spaces in each line.""")
Note that using a backslash here does not violate the prohibition againstexplicit line continuation; in this case, the backslash isescaping a newlinein a string literal.
For logging functions that expect a pattern-string (with %-placeholders) astheir first argument: Always call them with a string literal (not an f-string!)as their first argument with pattern-parameters as subsequent arguments. Somelogging implementations collect the unexpanded pattern-string as a queryablefield. It also prevents spending time rendering a message that no logger isconfigured to output.
Yes:importtensorflowastflogger=tf.get_logger()logger.info('TensorFlow Version is: %s',tf.__version__)
Yes:importosfromabslimportlogginglogging.info('Current $PAGER is: %s',os.getenv('PAGER',default=''))homedir=os.getenv('HOME')ifhomedirisNoneornotos.access(homedir,os.W_OK):logging.error('Cannot write to home directory, $HOME=%r',homedir)
No:importosfromabslimportlogginglogging.info('Current $PAGER is:')logging.info(os.getenv('PAGER',default=''))homedir=os.getenv('HOME')ifhomedirisNoneornotos.access(homedir,os.W_OK):logging.error(f'Cannot write to home directory, $HOME={homedir!r}')
Error messages (such as: message strings on exceptions likeValueError
, ormessages shown to the user) should follow three guidelines:
The message needs to precisely match the actual error condition.
Interpolated pieces need to always be clearly identifiable as such.
They should allow simple automated processing (e.g. grepping).
Yes:ifnot0<=p<=1:raiseValueError(f'Not a probability:{p=}')try:os.rmdir(workdir)exceptOSErroraserror:logging.warning('Could not remove directory (reason: %r): %r',error,workdir)
No:ifp<0orp>1:# PROBLEM: also false for float('nan')!raiseValueError(f'Not a probability:{p=}')try:os.rmdir(workdir)exceptOSError:# PROBLEM: Message makes an assumption that might not be true:# Deletion might have failed for some other reason, misleading# whoever has to debug this.logging.warning('Directory already was deleted: %s',workdir)try:os.rmdir(workdir)exceptOSError:# PROBLEM: The message is harder to grep for than necessary, and# not universally non-confusing for all possible values of `workdir`.# Imagine someone calling a library function with such code# using a name such as workdir = 'deleted'. The warning would read:# "The deleted directory could not be deleted."logging.warning('The %s directory could not be deleted.',workdir)
Explicitly close files and sockets when done with them. This rule naturallyextends to closeable resources that internally use sockets, such as databaseconnections, and also other resources that need to be closed down in a similarfashion. To name only a few examples, this also includesmmap mappings,h5py File objects, andmatplotlib.pyplot figure windows.
Leaving files, sockets or other such stateful objects open unnecessarily hasmany downsides:
Furthermore, while files and sockets (and some similarly behaving resources) areautomatically closed when the object is destructed, coupling the lifetime of theobject to the state of the resource is poor practice:
__del__
method. Different Python implementations use different memorymanagement techniques, such as delayed garbage collection, which mayincrease the object’s lifetime arbitrarily and indefinitely.Relying on finalizers to do automatic cleanup that has observable side effectshas been rediscovered over and over again to lead to major problems, across manydecades and multiple languages (see e.g.this articlefor Java).
The preferred way to manage files and similar resources is using thewith
statement:
withopen("hello.txt")ashello_file:forlineinhello_file:print(line)
For file-like objects that do not support thewith
statement, usecontextlib.closing()
:
importcontextlibwithcontextlib.closing(urllib.urlopen("http://www.python.org/"))asfront_page:forlineinfront_page:print(line)
In rare cases where context-based resource management is infeasible, codedocumentation must explain clearly how resource lifetime is managed.
UseTODO
comments for code that is temporary, a short-term solution, orgood-enough but not perfect.
ATODO
comment begins with the wordTODO
in all caps, a following colon, anda link to a resource that contains the context, ideally a bug reference. A bugreference is preferable because bugs are tracked and have follow-up comments.Follow this piece of context with an explanatory string introduced with a hyphen-
. The purpose is to have a consistentTODO
format that can be searched to findout how to get more details.
# TODO: crbug.com/192795 - Investigate cpufreq optimizations.
Old style, formerly recommended, but discouraged for use in new code:
# TODO(crbug.com/192795): Investigate cpufreq optimizations.# TODO(yourusername): Use a "\*" here for concatenation operator.
Avoid adding TODOs that refer to an individual or team as the context:
# TODO: @yourusername - File an issue and use a '*' for repetition.
If yourTODO
is of the form “At a future date do something” make sure that youeither include a very specific date (“Fix by November 2009”) or a very specificevent (“Remove this code when all clients can handle XML responses.”) thatfuture code maintainers will comprehend. Issues are ideal for tracking this.
Imports should be on separate lines; there areexceptions fortyping
andcollections.abc
imports.
E.g.:
Yes:fromcollections.abcimportMapping,SequenceimportosimportsysfromtypingimportAny,NewType
No:importos,sys
Imports are always put at the top of the file, just after any module commentsand docstrings and before module globals and constants. Imports should begrouped from most generic to least generic:
Python future import statements. For example:
from__future__importannotations
Seeabove for more information about those.
Python standard library imports. For example:
importsys
third-party moduleor package imports. For example:
importtensorflowastf
Code repositorysub-package imports. For example:
fromotherproject.aiimportmind
Deprecated: application-specific imports that are part of the sametop-levelsub-package as this file. For example:
frommyproject.backend.hgwellsimporttime_machine
You may find older Google Python Style code doing this, but it is no longerrequired.New code is encouraged not to bother with this. Simply treatapplication-specific sub-package imports the same as other sub-packageimports.
Within each grouping, imports should be sorted lexicographically, ignoring case,according to each module’s full package path (thepath
infrom path import...
). Code may optionally place a blank line between import sections.
importcollectionsimportqueueimportsysfromabslimportappfromabslimportflagsimportbs4importcryptographyimporttensorflowastffrombook.genresimportscififrommyproject.backendimporthuxleyfrommyproject.backend.hgwellsimporttime_machinefrommyproject.backend.state_machineimportmain_loopfromotherproject.aiimportbodyfromotherproject.aiimportmindfromotherproject.aiimportsoul# Older style code may have these imports down here instead:#from myproject.backend.hgwells import time_machine#from myproject.backend.state_machine import main_loop
Generally only one statement per line.
However, you may put the result of a test on the same line as the test only ifthe entire statement fits on one line. In particular, you can never do so withtry
/except
since thetry
andexcept
can’t both fit on the same line, andyou can only do so with anif
if there is noelse
.
Yes:iffoo:bar(foo)
No:iffoo:bar(foo)else:baz(foo)try:bar(foo)exceptValueError:baz(foo)try:bar(foo)exceptValueError:baz(foo)
Getter and setter functions (also called accessors and mutators) should be usedwhen they provide a meaningful role or behavior for getting or setting avariable’s value.
In particular, they should be used when getting or setting the variable iscomplex or the cost is significant, either currently or in a reasonable future.
If, for example, a pair of getters/setters simply read and write an internalattribute, the internal attribute should be made public instead. By comparison,if setting a variable means some state is invalidated or rebuilt, it should be asetter function. The function invocation hints that a potentially non-trivialoperation is occurring. Alternatively,properties may be anoption when simple logic is needed, or refactoring to no longer need getters andsetters.
Getters and setters should follow theNaming guidelines, suchasget_foo()
andset_foo()
.
If the past behavior allowed access through a property, do not bind the newgetter/setter functions to the property. Any code still attempting to access thevariable by the old method should break visibly so they are made aware of thechange in complexity.
module_name
,package_name
,ClassName
,method_name
,ExceptionName
,function_name
,GLOBAL_CONSTANT_NAME
,global_var_name
,instance_var_name
,function_parameter_name
,local_var_name
,query_proper_noun_for_thing
,send_acronym_via_https
.
Names should be descriptive. This includes functions, classes, variables,attributes, files and any other type of named entities.
Avoid abbreviation. In particular, do not use abbreviations that are ambiguousor unfamiliar to readers outside your project, and do not abbreviate by deletingletters within a word.
Always use a.py
filename extension. Never use dashes.
single character names, except for specifically allowed cases:
i
,j
,k
,v
, et al.)e
as an exception identifier intry/except
statements.f
as a file handle inwith
statements_T = TypeVar("_T")
,_P = ParamSpec("_P")
)Please be mindful not to abuse single-character naming. Generally speaking,descriptiveness should be proportional to the name’s scope of visibility.For example,i
might be a fine name for 5-line code block but withinmultiple nested scopes, it is likely too vague.
dashes (-
) in any package/module name
__double_leading_and_trailing_underscore__
names (reserved by Python)
offensive terms
names that needlessly include the type of the variable (for example:id_to_name_dict
)
“Internal” means internal to a module, or protected or private within aclass.
Prepending a single underscore (_
) has some support for protecting modulevariables and functions (linters will flag protected member access). Notethat it is okay for unit tests to access protected constants from themodules under test.
Prepending a double underscore (__
aka “dunder”) to an instance variableor method effectively makes the variable or method private to its class(using name mangling); we discourage its use as it impacts readability andtestability, and isn’treally private. Prefer a single underscore.
Place related classes and top-level functions together in amodule.Unlike Java, there is no need to limit yourself to one class per module.
Use CapWords for class names, but lower_with_under.py for module names.Although there are some old modules named CapWords.py, this is nowdiscouraged because it’s confusing when the module happens to be named aftera class. (“wait – did I writeimport StringIO
orfrom StringIO importStringIO
?”)
Newunit test files follow PEP 8 compliant lower_with_under methodnames, for example,test_<method_under_test>_<state>
. For consistency(*)with legacy modules that follow CapWords function names, underscores mayappear in method names starting withtest
to separate logical componentsof the name. One possible pattern istest<MethodUnderTest>_<state>
.
Python filenames must have a.py
extension and must not contain dashes (-
).This allows them to be imported and unittested. If you want an executable to beaccessible without the extension, use a symbolic link or a simple bash wrappercontainingexec "$0.py" "$@"
.
Type | Public | Internal |
---|---|---|
Packages | lower_with_under | |
Modules | lower_with_under | _lower_with_under |
Classes | CapWords | _CapWords |
Exceptions | CapWords | |
Functions | lower_with_under() | _lower_with_under() |
Global/Class Constants | CAPS_WITH_UNDER | _CAPS_WITH_UNDER |
Global/Class Variables | lower_with_under | _lower_with_under |
Instance Variables | lower_with_under | _lower_with_under (protected) |
Method Names | lower_with_under() | _lower_with_under() (protected) |
Function/Method Parameters | lower_with_under | |
Local Variables | lower_with_under |
For mathematically-heavy code, short variable names that would otherwise violatethe style guide are preferred when they match established notation in areference paper or algorithm.
When using names based on established notation:
descriptive_names
for public APIs, which are muchmore likely to be encountered out of context.pylint: disable=invalid-name
directive to silencewarnings. For just a few variables, use the directive as an endline commentfor each one; for more, apply the directive at the beginning of a block.In Python,pydoc
as well as unit tests require modules to be importable. If afile is meant to be used as an executable, its main functionality should be in amain()
function, and your code should always checkif __name__ == '__main__'
before executing your main program, so that it is not executed when the moduleis imported.
When usingabsl, useapp.run
:
fromabslimportapp...defmain(argv:Sequence[str]):# process non-flag arguments...if__name__=='__main__':app.run(main)
Otherwise, use:
defmain():...if__name__=='__main__':main()
All code at the top level will be executed when the module is imported. Becareful not to call functions, create objects, or perform other operations thatshould not be executed when the file is beingpydoc
ed.
Prefer small and focused functions.
We recognize that long functions are sometimes appropriate, so no hard limit isplaced on function length. If a function exceeds about 40 lines, think aboutwhether it can be broken up without harming the structure of the program.
Even if your long function works perfectly now, someone modifying it in a fewmonths may add new behavior. This could result in bugs that are hard to find.Keeping your functions short and simple makes it easier for other people to readand modify your code.
You could find long and complicated functions when working withsomecode. Do not be intimidated by modifying existing code: if working with such afunction proves to be difficult, you find that errors are hard to debug, or youwant to use a piece of it in several different contexts, consider breaking upthe function into smaller and more manageable pieces.
Familiarize yourself withtype hints.
Annotatingself
orcls
is generally not necessary.Self
can beused if it is necessary for proper type information, e.g.
fromtypingimportSelfclassBaseClass:@classmethoddefcreate(cls)->Self:...defdifference(self,other:Self)->float:...
Similarly, don’t feel compelled to annotate the return value of__init__
(whereNone
is the only valid option).
If any other variable or a returned type should not be expressed, useAny
.
You are not required to annotate all the functions in a module.
Try to follow the existingindentation rules.
After annotating, many function signatures will become “one parameter per line”.To ensure the return type is also given its own line, a comma can be placedafter the last parameter.
defmy_method(self,first_var:int,second_var:Foo,third_var:Bar|None,)->int:...
Always prefer breaking between variables, and not, for example, between variablenames and type annotations. However, if everything fits on the same line, go forit.
defmy_method(self,first_var:int)->int:...
If the combination of the function name, the last parameter, and the return typeis too long, indent by 4 in a new line. When using line breaks, prefer puttingeach parameter and the return type on their own lines and aligning the closingparenthesis with thedef
:
Yes:defmy_method(self,other_arg:MyLongType|None,)->tuple[MyLongType1,MyLongType1]:...
Optionally, the return type may be put on the same line as the last parameter:
Okay:defmy_method(self,first_var:int,second_var:int)->dict[OtherLongType,MyLongType]:...
pylint
allows you to move the closing parenthesis to a new line and align with theopening one, but this is less readable.
No:defmy_method(self,other_arg:MyLongType|None,)->dict[OtherLongType,MyLongType]:...
As in the examples above, prefer not to break types. However, sometimes they aretoo long to be on a single line (try to keep sub-types unbroken).
defmy_method(self,first_var:tuple[list[MyLongType1],list[MyLongType2]],second_var:list[dict[MyLongType3,MyLongType4]],)->None:...
If a single name and type is too long, consider using analias for the type. The last resort is to break after thecolon and indent by 4.
Yes:defmy_function(long_variable_name:long_module_name.LongTypeName,)->None:...
No:defmy_function(long_variable_name:long_module_name.LongTypeName,)->None:...
If you need to use a class name (from the same module) that is not yetdefined – for example, if you need the class name inside the declaration ofthat class, or if you use a class that is defined later in the code – eitherusefrom __future__ import annotations
or use a string for the class name.
Yes:from__future__importannotationsclassMyClass:def__init__(self,stack:Sequence[MyClass],item:OtherClass)->None:classOtherClass:...
Yes:classMyClass:def__init__(self,stack:Sequence['MyClass'],item:'OtherClass')->None:classOtherClass:...
As perPEP-008, usespaces around the=
only for arguments that have both a type annotation anda default value.
Yes:deffunc(a:int=0)->int:...
No:deffunc(a:int=0)->int:...
In the Python type system,NoneType
is a “first class” type, and for typingpurposes,None
is an alias forNoneType
. If an argument can beNone
, ithas to be declared! You can use|
union type expressions (recommended in newPython 3.10+ code), or the olderOptional
andUnion
syntaxes.
Use explicitX | None
instead of implicit. Earlier versions of type checkersalloweda: str = None
to be interpreted asa: str | None = None
, but that isno longer the preferred behavior.
Yes:defmodern_or_union(a:str|int|None,b:str|None=None)->str:...defunion_optional(a:Union[str,int,None],b:Optional[str]=None)->str:...
No:defnullable_union(a:Union[None,str])->str:...defimplicit_optional(a:str=None)->str:...
You can declare aliases of complex types. The name of an alias should beCapWorded. If the alias is used only in this module, it should be _Private.
Note that the: TypeAlias
annotation is only supported in versions 3.10+.
fromtypingimportTypeAlias_LossAndGradient:TypeAlias=tuple[tf.Tensor,tf.Tensor]ComplexTFMap:TypeAlias=Mapping[str,_LossAndGradient]
You can disable type checking on a line with the special comment# type:ignore
.
pytype
has a disable option for specific errors (similar to lint):
# pytype: disable=attribute-error
a:Foo=SomeUndecoratedFunction()
# type: <type name>
comment on the end of the line:a=SomeUndecoratedFunction()# type: Foo
Typed lists can only contain objects of a single type. Typed tuples can eitherhave a single repeated type or a set number of elements with different types.The latter is commonly used as the return type from a function.
a:list[int]=[1,2,3]b:tuple[int,...]=(1,2,3)c:tuple[int,str,float]=(1,"2",3.5)
The Python type system hasgenerics. A typevariable, such asTypeVar
andParamSpec
, is a common way to use them.
Example:
fromcollections.abcimportCallablefromtypingimportParamSpec,TypeVar_P=ParamSpec("_P")_T=TypeVar("_T")...defnext(l:list[_T])->_T:returnl.pop()defprint_when_called(f:Callable[_P,_T])->Callable[_P,_T]:definner(*args:_P.args,**kwargs:_P.kwargs)->_T:print("Function was called")returnf(*args,**kwargs)returninner
ATypeVar
can be constrained:
AddableType=TypeVar("AddableType",int,float,str)defadd(a:AddableType,b:AddableType)->AddableType:returna+b
A common predefined type variable in thetyping
module isAnyStr
. Use it formultiple annotations that can bebytes
orstr
and must all be the same type.
fromtypingimportAnyStrdefcheck_length(x:AnyStr)->AnyStr:iflen(x)<=42:returnxraiseValueError()
A type variable must have a descriptive name, unless it meets all of thefollowing criteria:
Yes:_T=TypeVar("_T")_P=ParamSpec("_P")AddableType=TypeVar("AddableType",int,float,str)AnyFunction=TypeVar("AnyFunction",bound=Callable)
No:T=TypeVar("T")P=ParamSpec("P")_T=TypeVar("_T",int,float,str)_F=TypeVar("_F",bound=Callable)
Do not use
typing.Text
in new code. It’s only for Python 2/3 compatibility.
Usestr
for string/text data. For code that deals with binary data, usebytes
.
defdeals_with_text_data(x:str)->str:...defdeals_with_binary_data(x:bytes)->bytes:...
If all the string types of a function are always the same, for example if thereturn type is the same as the argument type in the code above, useAnyStr.
For symbols (including types, functions, and constants) from thetyping
orcollections.abc
modules used to support static analysis and type checking,always import the symbol itself. This keeps common annotations more concise andmatches typing practices used around the world. You are explicitly allowed toimport multiple specific symbols on one line from thetyping
andcollections.abc
modules. For example:
fromcollections.abcimportMapping,SequencefromtypingimportAny,Generic,cast,TYPE_CHECKING
Given that this way of importing adds items to the local namespace, names intyping
orcollections.abc
should be treated similarly to keywords, and notbe defined in your Python code, typed or not. If there is a collision between atype and an existing name in a module, import it usingimport x as y
.
fromtypingimportAnyasAnyType
When annotating function signatures, prefer abstract container types likecollections.abc.Sequence
over concrete types likelist
. If you need to use aconcrete type (for example, atuple
of typed elements), prefer built-in typesliketuple
over the parametric type aliases from thetyping
module (e.g.,typing.Tuple
).
fromtypingimportList,Tupledeftransform_coordinates(original:List[Tuple[float,float]])->List[Tuple[float,float]]:...
fromcollections.abcimportSequencedeftransform_coordinates(original:Sequence[tuple[float,float]])->Sequence[tuple[float,float]]:...
Use conditional imports only in exceptional cases where the additional importsneeded for type checking must be avoided at runtime. This pattern isdiscouraged; alternatives such as refactoring the code to allow top-levelimports should be preferred.
Imports that are needed only for type annotations can be placed within anifTYPE_CHECKING:
block.
importtypingiftyping.TYPE_CHECKING:importsketchdeff(x:"sketch.Sketch"):...
Circular dependencies that are caused by typing are code smells. Such code is agood candidate for refactoring. Although technically it is possible to keepcircular dependencies, various build systems will not let you do sobecause each module has to depend on the other.
Replace modules that create circular dependency imports withAny
. Set analias with a meaningful name, and use the real type name fromthis module (any attribute ofAny
isAny
). Alias definitions should beseparated from the last import by one line.
fromtypingimportAnysome_mod=Any# some_mod.py imports this module....defmy_method(self,var:"some_mod.SomeType")->None:...
When annotating, prefer to specify type parameters forgeneric types in aparameter list; otherwise, the generics’ parameters will be assumed to beAny
.
# Yes:defget_names(employee_ids:Sequence[int])->Mapping[int,str]:...
# No:# This is interpreted as get_names(employee_ids: Sequence[Any]) -> Mapping[Any, Any]defget_names(employee_ids:Sequence)->Mapping:...
If the best type parameter for a generic isAny
, make it explicit, butremember that in many casesTypeVar
might be moreappropriate:
# No:defget_names(employee_ids:Sequence[Any])->Mapping[Any,str]:"""Returns a mapping from employee ID to employee name for given IDs."""
# Yes:_T=TypeVar('_T')defget_names(employee_ids:Sequence[_T])->Mapping[_T,str]:"""Returns a mapping from employee ID to employee name for given IDs."""
BE CONSISTENT.
If you’re editing code, take a few minutes to look at the code around you anddetermine its style. If they use_idx
suffixes in index variable names, youshould too. If their comments have little boxes of hash marks around them, makeyour comments have little boxes of hash marks around them too.
The point of having style guidelines is to have a common vocabulary of coding sopeople can concentrate on what you’re saying rather than on how you’re sayingit. We present global style rules here so people know the vocabulary, but localstyle is also important. If code you add to a file looks drastically differentfrom the existing code around it, it throws readers out of their rhythm whenthey go to read it.
However, there are limits to consistency. It applies more heavily locally and onchoices unspecified by the global style. Consistency should not generally beused as a justification to do things in an old style without considering thebenefits of the new style, or the tendency of the codebase to converge on newerstyles over time.