编写高质量代码改善Python程序的很多建议

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_38682860/article/details/85993541

基础语法

有节制地使用 from...import 语句

Python 提供三种方式来引入外部模块:import语句、from...import语句以及__import__函数,其中__import__函数显式地将模块的名称作为字符串传递并赋值给命名空间的变量。

使用import需要注意以下几点:

  • 优先使用import a的形式

  • 有节制地使用from a import A

  • 尽量避免使用from a import *

为什么呢?我们来看看 Python 的 import 机制,Python 在初始化运行环境的时候会预先加载一批内建模块到内存中,同时将相关信息存放在sys.modules中,我们可以通过sys.modules.items()查看预加载的模块信息,当加载一个模块时,解释器实际上完成了如下动作:

  1. 在sys.modules中搜索该模块是否存在,如果存在就导入到当前局部命名空间,如果不存在就为其创建一个字典对象,插入到sys.modules中

  2. 加载前确认是否需要对模块对应的文件进行编译,如果需要则先进行编译

  3. 执行动态加载,在当前命名空间中执行编译后的字节码,并将其中所有的对象放入模块对应的字典中

编写高质量代码改善Python程序的很多建议

从上可以看出,对于用户自定义的模块,import 机制会创建一个新的 module 将其加入当前的局部命名空间中,同时在 sys.modules 也加入该模块的信息,但本质上是在引用同一个对象,通过test.py所在的目录会多一个字节码文件。

优先使用 absolute import 来导入模块

i+=1 不等于 ++i

首先++i或--i在 Python 语法上是合法,但并不是我们通常理解的自增或自减操作:

编写高质量代码改善Python程序的很多建议

原来+或-只表示正负数符号。

使用 with 自动关闭资源

对于打开的资源我们记得关闭它,如文件、数据库连接等,Python 提供了一种简单优雅的解决方案:with。

先来看with实现的原理吧。

with的实现得益于一个称为上下文管理器(context manager)的东西,它定义程序运行时需要建立的上下文,处理程序的进入和退出,实现了上下文管理协议,即对象中定义了__enter__()和__exit__(),任何实现了上下文协议的对象都可以称为一个上下文管理器:

  • __enter__():返回运行时上下文相关的对象

  • __exit__(exception_type, exception_value, traceback):退出运行时的上下文,处理异常、清理现场等

with 表达式 [as 目标]:代码块

包含with语句的代码块执行过程如下:

编写高质量代码改善Python程序的很多建议

  1. 计算表达式的值,返回一个上下文管理器对象

  2. 加载上下文管理器对象的__exit__()以备后用

  3. 调用上下文管理器对象的__enter__()

  4. 将__enter__()的返回值赋给目标对象

  5. 执行代码块,正常结束调用__exit__(),其返回值直接忽略,如果发生异常,会调用__exit__()并将异常类型、值及 traceback 作为参数传递给__exit__(),__exit__()返回值为 false 异常将会重新抛出,返回值为 true 异常将被挂起,程序继续执行

于此,我们可以自定义一个上下文管理器:

编写高质量代码改善Python程序的很多建议

Python 还提供contextlib模块,通过 Generator 实现,其中的 contextmanager 作为装饰器来提供一种针对函数级别上的上下文管理器,可以直接作用于函数/对象而不必关心__enter__()和__exit__()的实现。

使用 else 子句简化循环(异常处理)

Python 的 else 子句提供了隐含的对循环是否由 break 语句引发循环结束的判断,有点绕哈,来看例子:

编写高质量代码改善Python程序的很多建议

可以看出,else 子句在循环正常结束和循环条件不成立时被执行,由 break 语句中断时不执行,同样,我们可以利用这颗语法糖作用在 while 和 try...except 中。

遵循异常处理的几点基本原则

异常处理的几点原则:

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

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

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

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

避免 finally 中可能发生的陷阱

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

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

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

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

编写高质量代码改善Python程序的很多建议

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

连接字符串优先使用 join 而不是 +

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

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

同上。

区别对待可变对象和不可变对象

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

编写高质量代码改善Python程序的很多建议

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

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

编写高质量代码改善Python程序的很多建议

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

编写高质量代码改善Python程序的很多建议

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

记住函数传参既不是传值也不是传引用

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

警惕默认参数潜在的问题

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

编写高质量代码改善Python程序的很多建议

慎用变长参数

  • 原因如下:

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

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

  • 使用场景:

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

  2. 参数数目不确定

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

深入理解 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()是对象还原

分清 staticmethod 和 classmethod 的适用场景

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

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

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

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

猜你喜欢

转载自blog.csdn.net/sinat_38682860/article/details/85993541
今日推荐