name='Tom'
is_generic_name = name in ('Tom','Dick','Harry')
1.4. Avoid comparing directly to True,False,or None
All of the following are considered False •None •False •zero for numeric types •empty sequences •empty dictionaries •a value of 0 or False returned when either __len__ or __nonzero__ is called
Don’t
1
2
if foo == True:
pass
Do:
1
2
3
4
5
6
if foo:
passdefinsert_value(value,position=None):if position isnotNone:
pass
1.5. Use if and else as a short ternary operator replacement(三元运算符 ? :)
Don’t:
1
2
3
4
5
foo = True
value = 0if foo:
value = 1
print(value)
Do:
1
2
3
foo = True
value = 1if foo else0
print(value)
2. for Loops
2.1. enumerate function in loops instead of creating an index variable
Don’t:
1
2
3
4
5
my_container = ['Larry', 'Moe', 'Curly']
index = 0for element in my_container:
print ('{} {}'.format(index, element))
index += 1
2.2. Use the in keywords to iterate over an iterable
Don’t:
1
2
3
4
5
my_container = ['Larry', 'Moe', 'Curly']
index = 0whileindex < len(my_container):
print (my_container[index])
index += 1
Do:
1
2
3
my_container = ['Larry', 'Moe', 'Curly']
for ele in my_container:
print(ele)
2.3. Use else to execute code after a for loop concludes
Don’t:
1
2
3
4
5
6
7
8
9
10
for user in get_all_users():
has_malformed_email_address=False
print('Checking{}'.format(user))
for email_address in user.get_all_email_addresses():
if email_is_malformed(email_address):
has_malformed_email_address=True
print('Has a malformede mail address!')
breakifnot has_malformed_email_address:
print('All email addresses are valid!')
Do:
1
2
3
4
5
6
7
8
for user in get_all_users():
print('Checking{}'.format(user))
for email_address in user.get_all_email_addresses():
if email_is_malformed(email_address):
print('Has a malformede mail address!')
breakelse:
print('All email addresses are valid!')
3. Functions
3.1. Avoid using a mutable object as the default value for a function argument
list,dict,set and most class instances are mutable. string,int,tuple objects are all immutabled.
Don’t:
1
2
3
deftest(a,L=[]):
L.append(a)
return L
Do:
1
2
3
4
5
deftest(a,L=None):if L isNone:
L = []
L.append(a)
return L
3.2. Use return to evaluate expressions in addition to return values
Don’t:
1
2
3
4
5
defall_equal(a,b,c):
result=Falseif a==b==c:
result=Truereturn result
defprint_addition_table():for x in xrange(1,4):
for y in xrange(1,4):
print(str(x+y)+'\n')
defprint_subtraction_table():for x in xrange(1,4):
for y in xrange(1,4):
print(str(x-y)+'\n')
defprint_multiplication_table():for x in xrange(1,4):
for y in xrange(0,4):
print(str(x*y)+'\n')
defprint_division_table():for x in xrange(1,4):
for y in xrange(1,4):
print(str(x/y)+'\n')
print_addition_table()
print_subtraction_table()
print_multiplication_table()
print_division_table()
Do:
1
2
3
4
5
6
7
8
import operator as op
defprint_table(operator):for x in xrange(1,4):
for y in xrange(0,4):
print(operator(x,y)+'\n')
for operator in (op.add,op.sub,op.mul,op.div):
print_table(operator)
3.6. Use the function-based version of print
Don’t:
1
print1,'foo',__name__
Do:
1
2
from __future__ import print_function
print(1,'foo',__name__)
4. Exceptions
4.1. Don’t be Afraid to Use Exceptions
4.2. Use Exceptions to Write Code in an “EAFP” Style
Code that doesn’t use exceptions is always checking if it’s OK to do something.
defget_log_level(config_dict):try:
if config_dict['ENABLE_LOGGING']:
return config_dict['DEFAULT_LOG_LEVEL']
except KeyError:
#if either value wasn't present,a#KeyError will be raised,so#return NonereturnNone
4.3. Avoid ‘’Swallowing’’ Useful Exceptions With Bare Except Clauses
A common mistake made by novices when using exceptions is to feel compelled to catch anye xception code could raise.
import requests
defget_json_response(url):return requests.get(url).json()
#If we need to make note of the exception,we#would write the function this way...defalternate_get_json_response(url):try:
r=requests.get(url)
returnr.json()
except:
#do some logging here,but don't handle the exception#...raise
5. Data Structure
5.1 Use multiple assignment to condense variables all set to the same value
1
x = y = z = 5
instead of
1
2
3
x = 5y = 5z = 5
5.2. Avoid using a temporary variable when performing a swap of two values
1
2
3
4
x = 10y = 5
# tmp=x,x=y,y=tempx, y = y, x
5.3. Chain string functions to make a simple series of transformations more clear
1
2
str1 = 'i am a bug!'
str4 = str1.strip().upper().replace('!', '?')
5.4. Use ''.join when creating a single string for list elements
5.10. Prefer xrange to range unless you need the resulting list
1
2
3
forindex in xrange(3, 1000000000):
ifindex % 2 == 0:
printindex
5.11. 用dict对象完成 switch…case… 的功能
5.12. 使用dict.get方法可以提供一个默认值
1
print mydict.get('b', 2)
5.13. list/dict/set comprehension
1
2
3
4
5
user_list = [{'name': 'lucy', 'email': 'lucy@g.com'}, {'name': 'lily',\
'email': 'lily@g.com'}]
{user['name']: user['email'] for user in user_list if'email'in user}
users_first_names = {user.first_name for user in users}
5.14. Use sets to eliminate duplicate entries from Iterable containers
1
unique_names = set(name_list)
5.15. Use collections.namedtuple to make tuple-heavy code more clear
1
2
3
4
5
6
7
8
9
rows = [('lily', 20, 2000), ('lucy', 19, 2500)]
for row in rows:
print'{}` age is {}, salary is {} '.format(row[0], row[1], row[2])
from collections import namedtuple
Employee = namedtuple('Employee', 'name, age, salary')
for row in rows:
employee = Employee._make(row)
print'{}` age is {}, salary is {} '.format(employee.name, employee.age, employee.salary)
5.16. Use _ as a placeholder for data in a tuple that should be ignored
Don’t:
1
2
3
4
(name,age,temp,temp2)=get_user_info(user)
if age>21:
output='{name} can drink!'.format(name=name)
#"Wait,where are temp and temp2 being used?"
Do:
1
2
3
4
(name,age,_,_)=get_user_info(user)
if age>21:
output='{name} cand rink!'.format(name=name)
#"Clearly,only name and age are interesting"
5.17. Use tuples to unpack data
Don’t:
1
2
3
4
5
6
7
list_from_comma_separated_value_file=['dog','Fido',10]
animal=list_from_comma_separated_value_file[0]
name=list_from_comma_separated_value_file[1]
age=list_from_comma_separated_value_file[2]
output=('{name} the {animal} is {age} years old'.format(\
animal=animal,name=name,age=age))
Do:
1
2
3
4
list_from_comma_separated_value_file=['dog','Fido',10]
(animal,name,age)=list_from_comma_separated_value_file
output=('{name} the {animal} is {age} years old'.format(\
animal=animal,name=name,age=age))
5.18. Use a tuple to return multiple values from a function
1
2
3
4
5
6
7
8
from collections import Counter
defcalculate_staistics(value_list):
mean=float(sum(value_list)/len(value_list))
median=value_list[int(len(value_list)/2)]
mode=Counter(value_list).most_common(1)[0][0]
return(mean,median,mode)
(mean,median,mode)=calculate_staistics([10,20,20,30])
6. Classes
6.1. Use the isinstance function to determine the type of ano bject
classFoo(object):def__init__(self):
self.id=8
self.value=self.get_value()
defget_value(self):passdefshould_destroy_earth(self):return self.id==42classBaz(Foo):defget_value(self,some_new_parameter):"""Since 'get_value' is called from the base class's __init__ method
and the base class definition doesn't take a parameter,trying to
createa Baz instance will fail"""passclassQux(Foo):def__init__(self):"""We aren't aware of Foo's internals,and we innocently createan
instance attribute named'id' and set it to 42.This overwrites
Foo's id attribute and we inadvertently blow up the earth."""
super(Qux,self).__init__()
self.id=42
q=Qux()
b=Baz() #Raises'TypeError'
q.should_destroy_earth()#returnsTrue
q.id==42#returnsTrue
classFoo(object):def__init__(self):""" Since 'id' is of vital importance to us,we don't want a derived
class accidentally overwriting it.We'll prepend with double
underscores to introduce name mangling."""
self.__id=8
self.value=self.__get_value() # call our 'private copy'defget_value(self):passdefshould_destroy_earth(self):return self.__id==42#Here,we're storing a 'private copy' of get_value,#and assigning it to '__get_value'.Even if aderived#class overrides get_value in a way incompatible with#ours,we're fine
__get_value=get_value
classBaz(Foo):defget_value(self,some_new_parameter):passclassQux(Foo):def__init__(self):""" Now when we set 'id' to 42,it's not the same 'id' that
'should_destroy_earth' is concerned with. In fact,if you inspecta
Qux object,you'll find it doesn't havean __id attribute.So we can't
mistakenly change Foo's__id attribute even if we wanted to."""
super(Qux,self).__init__()
#No relation to Foo's id,purely coincidental
self.id=42
q=Qux()
b=Baz() #Works fine now
q.should_destroy_earth()#returns False
q.id==42#returns True
6.3. Use properties to “future-proof” your class implementation
Oftentimes,it is convenient to provide direct access to a class’s data attributes.A Point class,for example,may have x and y attributes rather than using getter and setter functions. Don’t:
classProduct(object):
default__init__(self,name,price):
self.name=name
self.price =price
@propertydefprice(self):#now if we need to change how price is calculated,we can do it#here(or in the "setter" and __init__)
retrun self._price * TAX_RATE
@price.setterdefprice(self,value):#The "setter" function must have the same name as the property
self._price=value
6.4. Use __repr__ for a machine-readable representation of a class and define __str__ in a class to show a human-readable representation
While __str__ is used for printing a class in a way that a human can read,__repr__ is used for machines to read. Don’t
7.1. Use a context manager to ensure resources are properly managed
Don’t:
1
2
3
4
file_handle=open(path_to_file,'r')
forlinein file_handle.readlines():
if raise_exception(line):
print('No!An Exception!')
Do:
1
2
3
4
withopen(path_to_file,'r') as file_handle:
forlinein file_handle:
if raise_exception(line):
print('No!AnException!')
8. Generators
8.1. Prefer a generator expression to a list comprehension for simple iteration
generator, 用 (…) 代替 [….], 就像用xrange代替range一样
1
2
for e in (i * 2for i in oneList):
pass
8.2. Use a generator to lazily load infinite sequences
Don’t:
1
2
3
4
5
6
7
8
9
10
11
12
13
defget_twitter_stream_for_keyword(keyword):
imaginary_twitter_api=ImaginaryTwitterAPI()
if imaginary_twitter_api.can_get_stream_data(keyword):
return imaginary_twitter_api.get_stream(keyword)
current_stream=get_twitter_stream_for_keyword('#jeffknupp')
for tweet in current_stream:
process_tweet(tweet)
defget_list_of_incredibly_complex_calculation_results(data):return [first_incredibly_long_calculation(data),\
second_incredibly_long_calculation(data),\
third_incredibly_long_calculation(data)]
Do:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
defget_twitter_stream_for_keyword(keyword):
imaginary_twitter_api=ImaginaryTwitterAPI()
while imaginary_twitter_api.can_get_stream_data(keyword):
yield imaginary_twitter_api.get_stream(keyword)
for tweet in get_twitter_stream_for_keyword('#jeffknupp'):
if got_stop_signal:
break
process_tweet(tweet)
defget_list_of_incredibly_complex_calculation_results(data):yield first_incredibly_long_calculation(data)
yield second_incredibly_long_calculation(data)
yield third_incredibly_long_calculation(data)
10. Organizing Your Code
10.1. Use all capital letters when declaring global constant values
1
SECONDS_IN_A_DAY=60*60*24
10.2. Avoid placing multiple statements on a single line
10.3. Multiple line docstring """"""
10.4. Document what something does,not how
10.5. Arrange your import statements in a standard order
10.6. Prefer absolute imports to relative imports
10.7. Do not use from foo import * to import the contents of a module.
10.8. Use the tuples to organizea long list of modules to import
10.9. Make use of __init__.py files to simplify package interfaces
10.10. Separate your test code from your application code