我们都喜欢 Python,因为它让编程和理解变的更为简单。但是一不小心,我们就会忽略规则,以非 Pythonic 方式编写一堆垃圾代码,从而浪费 Python 这个出色的语言赋予我们的优雅。Python 的代码风格是非常优雅、明确和简单,在 Python 解释器中执行 import this 你可以看到 Tim Peters 编写的 Python 之禅:
   
    - 
     
      
     
     
      
       >>> 
       import this
      
     
- 
     
      
     
     
      
       The Zen of Python, by Tim Peters
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       Beautiful is better than ugly.
      
     
- 
     
      
     
     
      
       Explicit is better than implicit.
      
     
- 
     
      
     
     
      
       Simple is better than 
       complex.
      
     
- 
     
      
     
     
      
       Complex is better than complicated.
      
     
- 
     
      
     
     
      
       Flat is better than nested.
      
     
- 
     
      
     
     
      
       Sparse is better than dense.
      
     
- 
     
      
     
     
      
       Readability counts.
      
     
- 
     
      
     
     
      
       Special cases aren
       't special enough to break the rules.
      
     
- 
     
      
     
     
      
       Although practicality beats purity.
      
     
- 
     
      
     
     
      
       Errors should never pass silently.
      
     
- 
     
      
     
     
      
       Unless explicitly silenced.
      
     
- 
     
      
     
     
      
       In the face of ambiguity, refuse the temptation to guess.
      
     
- 
     
      
     
     
      
       There should be one-- and preferably only one --obvious way to do it.
      
     
- 
     
      
     
     
      
       Although that way may not be obvious at first unless you're Dutch.
      
     
- 
     
      
     
     
      
       Now is better than never.
      
     
- 
     
      
     
     
      
       Although never is often better than *right* now.
      
     
- 
     
      
     
     
      
       If the implementation is hard to explain, it
       's a bad idea.
      
     
- 
     
      
     
     
      
       If the implementation is easy to explain, it may be a good idea.
      
     
- 
     
      
     
     
      
       Namespaces are one honking great idea -- let's do more of those!
      
     
这里我找了目前最好的中文版本:
美 优于 丑
明确 优于 隐晦
简单 优于 复杂
复杂 也好过 繁复
扁平 优于 嵌套
稀疏 优于 拥挤
可读性很重要
固然代码实用与否 比洁癖更重要,
我们以为的特例也往往没有特殊到必须打破上述规则的程度
除非刻意地静默,否则不要无故忽视异常
如果遇到模棱两可的逻辑,请不要自作聪明地瞎猜。
应该提供一种,且最好只提供一种,一目了然的解决方案
当然这是没法一蹴而就的,除非你是荷兰人[1]
固然,立刻着手 好过 永远不做。
然而,永远不做 也好过 不审慎思考一撸袖子就莽着干
如果你的实现很难解释,它就一定不是个好主意
即使你的实现简单到爆,它也有可能是个好办法
命名空间大法好,不搞不是地球人!
[1]:本文作者 Tim peters 解释说这里的荷兰人指的是 Python 的作者 Guido van Rossum.
以下是用 Python 编写更好的代码的 8 种方法:
一、忘掉类 C 语言风格
如果需要打印列表中的所有元素及其索引,你想到的第一件事是:
   
    - 
     
      
     
     
      
       for i in 
       range(
       len(arr)):
      
     
- 
     
      
     
     
      
           
       print(i, arr[i])
      
     
那么你仍然在编写 C 代码。摆脱这一点,请牢记 Python 关键字 enumerate 。它索引列表/字符串中的所有元素,并且支持设置索引的起始编号:
   
    - 
     
      
     
     
      
       >>> 
       for index, item in enumerate([
       'a',
       'b',
       'c']): 
      
     
- 
     
      
     
     
      
       ...     
       print(index, item)
      
     
- 
     
      
     
     
      
       ... 
      
     
- 
     
      
     
     
      
       0 a
      
     
- 
     
      
     
     
      
       1 b
      
     
- 
     
      
     
     
      
       2 c
      
     
- 
     
      
     
     
      
       >>> 
       for index, item in enumerate([
       'a',
       'b',
       'c'],
       1): #这里第二个参数可以设置起始编号
      
     
- 
     
      
     
     
      
       ...     
       print(index,item)
      
     
- 
     
      
     
     
      
       ... 
      
     
- 
     
      
     
     
      
       1 a
      
     
- 
     
      
     
     
      
       2 b
      
     
- 
     
      
     
     
      
       3 c
      
     
现在看起来更好了,而且更加 Pythonic。将列表转换成字符串呢?如果你这样写:
   
    - 
     
      
     
     
      
       # The C way
      
     
- 
     
      
     
     
      
       string = 
       ''
      
     
- 
     
      
     
     
      
       for i in arr:
      
     
- 
     
      
     
     
      
           
       string += i
      
     
就是 C 风格,如果使用 Python 的关键字 join,不仅效率更高,而且更优雅:
   
    - 
     
      
     
     
      
       # The Python way
      
     
- 
     
      
     
     
      
       string = 
       ''.join(arr)
      
     
就像 join 一样 ,Python 有很多神奇的关键字,因此请不要为语言工作,而是使用该语言为你工作。
 
 
 二、牢记 PEP8
我不是要求你完全遵循 PEP8,而是要求遵循其中的大多数规则,何况现在有很多自动格式化的工具,足以让你的代码更加美观,我们的 Python 之父也说过:阅读代码的频率远远高于写代码的频率,他是如此的正确!因此代码的可读性非常重要。
你是否对自己曾经写过的代码感到好奇?为什么这么写,这句话为什么在这?好吧,PEP8 是大多数这类问题的答案。尽管代码注释是个好方法,但是代码的风格也需要加以调整,比如变量 i , j , count 等即使第一次出现时写了注释,也不能保证后面你仍然记得住,这样来看就浪费了宝贵的时间。
任何普通的程序员都可以编写计算机可以理解的代码。只有好的程序员可以编写人类可以理解的代码。
 
 
 首选 CamelCase 作为类, UPPER_WITH_UNDERSCORES 作为常量,而 lower_with_underscores 作为变量,方法和模块名称。即使使用,也要避免使用单一名称功能 lambda 。
三、善用推导式
常用的推导式有:列表推导式,集合推导式,字典推导式。先说下列表推导式。
列表推导式就是当我们需要基于一个已有的列表创建新的列表时,所使用的语法格式,列表推导式包含以下四个部分:
1、一个输入序列(Input Sequence) 2、一个变量,代表着输入序列的一个成员(Variable) 3、一个可选的判定表达式,表达这个变量满足的条件(Optional Predicate ) 4、一个输出序列,根据 2 和 3 生成一个输出序列(Output Expression)
比如有个列表既有数字,又有字符,现在需要计算数字的平方,并将结果放在新的列表中,如果不用列表推导式,写出的代码就是这样的:
   
    - 
     
      
     
     
      
       # bad code
      
     
- 
     
      
     
     
      
       a_list = [
       1, ‘
       4’, 
       9, ‘a’, 
       0, 
       4]
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       squared_ints = []
      
     
- 
     
      
     
     
      
       for item in a_list:
      
     
- 
     
      
     
     
      
           
       if 
       type(item) == types.IntType:
      
     
- 
     
      
     
     
      
               squared_ints.
       append(item**
       2)
      
     
如果使用列表推导式,只需要两行代码,非常的优雅:
   
    - 
     
      
     
     
      
       a_list = [
       1, ‘
       4’, 
       9, ‘a’, 
       0, 
       4]
      
     
- 
     
      
     
     
      
       squared_ints = [ e**
       2 
       for e in a_list 
       if 
       type(e) == types.IntType ]
      
     
 
 
 当然,如果你喜欢 map 和 filter,你还可以这样做,当时这是不推荐的,因为可读性不好:
map(lambda e: e**2, filter(lambda e: type(e) == types.IntType, a_list))
比如集合推导式的使用:
给定输入
names = [ 'Bob', 'JOHN', 'alice', 'bob', 'ALICE', 'J', 'Bob' ]
希望得到:
{ 'Bob', 'John', 'Alice' }
那么集合推导式就是:
{ name[0].upper() + name[1:].lower() for name in names if len(name) > 1 }
再比如字典推导式:
   
    - 
     
      
     
     
      
       mcase = {
       'a':
       10, 
       'b': 
       34, 
       'A': 
       7, 
       'Z':
       3}
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       mcase_frequency = { k.lower() : mcase.get(k.lower(), 
       0) + mcase.get(k.upper(), 
       0) 
       for k in mcase.keys() }
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       # mcase_frequency == {
       'a': 
       17, 
       'z': 
       3, 
       'b': 
       34}
      
     
从上面可以看出。推导式风格的代码是优雅的,人类易读的。
四、你还在显式的关闭文件吗?
如果你在写代码时仍然在显式的关闭文件,就像上图中的 programmer,你在为编程语言工作,如果你学会了使用 with 上下文管理器,那么你就是一个 Python programmer,让编程语言为你工作:
   
    - 
     
      
     
     
      
       with open(
       'filename.txt', 
       'w') as filename:
      
     
- 
     
      
     
     
      
           filename.write(
       'Hello')
      
     
当程序退出 with 块时,文件会自动关闭。with 语句的语法格式:
   
    - 
     
      
     
     
      
       with VAR as EXPR:
      
     
- 
     
      
     
     
      
           BLOCK
      
     
相当于:
   
    - 
     
      
     
     
      
       mgr = (EXPR)
      
     
- 
     
      
     
     
      
       exit = 
       type(mgr).__exit__  # Not calling it yet
      
     
- 
     
      
     
     
      
       value = 
       type(mgr).__enter__(mgr)
      
     
- 
     
      
     
     
      
       exc = True
      
     
- 
     
      
     
     
      
       try:
      
     
- 
     
      
     
     
      
           try:
      
     
- 
     
      
     
     
      
               VAR = value  # Only 
       if 
       "as VAR" is present
      
     
- 
     
      
     
     
      
               BLOCK
      
     
- 
     
      
     
     
      
           except:
      
     
- 
     
      
     
     
      
               # The exceptional 
       case is handled here
      
     
- 
     
      
     
     
      
               exc = False
      
     
- 
     
      
     
     
      
               
       if not exit(mgr, *sys.exc_info()):
      
     
- 
     
      
     
     
      
                   raise
      
     
- 
     
      
     
     
      
               # The exception is swallowed 
       if exit() returns 
       true
      
     
- 
     
      
     
     
      
       finally:
      
     
- 
     
      
     
     
      
           # The normal and non-local-
       goto cases are handled here
      
     
- 
     
      
     
     
      
           
       if exc:
      
     
- 
     
      
     
     
      
               exit(mgr, None, None, None)
      
     
- 
     
      
     
     
       
      
     
有很多网络连接、数据库连接库都会提供 with 功能。甚至熟悉了 with 的实现机制后,可以自行实现 with 功能:
   
    - 
     
      
     
     
      
       class File(object):
      
     
- 
     
      
     
     
      
           def __init__(self, file_name, method):
      
     
- 
     
      
     
     
      
               self.file_obj = open(file_name, method)
      
     
- 
     
      
     
     
      
           def __enter__(self):
      
     
- 
     
      
     
     
      
               
       return self.file_obj
      
     
- 
     
      
     
     
      
           def __exit__(self, 
       type, value, traceback):
      
     
- 
     
      
     
     
      
               self.file_obj.
       close()
      
     
只要定义了 __enter__,__exit__方法,就可以使用 with 语句:
   
    - 
     
      
     
     
      
       with File(
       'demo.txt', 
       'w') as opened_file:
      
     
- 
     
      
     
     
      
           opened_file.write(
       'Hola!')
      
     
五、使用迭代器和生成器
迭代器:iterator 生成器:generator
迭代器和生成器都是 Python 中功能强大的工具,值得精通。迭代器是一个更笼统的概念:任何一个对象只要它所属的类具有__next__方法(Python 2是next)和具有返回 self 的__iter__方法都是迭代器。
每个生成器都是一个迭代器,但反之不然。生成器是通过调用具有一个或多个 yield 表达式的函数而构建的,并且该函数是满足上一段对iterator 的定义的对象。
使用区别:
网络上很多技术博主都说生成器是懒人版的迭代器,比迭代器更节省内存,其实是错误的,他们都很节省内存(我会举例子)。
他们真正的区别是:当你需要一个具有某些复杂的状态维护行为的类,或者想要公开除__next__(和__iter__和__init__)之外的其他方法时,你就需要自定义迭代器,而不是生成器。
通常,一个生成器(有时,对于足够简单的需求,一个生成器表达式)就足够了,并且它更容易编写代码。
比如说计算正整数 a 到 b (b 远远大于 a)直接的平方,生成器的话就是:
   
    - 
     
      
     
     
      
       def squares(start, stop):
      
     
- 
     
      
     
     
      
           
       for i in 
       range(start, stop):
      
     
- 
     
      
     
     
      
               yield i * i
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       generator = squares(a, b)
      
     
或者:
generator = (i*i for i in range(a, b))
如果是迭代器,则是这样:
   
    - 
     
      
     
     
      
       class Squares(object):
      
     
- 
     
      
     
     
      
           def __init__(self, start, stop):
      
     
- 
     
      
     
     
      
              self.start = start
      
     
- 
     
      
     
     
      
              self.stop = stop
      
     
- 
     
      
     
     
      
           def __iter__(self): 
       return self
      
     
- 
     
      
     
     
      
           def __next__(self): # next in Python 
       2
      
     
- 
     
      
     
     
      
              
       if self.start >= self.stop:
      
     
- 
     
      
     
     
      
                  raise StopIteration
      
     
- 
     
      
     
     
      
              current = self.start * self.start
      
     
- 
     
      
     
     
      
              self.start += 
       1
      
     
- 
     
      
     
     
      
              
       return current
      
     
- 
     
      
     
     
       
      
     
- 
     
      
     
     
      
       iterator = Squares(a, b)
      
     
可以看出,迭代器写起来稍麻烦,当也更为灵活,比如你想提供一个 current 方法时,可以直接添加到 Squares 类中:
   
    - 
     
      
     
     
      
           def current(self):
      
     
- 
     
      
     
     
      
              
       return self.start
      
     
从上述可以看出,迭代器并没有保存 a 到 b 之间的所有值,所有并不消耗过多的内存,这一点也可以自行测试,代码如下:
   
    - 
     
      
     
     
      
       >>> from collections.abc 
       import Iterator
      
     
- 
     
      
     
     
      
       >>> from sys 
       import getsizeof
      
     
- 
     
      
     
     
      
       >>> a = [i 
       for i in 
       range(
       1001)]
      
     
- 
     
      
     
     
      
       >>> 
       print(
       type(a))
      
     
- 
     
      
     
     
      
       <class 
       'list'>
      
     
- 
     
      
     
     
      
       >>> 
       print(getsizeof(a))
      
     
- 
     
      
     
     
      
       9016
      
     
- 
     
      
     
     
      
       >>>
      
     
- 
     
      
     
     
      
       >>> b = iter(a)
      
     
- 
     
      
     
     
      
       >>> 
       print(
       type(b))
      
     
- 
     
      
     
     
      
       <class 
       'list_iterator'>
      
     
- 
     
      
     
     
      
       >>> 
       print(isinstance(b,Iterator))
      
     
- 
     
      
     
     
      
       True
      
     
- 
     
      
     
     
      
       >>> 
       print(getsizeof(b))
      
     
- 
     
      
     
     
      
       48
      
     
- 
     
      
     
     
      
       >>> c = (i 
       for i in 
       range(
       1001))
      
     
- 
     
      
     
     
      
       >>> 
       print(getsizeof(b))
      
     
- 
     
      
     
     
      
       48
      
     
- 
     
      
     
     
      
       >>> 
       type(c)
      
     
- 
     
      
     
     
      
       <class 
       'generator'>
      
     
- 
     
      
     
     
      
       >>> 
       type(b)
      
     
- 
     
      
     
     
      
       <class 
       'list_iterator'>
      
     
可以看出 b 是 iterator,c 是 generator,它们占用的内存大小是一样的。
六、善用 itertools
itertools 模块标准化了一个快速、高效利用内存的核心工具集,这些工具本身或组合都很有用。它们一起形成了“迭代器代数”,这使得在纯 Python 中有可以创建简洁又高效的专用工具。比如,如果你想要字符串中所有字符的组合或列表中数字的所有组合,则只需编写
   
    - 
     
      
     
     
      
       from itertools 
       import combinations
      
     
- 
     
      
     
     
      
       names = 
       'ABC'
      
     
- 
     
      
     
     
      
       for combination in combinations(names, 
       2):
      
     
- 
     
      
     
     
      
           
       print(combination)
      
     
- 
     
      
     
     
      
       ''
       ' Output -
      
     
- 
     
      
     
     
      
           ('A
       ', 'B
       ')
      
     
- 
     
      
     
     
      
           ('A
       ', 'C
       ')
      
     
- 
     
      
     
     
      
           ('B
       ', 'C
       ')
      
     
- 
     
      
     
     
      
       '
       ''
      
     
这是一个值得经常使用的标准库,更多详细功能请参考官方文档:https://docs.python.org/zh-cn/3/library/itertools.html[1]
七、善用 collections
这又是一个值得使用的标准库 collections,它提供替代内置的数据类型的多个容器,如 defaultdict、OrderedDict、namedtuple、Counter、deque 等,非常使用,而且比自己实现要安全稳定的多。比如:
   
    - 
     
      
     
     
      
       # frequency of all characters in a 
       string in sorted order
      
     
- 
     
      
     
     
      
       from collections 
       import (OrderedDict, Counter)
      
     
- 
     
      
     
     
      
       string = 
       'abcbcaaba'
      
     
- 
     
      
     
     
      
       freq = Counter(
       string)
      
     
- 
     
      
     
     
      
       freq_sorted = OrderedDict(freq.most_common())
      
     
- 
     
      
     
     
      
       for key, val in freq_sorted.items():
      
     
- 
     
      
     
     
      
           
       print(key, val)
      
     
- 
     
      
     
     
      
       ''
       ' Output -
      
     
- 
     
      
     
     
      
           ('a
       ', 4)
      
     
- 
     
      
     
     
      
           ('b
       ', 3)
      
     
- 
     
      
     
     
      
           ('c
       ', 2)
      
     
- 
     
      
     
     
      
       '
       ''
      
     
不多说了,看官方文档:https://docs.python.org/3/library/collections.html[2]
八、不要过度使用类
不要过度使用类。坚持用 Java 和 C ++ 的程序员会经常使用类,但是在使用 Python 时,可以在函数和模块的帮助下复用代码。除非绝对需要,否则不必创建类。
本文讲述类 8 个让你写出更好 Python 代码的方法,希望对你有所帮助。
推荐阅读:
参考资料
[1]
https://docs.python.org/zh-cn/3/library/itertools.html: https://docs.python.org/zh-cn/3/library/itertools.html
[2]https://docs.python.org/3/library/collections.html: https://docs.python.org/3/library/collections.html
转载:https://blog.csdn.net/somenzz/article/details/109685295
