Write Idiom Python

Python支持链式比较

  1. # bad
  2. a = 5
  3. if a > 1 and a < 7:
  4. pass
  5. # good
  6. if 1 < a < 7:
  7. pass

Python交换变量

  1. # bad
  2. x = 10
  3. y = 5
  4. tmp = x
  5. x = y
  6. y = tmp
  7. # good
  8. x = 10
  9. y = 5
  10. x, y = y, x

Python中替代三目运算符?:

  1. # bad
  2. a = 10
  3. b = 5
  4. if a > b:
  5. c = a
  6. else:
  7. c = b
  8. # good
  9. c = a if a > b else b

拼接字符列表时,用join方法去实现

  1. # bad

格式化字符时多使用format函数

  1. # bad
  2. name = "tony"
  3. age = 100
  4. str = "myname : " + name + " my age : " + str(age)
  5. str1 = "myname : %s my age : %d" % (name, age)
  6. # good
  7. str2 = "myname : {} my age {}".format(name, age)

使用列表或者字典comprehension

  1. # bad
  2. mylist = range(20)
  3. odd_list = []
  4. for e in mylist:
  5. if e % 2 == 1:
  6. odd_list.append(e)
  7. # good
  8. odd_list = [e for e in mylist if e % 2 == 1]
  9. # bad
  10. user_list = [{'name': 'lucy', 'email': 'lucy@g.com'}, {'name': 'lily', 'email': 'lily@g.com'}]
  11. user_email = {}
  12. for user in user_list:
  13. if 'email' in user:
  14. user_email[user['name']] = user['email']
  15. # good
  16. {user['name']: user['email'] for user in user_list if 'email' in user}

条件判断时,避免直接和True, False, None进行比较(==)

  1. # bad
  2. if l == []:
  3. pass
  4. # good
  5. if l: # 实际调用l.__len__() == 0
  6. pass
  7. # bad
  8. if something == None:
  9. # good, None 是单例对象
  10. if something is None:

使用enumerate代替for循环中的index变量访问

  1. # bad
  2. my_container = ['lily', 'lucy', 'tom']
  3. index = 0
  4. for element in my_container:
  5. print '{} {}'.format(index, element)
  6. index += 1
  7. # good
  8. for index, element in enumerate(my_container):
  9. print '%d %s' % (index, element)

避免使用可变(mutable)变量作为函数参数的默认初始化值

  1. # bad
  2. def function(l = []):
  3. l.append(1)
  4. return l
  5. print function()
  6. print function()
  7. print function()
  8. # print
  9. [1]
  10. [1, 1]
  11. [1, 1, 1]
  12. # good 使用None作为可变对象占位符
  13. def function(l=None):
  14. if l is None:
  15. l = []
  16. l.append(1)
  17. return l

一切皆对象

  1. # bad
  2. def print_addition_table():
  3. for x in range(1, 3):
  4. for y in range(1, 3):
  5. print(str(x + y) + '\n')
  6. def print_subtraction_table():
  7. for x in range(1, 3):
  8. for y in range(1, 3):
  9. print(str(x - y) + '\n')
  10. def print_multiplication_table():
  11. for x in range(1, 3):
  12. for y in range(1, 3):
  13. print(str(x * y) + '\n')
  14. def print_division_table():
  15. for x in range(1, 3):
  16. for y in range(1, 3):
  17. print(str(x / y) + '\n')
  18. print_addition_table()
  19. print_subtraction_table()
  20. print_multiplication_table()
  21. print_division_table()
  22. # good, python一切都是对象,可以函数作为参数,类似技巧可以用来简化代码
  23. import operator as op
  24. def print_table(operator):
  25. for x in range(1, 3):
  26. for y in range(1, 3):
  27. print(str(operator(x, y)) + '\n')
  28. for operator in (op.add, op.sub, op.mul, op.div):
  29. print_table(operator)

防御式编程EAFP vs LBYL

  • EAFP:easier to ask forgiveness than permission
  • LBYL:look before you leap

EAFP可以理解成一切按正常的逻辑编码,不用管可能出现的错误,等出了错误再说;而LBYL就是尽可能每写一行代码,都要提前考虑下当前的前置条件是否成立;

  1. # LBYL
  2. def getPersonInfo(person):
  3. if person == None:
  4. print 'person must be not null!'
  5. print person.info
  6. # EAFP
  7. def getPersonInfo(person):
  8. try:
  9. print person.info
  10. except NameError:
  11. print 'person must be not null!'

其实用EAFP风格的代码最大的好处是代码逻辑清晰,而LBYL会导致本来两句话说清楚的事,往往因为穿插了很多条件检查的语句使代码逻辑变得混乱。Python社区更提倡EAFP形式的。另外还有一个原因,在高并发场景下, if条件如果是个表达式,会造成一致性问题,这个时候必须用EAFP形式。这个可以参考Glow团队的技术博客[Glow cache structure](http://tech.glowing.com/cn/glow-cache-structure).

用dict对象完成switch…case…的功能

  1. # bad
  2. def apply_operation(left_operand, right_operand, operator):
  3. if operator == '+':
  4. return left_operand + right_operand
  5. elif operator == '-':
  6. return left_operand - right_operand
  7. elif operator == '*':
  8. return left_operand * right_operand
  9. elif operator == '/':
  10. return left_operand / right_operand
  11. # good
  12. def apply_operation(left_operand, right_operand, operator):
  13. import operator as op
  14. operator_mapper = {'+': op.add, '-': op.sub, '*': op.mul, '/': op.truediv}
  15. return operator_mapper[operator](left_operand, right_operand)

访问tuple的数据项时,可以用namedtuple代替index的方式访问

  1. # bad
  2. rows = [('lily', 20, 2000), ('lucy', 19, 2500)]
  3. for row in rows:
  4. print '{}`age is {}, salary is {} '.format(row[0], row[1], row[2])
  5. # good
  6. from collections import namedtuple
  7. Employee = namedtuple('Employee', 'name, age, salary')
  8. for row in rows:
  9. employee = Employee._make(row)
  10. print '{}`age is {}, salary is {} '.format(employee.name, employee.age, employee.salary)

用isinstance来判断对象的类型

因为在python中定义变量时,不用像其它静态语言,如java, 要指定其变量数据类型,如int = 4. 但是这并不意味在python中没有数据类型,只是一个变量的数据类型是在运行的时候根据具体的赋值才最终确定。比如下面的代码是计算一个对象的长度值,如果是序列类型(str,list,set,dict)的, 直接调用len方法,如果是True, False, None则返回1,如果是数值的,则返回其int值.

  1. # bad
  2. def get_size(some_object):
  3. try:
  4. return len(some_object)
  5. except TypeError:
  6. if some_object in (True, False, None):
  7. return 1
  8. else:
  9. return int(some_object)
  10. print(get_size('hello'))
  11. print(get_size([1, 2, 3, 4, 5]))
  12. print(get_size(10.0))
  13. # good
  14. def get_size(some_object):
  15. if isinstance(some_object, (list, dict, str, tuple)):
  16. return len(some_object)
  17. elif isinstance(some_object, (bool, type(None))):
  18. return 1
  19. elif isinstance(some_object, (int, float)):
  20. return int(some_object)

用with管理操作资源的上下文环境

在一个比较典型的场景里,如数据库操作,我们操作connection时一般要正常关闭连接,而不管是正常退出还是异常退出。如下:

  1. # bad
  2. class Connection(object):
  3. def execute(self, sql):
  4. raise Exception('ohoh, exception!')
  5. def close(self):
  6. print 'closed the Connection'
  7. try:
  8. conn = Connection()
  9. conn.execute('select * from t_users')
  10. finally:
  11. conn.close()
  12. # good
  13. class Connection(object):
  14. def execute(self, sql):
  15. raise Exception('ohoh, exception!')
  16. def close(self):
  17. print 'closed the Connection'
  18. def __enter__(self):
  19. return self
  20. def __exit__(self, errorType, errorValue, error):
  21. self.close()
  22. with Connection() as conn:
  23. conn.execute('select * from t_users')

使用generator返回耗费内存的对象

  1. # bad
  2. def f():
  3. # ...
  4. return biglist
  5. # good
  6. def f():
  7. # ...
  8. for i in biglist:
  9. yield i

更多资源: