⬆️ ⬇️

Google coding tips in Python. Part One: Programming Tips



Hi, Habr!

Today I want to present my dear habra translation to my dear habrasoobshchestvo. Programming in Python is like a song. But it is even better when your code is read and understandable, which means it is a little more poetic than production usually happens. Everyone has their own rules and stereotypes regarding the writing and design of the source code, no matter what language it is written. Many copies are broken on the boards on the forums, but, anyway, one can not ignore the opinion of authoritative comrades. So now the translation of the first part of the style guide for the Python language from Google will be presented. It touches exactly the postulates of writing code (the second part will also appear soon, and it will be devoted to formatting the source code). Immediately I warn you: there are many (if not most) of the truisms that everyone has known for a long time. But I sincerely hope that you can find here something new or at least remember the old. Let's start under the cut. And pdf here as here.



Google Python Style Guide



Version: 2.48

Authors: Amit Patel , Antoine Picard , Eugene Jhong , Jeremy Hylton , Matt Smart , Mike Shields .



Training



Python is the main scripting language used by Google. This guide is a list of "good" and "bad" for programs written in Python. To help you format your code correctly, we created a configuration file for the Vim editor . In Emacs, the default settings should be suitable for our purposes.



Python programming tips



')

PyChecker


Use PyChecker to verify your code.
Definition


PyChecker is a tool for finding bugs in Python program sources. It finds problems that would be revealed by the compiler of less dynamic languages, such as C and C ++. This is very tempting. Due to the dynamic nature of Python, some warnings can be unfair, however false warnings should not occur often.



pros


It detects hard-to-detect errors, such as: type errors, use of variables before their declaration, etc.



Minuses


PyChecker is not perfect. To get all its benefits, we need:

  • Write with an eye to him
  • Suppress his warnings
  • Correct mistakes
  • Or ignore them


Decision


Make sure you run PyChecker with your code. To learn how to start PyChecker, check out its homepage . To suppress warnings, you need to create a __ pychecker__ variable in this module and specify which errors should be suppressed. For example:



__pychecker__ = 'no-callinit no-classattr' 


Suppressing warnings in this way has the advantage of the following plan - we can easily search for suppressions and return to them. You can see the list of PyChecker alerts if you type:



 pychecker -- help 


Unused arguments can be omitted using '_', as the names of the unused argument, or the prefix of the argument "unused_". In situations where changing the name of the argument is not possible, you can mention them at the beginning of the function. For example:



 def foo(a, unused_b, unused_c, d=None, e=None): _ = d, e return a 


Ideally, PyChecker would be fine-tuned so that you can safely assert that “unused declarations” are in reality.



Imports


Import only packages and modules.
Definition


Open source reuse mechanism from one module to another.



pros


The namespace convention is very simple. The location of each object in the code is indicated in a uniform way. x.Obj says that the Obj object is declared in module x.



Minuses


Sometimes module names may conflict. Some module names may be inconveniently long.



Decision


Use import x to import packages and modules.

Use from x import y when x is a package prefix, and y is the name of a module without a prefix.

Use from x import y as z if the two modules are named y , or if the name of the module is inconveniently long.

For example, the sound.effects.echo module can be imported as follows:



 from sound.effects import echo ... 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 only the full name of the package. This will help prevent the situation when the same package is imported twice.



Packages


Import each module using the full path to it.
pros


Bypasses conflicts in module names. It becomes easier to find the module on the disk.



Minuses


It becomes harder to transfer code, because You must completely recreate the complete hierarchy of modules.



Decision


All your new code should import each module by its full path in the package.

Import should be as follows:



 #       import sound.effects.echo #        () from sound.effects import echo 




Exceptions


Exceptions are allowed, but must be used carefully.
Definition


An exception is a means of exiting the normal control flow of a code block for handling errors or other exceptional conditions.



pros


The control flow of a regular code does not intermix with the error interception code. It also allows the execution thread to skip a certain number of frames when certain conditions are met, for example, returning from N-nested functions in one step instead of multiple error code handling.



Minuses


The control flow may not start correctly. It is very easy to skip the situation in which an error will be raised when calling a function from the library.



Decision


Exception handling should follow the following positions:

  • Raise exceptions like this: raise MyException ('Error message') , or raise MyException . Do not use the entry form with two arguments: ( raise MyException, 'Error message' ), just do not use the string form entry ( raise 'Error message' ).
  • Modules (or packages) must define their own domain-specific exception classes, which must inherit from the built-in class Exception .

    The main exception of the module should be called Error.



     class Error(Exception): pass 


  • Never use an exception that catches all exceptions only if you are not going to wake them up later, or you are not in an external code block in the thread (and an error message is printed). Pytnon is very tolerant in this regard, besides you can catch anything: naming errors, calling sys.exit () , interrupting ctrl + C , failures of tests and various types of exceptions that you just don’t want to catch.
  • Reduce the amount of code located in the try / except block. The larger the body of the try block, the more likely that an exception will be raised in the line of code in which you do not expect the latter to be excited. In these cases, the try / except block hides the real error.
  • Use the finally statement to execute code regardless of whether an exception was thrown in a try block or not. This is often useful for final actions, such as closing a file.
  • When catching an exception, it is better to use as than a comma:



     try: raise Error except Error as error: pass 






Global variables


Avoid using global variables.
Definition


Variables that are defined at the module level.



pros


Sometimes useful.



Minuses


You can inadvertently change the behavior of a module during import, since assignment of module level variables is already completed when the module is imported.



Decision


Avoid using global variables in favor of class variables. A few exceptions below:

  • Standard Script Settings.
  • Module Level Constants For example, PI = 3.14159 . Constants must be named using capital letters and underscores only. See the naming rules below.
  • Sometimes it is useful to use global variables to cache the values ​​needed for a function or returned by a function.
  • If necessary, global variables should be created inside the module and accessible through the public functions of the module level. See naming rules below.




Nested / local / internal classes and functions


Nested / local / internal classes and functions are good
Definition


A class can be defined inside a method, function, or other class. A function can be defined inside a method or another function. Nested functions have read-only access to variables defined in the parent area.



pros


Makes it possible to define auxiliary classes and functions that will be used only within a very limited space. Complies with the ADT principle.



Minuses


Instances of nested or local classes cannot be serialized.



Decision


They are good.



List generators


Can be used in simple cases.
Definition


List generators and generator expressions provide a compact and efficient way to create lists and iterators without using the map () , filter () or lambda expressions.



pros


A simple list generator can be cleaner and easier than other ways to create lists. An expression generator can be very effective, because it does not create the entire list at once.



Minuses


Complex list generators or generator expressions can be hard to read.



Decision


Can be used in simple cases. Each part should be located on one line. Display definition, for statement, conditional expression. Multiple for instructions or conditions are invalid. Use loops if your expressions become too complex.



Good:



  result = [] for x in range(10): for y in range(5): if x * y > 10: result.append((x, y)) for x in xrange(5): for y in xrange(5): if x != y: for z in xrange(5): if y != z: yield (x, y, z) return ((x, complicated_transform(x)) for x in long_generator_function(parameter) if x is not None) squares = [x * x for x in range(10)] eat(jelly_bean for jelly_bean in jelly_beans if jelly_bean.color == 'black') 


Poorly:



  result = [(x, y) for x in range(10) for y in range(5) if x * y > 10] return ((x, y, z) for x in xrange(5) for y in xrange(5) if x != y for z in xrange(5) if y != z) 




Standard iterators and operators


Use standard iterators and operators.
Definition


Container types, such as dictionaries and lists, define standard iterators, a set of test operators in and not in .



pros


Standard iterators and operators are simple and efficient. They express the operation directly, without internal callbacks. And the function that uses standard operators is simple. It can be used with different types that support this operation.



Minuses


You cannot name object types by method name (for example, has_key () means that it is a dictionary). This is also an advantage.



Decision


Use standard iterators and operators for types that support them, such as lists, dictionaries, and files. Built-in types also define iterator methods. Prefer these methods to methods that return lists, except when you want to change the container itself while iterating.



Good:



  for key in adict: ... if key not in adict: ... if obj in alist: ... for line in afile: ... for k, v in dict.iteritems(): ... 


Poorly:



  for key in adict.keys(): ... if not adict.has_key(key): ... for line in afile.readlines(): ... 




Generators


Use generators as needed
Definition


The generator function returns an iterator that produces a value each time it executes a yield expression. After generating the value, the execution status of the function is suspended until the next value is required.



pros


Simplifies the code, because the state of local variables and control flow remain the same for each call. The generator requires less memory than the function that creates the list of values ​​immediately.



Minuses


No cons.



Decision


Perfectly. Give preference to “Generates:" rather than "Returns:" in the documentation lines for generator functions.



Lambda functions


Good for inline expressions.
Definition


Lambda functions define anonymous functions in an expression as an alternative to the construction of a regular function. They are often used to declare callback functions or operators for higher order functions, such as map () and filter () .



pros


Conveniently.



Minuses


More difficult to read and debug than local functions. Missing names means that the call stack will be harder to read. Expressiveness is limited, because a function can contain only one expression.



Decision


Good for inline expressions. If the code inside the lambda function is longer than 60-80 characters, then it may be better to define this function as a normal (or nested) function. For simple operations, such as multiplication, use functions from the operator module instead of lambda functions. For example, it is better to use operator.mul instead of lambda x, y: x * y .



Conditional expressions


Good for inline expressions.
Definition


Conditional expressions are a mechanism that provides a short syntax for an if construct. For example, x = 1 if cond else 2 .



pros


Shorter and more convenient than the if construct.



Minuses


It may be more difficult to read than an if statement . The condition may be too difficult to write if the expression is very long.



Decision


Use for inline expressions only. In other cases, give preference to the use of a full-fledged if .



Default Arguments


Can be used in most cases.
Definition


You can assign values ​​to variables at the end of the function argument list, for example, def foo (a, b = 0):

If the foo function is called with only one argument, the argument b will be 0 . If it is called with two arguments, b will have the value passed in the second argument.



pros


Often you have a function that uses many default values. But sometimes you need to override the default values. Also, Python does not support overloading of methods / functions, and standard arguments are an easy way to “simulate” overload behavior.



Minuses


Values ​​of standard arguments are calculated once during module loading. This can cause a problem if the argument is a mutable object, such as a list or dictionary. If the function changes the object (for example, by adding an item to the list), the default value will be changed.



Decision


Can be used with the following cautions:

Do not use mutable objects as standard values ​​in functions or method definitions.



Good:



 def foo(a, b=None): if b is None: b = [] 


Poorly:



 def foo(a, b=[]): 


Code call must use named values ​​for arguments with a default value. This allows you to document the code and helps prevent and detect defects when more arguments are passed than is needed.

 def foo(a, b=1): ... 




Good:



 foo(1) foo(1, b=2) 


Poorly:



 foo(1, 2) 




Properties


Use properties to access data or assign values
Use properties to access data or assign values ​​where you would normally use the simple getter or setter method.



Definition


The way to wrap up calls to an access method and attribute assignment as standard attribute access is, of course, if the calculations are not very complex.



pros


Readability is enhanced by eliminating explicit calls to get and set methods for easy access to an attribute. Lazy calculations are possible. It is believed that supporting the class interface is part of the Python path. From the point of view of performance, allows the property to be accessible through the simplest getter method (accessor), when direct access to a variable is needed. It also allows access methods to be added later without changing the interface.



Minuses


Properties are declared after the get and set methods have been declared, demanding to inform them that they are used for properties located below in the code (excluding read-only properties created using the @property decorator, see below for details). They must inherit from the object class. Can hide side effects, such as operator overload. Can be misleading classes-heirs.



Decision


Use properties in the code you just wrote to access or change data where you would use a simple get method or set method. “Read only” properties should be created using the @property decorator. Inheritance with properties may not be very transparent unless the parent property is overridden. Thus, it is necessary to make sure that the getter methods are invoked indirectly in order to provide redefinition to methods in subclasses called via property (using the Template method pattern)



Good:



 import math class Square(object): """A square with two properties: a writable area and a read-only perimeter. To use: >>> sq = Square(3) >>> sq.area 9 >>> sq.perimeter 12 >>> sq.area = 16 >>> sq.side 4 >>> sq.perimeter 16 """ def __init__(self, side): self.side = side def __get_area(self): """Calculates the 'area' property.""" return self.side ** 2 def ___get_area(self): """Indirect accessor for 'area' property.""" return self.__get_area() def __set_area(self, area): """Sets the 'area' property.""" self.side = math.sqrt(area) def ___set_area(self, area): """Indirect setter for 'area' property.""" self.__set_area(area) area = property(___get_area, ___set_area, doc="""Gets or sets the area of the square.""") @property def perimeter(self): return self.side * 4 




Calculations True / False


Use False explicitly if possible.
Definition


Python calculates certain values ​​in False when we are in the context of a boolean value. The easiest way to remember this is to know that all “empty” values, such as False , 0 , None , [] , {} , are evaluated in False in a boolean context.



pros


Such conditional constructs that use the Python boolean values ​​are readable and less prone to errors. In most cases, they are faster.



Minuses


It may look weird for C / C ++ developers.



Decision


Use False explicitly if possible. For example, if foo: better than if foo! = []:. You should remember a few exceptions:

  • Never use == or ! = To compare singleton objects such as None . Use is or is not .
  • Beware of writing if x: when you mean if x is not None:, for example, when testing a variable or argument that is None by default, but its value has been changed to something else. Another value can be computed in False with a boolean check.
  • Never compare a Boolean variable to False using == . Use if not x instead. False None , , if not x and x is not None .
  • (, , ) , False , if not seq if seq , if len(seq) if not len(seq) :.
  • , False , ( None 0 ). , ( len() ) c .




:



 if not users: print "no" if foo == 0: self.handle_zero() if i % 10 == 0: self.handle_multiple_of_ten() 




:



 if len(users) == 0: print 'no users' if foo is not None and not foo: self.handle_zero() if not i % 10: self.handle_multiple_of_ten() 




, “0” (.. 0 ) True .



Outdated language features


Use methods instead of the string module where possible
string , . , apply . for filter map , -. reduce .





Python .



Decision


Python, , .



:



 words = foo.split(':') [x[1] for x in my_list if x[2] == 5] map(math.sqrt, data) #   -     fn(*args, **kwargs) 




:



 words = string.split(foo, ':') map(lambda x: x[1], filter(lambda x: x[2] == 5, my_list)) 




Lexical context


Permitted to use


, , . , . Python , . , .

:



 def get_adder(summand1): """Returns a function that adds numbers to a given number.""" def adder(summand2): return summand1 + summand2 return adder 


pros


. Lisp Scheme ( Haskell ML ..) .



Minuses


, PEP-0227:

 i = 4 def foo(x): def bar(): print i, # ... #    # ... for i in x: # ,  i   Foo,  - Bar  . print i, bar() 


foo([1,2,3]) 1 2 3 3 , 1 2 3 4 .



Decision


.





From translator



Acknowledgments


I want to express many thanks to SquaII , for reading and help in punctuation.



Soc. poll

Source: https://habr.com/ru/post/179271/



All Articles