改善 Python 程序的 91 个建议(二)

Linux编程

点击右侧关注,免费入门到精通!

作者丨驭风者

https://zhuanlan.zhihu.com/p/26162701

建议 24:遵循异常处理的几点基本原则

异常处理的几点原则:

1.注意异常的粒度,不推荐在 try 中放入过多的代码

2.谨慎使用单独的 except 语句处理所有异常,最好能定位具体的异常

3.注意异常捕获的顺序,在适合的层次处理异常,Python 是按内建异常类的继承结构处理异常的,所以推荐的做法是将继承结构中子类异常在前抛出,父类异常在后抛出

4.使用更为友好的异常信息,遵守异常参数的规范

建议 25:避免 finally 中可能发生的陷阱

当 finally 执行完毕时,之前临时保存的异常将会再次被抛出,但如果 finally 语句中产生了新的异常或执行了 return 或 break 语句,那么临时保存的异常将会被丢失,从而异常被屏蔽。

在实际开发中不推荐 finally 中使用 return 语句进行返回。

建议 26:深入理解 None,正确判断对象是否为空

类型FalseTrue布尔False (与0等价)True (与1等价)字符串”"( 空字符串)非空字符串,例如 ” “, “blog”数值0, 0.0非0的数值,例如:1, 0.1, -1, 2容器[], (), {}, set()至少有一个元素的容器对象,例如:[0], (None,), ['']NoneNone非None对象

#3执行中会调用__nonzero__()来判断自身对象是否为空并返回0/1或True/False,如果没有定义该方法,Python 将调用__len__()进行判断,返回 0 表示为空。如果一个类既没有定义__len__()又没有定义__nonzero__(),该类实例用 if 判断为True。

建议 27:连接字符串优先使用 join 而不是 +

这一点之前我在博文里总结过,+涉及到更多的内存操作。

建议 28:格式化字符串时尽量使用 .format 而不是 %

同上。

建议 29:区别对待可变对象和不可变对象

Python 中一切皆对象,每个对象都有一个唯一的标识符(id)、类型(type)和值。数字、字符串、元组属于不可变对象,字典、列表、字节数组属于可变对象。

默认参数在初始化时仅仅被评估一次,以后直接使用第一次评估的结果,course 指向的是 list 的地址,每次操作的实际上是 list 所指向的具体列表,所以对于可变对象的更改会直接影响原对象。

最好的方法是传入None作为默认参数,在创建对象的时候动态生成列表。

建议 30:[]、() 和 {} 一致的容器初始化形式

其实就是列表生成式、元组生成式和字典生成式。

建议 31:记住函数传参既不是传值也不是传引用

正确的说法是传对象(call by object)或传对象的引用(call-by-object-reference),函数参数在传递过程中将整个对象传入,对可变对象的修改在函数外部以及内部都可见,对不可变对象的”修改“往往是通过生成一个新对象然是赋值实现的。

建议 32:警惕默认参数潜在的问题

其中就是默认参数如果是可变对象,在调用者和被调用者之间是共享的。

建议 33:慎用变长参数

原因如下:

1.使用过于灵活,导致函数签名不够清晰,存在多种调用方式

2.使用*args和**kw简化函数定义就意味着函数可以有更好的实现方法

使用场景:

1.为函数添加一个装饰器

2.参数数目不确定

3.实现函数的多态或子类需要调用父类的某些方法时

建议 34:深入理解 str() 和repr() 的区别

总结几点:

1.str()面向用户,返回用户友好和可读性强的字符串类型;repr()面向 Python 解释器或开发人员,返回 Python 解释器内部的含义

2.解释器中输入a默认调用repr(),而print(a)默认调用str()

3.repr()返回值一般可以用eval()还原对象:obj == eval(repr(obj))

4.以上两个方法分别调用内建的__str__()和__repr__(),一般来说类中都应该定义__repr__(),但当可读性比准确性更为重要时应该考虑__str__(),用户实现__repr__()方法的时候最好保证其返回值可以用eval()是对象还原

建议 35:分清 staticmethod 和 classmethod 的适用场景

这两种方法之前已经总结过了的,下面我们只讨论它们的使用场景。

调用类方法装饰器的修饰器的方法,会隐式地传入该对象所对应的类,可以动态生成对应的类的类变量,同时如果我们期望根据不同的类型返回对应的类的实例,类方法才是正确的解决方案。

反观静态方法,当我们所定义的方法既不跟特定的实例相关也不跟特定的类相关,可以将其定义为静态方法,这样使我们的代码能够有效地组织起来,提高可维护性。

当然,也可以考虑定义一个模块,将一组的方法放入其中,通过模块来访问。

第四章库

建议 36:掌握字符串的基本用法

下面来介绍一些易混淆的地方:

建议 37:按需选择 sort() 或者 sorted()

所以如果实际过程中需要保留原有列表,可以使用sorted()。sort()不需要复制原有列表,消耗内存较小,效率较高。同时传入参数key比传入参数cmp效率要高,cmp传入的函数在整个排序过程中会调用多次,而key针对每个元素仅作一次处理。

建议 38:使用 copy 模块深拷贝对象

浅拷贝(shallow copy):构造一个新的复合对象并将从原对象中发现的引用插入该对象中。工厂函数、切片操作、copy 模块中的 copy 操作都是浅拷贝

深拷贝(deep copy):针对引用所指向的对象继续执行拷贝,因此产生的对象不受其它引用对象操作的影响。深拷贝需要依赖 copy 模块的 deepcopy() 操作

在 python 中,标识一个对象唯一身份的是:对象的id(内存地址),对象类型,对象值,而浅拷贝就是创建一个具有相同类型,相同值但不同id的新对象。因此使用浅拷贝的典型使用场景是:对象自身发生改变的同时需要保持对象中的值完全相同,比如 list 排序:

深拷贝不仅仅拷贝了原始对象自身,也对其包含的值进行拷贝,它会递归的查找对象中包含的其他对象的引用,来完成更深层次拷贝。因此,深拷贝产生的副本可以随意修改而不需要担心会引起原始值的改变:

使用 _copy_ 和 __deepcopy__ 可以完成对一个对象拷贝的定制。

参考博文

http://wecatch.me/blog/2016/06/18/python-copy-deepcopy/

建议 39: 使用 Counter 进行计数统计

常见的计数统计可以使用dict、defaultdict、set和list,不过 Python 提供了一个更优雅的方式:

Counter 类属于字典类的子类,是一个容器对象,用来统计散列对象,支持+、-、&、|,其中&和|分别返回两个 Counter 对象各元素的最小值和最大值。

建议 40:深入掌握 ConfigParser

几乎所有的应用程序都会读取配置文件,ini是一种比较常见的文件格式:

Python 提供标准库 ConfigParser 来支持它:

再来看个SQLAlchemy配置文件的例子:

推荐↓↓↓

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181028B18PAA00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券