六星源课堂:需要注意的常见 Python 反模式
反模式可以让你在编写代码时更加专心。
照片由Markus Spiske在
Unsplash上拍摄
在软件中,反模式是一个描述如何不解决代码中重复出现的问题的术语。反模式被认为是糟糕的软件设计,通常是无效或模糊的修复。
以下是我遇到/纠正的一些 Python 反模式。了解这些反模式将帮助您在自己的代码中避免它们,并使您成为更好的程序员(希望如此)。
不对文件使用上下文管理器
这是我在众多代码审查中观察到的最常见的反模式。python 中的上下文管理器有助于促进对资源的正确处理,以控制在创建或销毁对象时要执行的操作。这消除了处理对象创建和删除的开销。
坏的
file_obj = open('abc.txt', 'r')
数据 = file_obj.read()
file_obj.close()
好的
使用 open('abc.txt', 'r') as file_obj:
data = file_obj.read()
使用 type() 比较类型
isinstance()通常是比较类型的首选方法。它不仅更快,而且还考虑了继承,这通常是所需的行为。在 Python 中,您通常想要检查给定对象的行为是否像字符串或列表,不一定是字符串。因此,您无需检查字符串及其所有自定义子类,只需使用isinstance.
名称 = '亚历克斯'if type(name)== str:
print('It is a string')
else:
print('It is not a string')
执行器的使用
该exec语句使您能够动态执行存储在文字字符串中的任意 Python 代码。构建一个复杂的 Python 代码字符串,然后将该代码传递给难以阅读和测试的代码。任何时候Use of exec遇到错误,你都应该回到代码中,检查是否有更清晰、更直接的方法来完成任务。
s = "print(\"你好,世界!\")"执行者
def print_word():
print("你好,世界!")
打印字()
未指定异常类型
不指定异常类型可能不仅会隐藏错误,还会导致丢失有关错误本身的信息。因此,最好使用适当的错误而不是通用异常来处理这种情况,否则如下所示。
尝试:
5 / 0
除外:
打印(“异常”)
使用这种模式,您可以根据它们的实际异常类型来处理异常。首先处理与当前错误匹配的第一个异常类型。因此,建议首先处理特定的异常类型(例如 ZeroDivisionError),然后在 try-except 块的末尾处理通用错误类型(例如 Exception)。
使用 map() 或 filter() 代替列表理解
对于可以表示为列表推导的简单转换,最好使用列表推导而不是map()映射filter()和过滤器,用于太长或太复杂而无法用列表推导表达的表达式。尽管 a or表达式在功能上可能等同于列表推导式,但列表推导式通常更简洁且更易于阅读。
值 = [1, 2, 3]doubles = map(lambda num: num * 2, values)
值 = [1, 2, 3]doubles = [num * 2 for num in values]
在 for 循环中适当的地方不使用 else
Python 为 for 循环提供了内置的 else 子句。如果 for 循环在没有被 break 或 return 语句提前中断的情况下完成,则执行循环的 else 子句。
numbers = [1, 2, 3]
n = 4
found = False
for num in numbers:
if num == n:
found = True
print("Number found")
break
if not found:
print("Number not found")
使用单字母变量名
l = [2,3,4,5]
数字 = [2,3,4,5]
请求许可而不是宽恕
Python 社区使用 EAFP(请求宽恕比许可更容易)编码风格。这种编码风格假定存在所需的变量、文件等。任何问题都会被捕获为异常。这导致了包含大量try和except语句的通常干净简洁的样式。
导入 os my_file = "/path/to/my/abc.txt"
以错误的方式与无/布尔值进行比较
数字 = 无
标志 = 真
if number == None or flag == True:
print("这可行,但不是首选的 PEP 8 模式")
如果 number 为 None 或 flag 为 True:
print("PEP 8 Style Guide 更喜欢这种模式")
使用通配符导入
从集合导入 *
从集合导入计数器
不使用 zip() 遍历一对列表
数字 = [1, 2, 3]
字母 = ["A", "B", "C"]
范围内的索引(len(数字)):
打印(数字[索引],字母[索引])
对于数字值,zip中的字母值(数字,字母):
打印(数字值,字母值)
使用列表中的键检查键是否包含在列表中
使用key in list迭代列表可能需要n迭代才能完成,其中是列表中的项目数。
如果可能,您应该将列表更改为集合或字典,因为 Python 可以通过尝试直接访问它们而无需迭代来搜索集合或字典中的项目,这更有效(因为 set 在 python 中实现为哈希访问元素的表和时间复杂度为 O(1))。
如果您的列表有重复项并且您想要查找所有出现的元素,则此方法不适用。
l = [1, 2, 3, 4]
s = {1, 2, 3, 4}
不get()用于从字典中返回默认值
您经常会看到代码创建一个变量,为该变量分配一个默认值,然后检查字典中的某个键。如果键存在,则将键的值复制到变量的值中。dict.get(key[, default])虽然这并没有错,但使用Python 标准库中的内置方法会更简洁。如果该键存在于字典中,则返回该键的值。如果不存在,则返回指定为第二个参数的默认值。
股票= {'AMZ':'亚马逊','AAPL':'Apple'}如果股票中的'AMC':股票
名称=股票['AMC']
否则:
股票名称='未定义'
股票= {'AMZ':'亚马逊','AAPL':'苹果'}stock_name = stock.get('AMC', '未定义')
不使用显式解包
拆包更简洁,更不容易出错,因为手动将多个变量分配给列表的元素更加冗长和繁琐。
汽车 = ['BMW', '马自达', '特斯拉'] car_1 = 汽车[0]
car_2 = 汽车[1]
car_3 = 汽车[2]
汽车 = ['宝马', '马自达', '特斯拉'] car_1,car_2,car_3 = 汽车
不在循环中使用 enumerate()
创建一个使用递增索引来访问循环构造中列表的每个元素的循环并不是访问列表中每个元素的首选样式。首选样式是用于enumerate()同时检索索引和列表元素。
汽车 = ['宝马','马自达','特斯拉']对于范围内的 ind(len(cars)):
print(ind,cars[ind])
对于 ind,car 枚举(汽车):
打印(ind,汽车)
以上为本次分享的全部内容,如果对编程想获得更多了解,请前往六星源课堂,开启你的编程之旅~