你应该避免的18种常见 Python“反设计模式”

从优美的代码中可以学到很多东西,从糟糕的代码中也一样可以。

编者按:编程也需要养成良好的习惯,遵循正确的规范。过分的炫技和糟糕的习惯都要不得,那会令你的代码难以识别和维护,最终成为一个谁都不愿意接手的烂摊子。无论你是初学者还是有了一定经验的开发者,了解这些不好的反设计模式(Anti-Pattern)都会有帮助。本文译自Medium,作者 Ahmed Besbes,原标题为“18 Common Python Anti-Patterns I Wish I Had Known Before”。

我使用Python编程已经六年多了,直到现在我还在学习新的程序设计理念和优秀的方法,以期提高我的代码质量。

刚开始的时候并不容易,我付出了很多努力。

在这篇文章中我整理出了18种Python反设计模式和最糟糕的Python编程案例:了解它们可能会为你节省很多时间,让你在调试项目时能尽快上手。我真心希望以前就有人能教给我这些。

我列出的这份清单是基于与其他Python开发者、编程专家的讨论,还有在Reddit网站上长期收集来的信息。

但这并不是一个非常完善的列表:如果你想深入研究这个话题(我发誓这是一个非常有趣的话题),我在最后提供了一些参考资料。

0-什么是反设计模式?

反设计模式是软件开发中的某些不好的编程实践。

相反,设计模式则是解决常见问题的通用方法,已经被正式普遍采用,一般被认为是良好的开发实践,反设计模式与之相反,是不可取的。

反设计模式会使代码难以阅读,难以维护,运行速度慢,工程量过大,运行不稳定,容易出错,而且会有潜在的安全问题。

产生反设计模式有很多原因:

  • 缺乏代码审查
  • 总想尝试“酷炫”的方法,其实简单的方法就可以实现
  • 没有使用正确的工具(比如遵循PEP8惯例的代码编译器和格式化程序、文档字符串生成器、支持自动完成的IDE,等等)
  • 或者仅仅是不知道有更好的选择

反设计模式会导致以下的某个或者多个负面结果:

  1. 影响正确性:反设计模式会破坏你的代码或得到错误的运行结果。
  2. 影响可维护性:反设计模式会使你的代码难以维护或扩展。
  3. 影响可读性:反设计模式会使你的代码难以阅读或理解。
  4. 影响性能:反设计模式会不必要地减慢你的代码运行速度。
  5. 影响安全性:反设计模式会给你的程序带来安全风险

现在让我们看看18种反设计模式都是哪些:

1 - 使用意义不明确的变量名

变量名应该始终是描述性的,以提供最基本的背景:变量名应该从字面上能说明这个变量代表的是什么意义。

这样你的代码对其他开发者来说更容易理解,对你自己来说则更容易调试。

提示👉:不要害怕使用长的变量名,现代IDE(如VSCode和Pycharm)均提供了快速自动完成功能。

2 - 忽略注释

没有文档的代码是个噩梦,它可能会给这些人带来棘手的问题:

6个月后的你,你会忘记自己当时为什么要写这行代码

接手这个项目的你的同事

代码应该始终很清晰,应该用注释说明你为什么要这样做。同时你写注释要简明扼要。如果你的代码不言自明,就不需要注释了。

提示👉:如果你使用VSCode,你可以用这个扩展来快速生成文档字符串,它可以为你的类和函数自动生成一个注释模板。

3 - 忘记更新注释

与代码相矛盾的注释比没有注释更糟糕。

这种情况其实挺多的:由于时间紧张你匆忙修复了一个出问题的程序,可能就没有去更新旧的注释。

一个过期的注释会误导看到这段代码的所有人。

总是有时间来更新注释的,关键看你是否重视这个问题。

4 - 在函数名中使用CamelCase(“骆驼拼写法”,是指在英语中,依靠单词的大小写拼写复合词的做法。)

惯例是这样的:PEP 8风格指南建议,函数名应该采用小写字母,单词之间用下划线连接。

5 - 不直接迭代迭代器中的元素

这是一个很常见的反设计模式。如果你不需要迭代器中的元素的索引,你不一定需要迭代它们。你可以直接迭代这些元素。

这会使你的代码更简洁地道。

6 - 当你需要元素和它的索引时不使用枚举

当你需要在迭代器上同时访问一个元素和它的索引时,请使用枚举。

7 - 不使用zip来迭代成对的列表

zip是一个很有用的内置函数,它允许你从两个迭代器创建一个元组列表。每个元组的第一个元素来自第一个迭代器,而第二个元素来自第二个迭代器。

如果你想同时对两个或更多的迭代器进行迭代,zip会很有帮助。

8 - 读取或写入文件时不使用上下文管理器

当你使用了open语句而没使用上下文管理器时,如果在你关闭文件之前发生了一些异常(关闭文件是你在以这种方式打开文件时必须记住的事情),可能会发生内存故障,文件可能会被损坏。

当你使用with语句打开一个文件并发生异常时,Python会保证文件被正常关闭。

9 - 使用 in 来检查一个元素是否包含在一个 (大) 列表中

使用 in 语句检查一个元素是否包含在一个很大的列表中时,运行速度可能会很慢。可以考虑使用 set 或 bisect 来代替 in。

下面是这三种方法的比较。

10 - 向函数传递易变的默认参数(比如一个空的列表)

Python中有一个有趣的地方,可能会导致错误和不明显的bug:默认参数在定义函数时会被评估一次,而不是在每次调用函数时被评估。

这意味着如果你使用了一个可变的默认参数(比如一个列表)并对其进行了改变,那么在以后对该函数的所有调用中,你也将并且已经改变了该对象。

为了避免这个问题,你可以把默认参数 to 设置为None。

如果函数被多次调用,而 to 被设置为 None,那么就会创建一个新的空列表,并且每次都将元素追加到其中。

当你把一个列表传给 to 时,就会把一个元素附加到它上面。因为它不是默认的函数参数,所以这样做效果很好。

11 - 在一个函数中返回不同的类型

当处理可能产生错误的特殊用户输入时,你有时可以引入 None 作为输出。这会使你的代码前后不一致,因为这个函数现在至少返回两种类型:你预设的初始类型和NoneType类型。

这使得以后很难进行测试和调试。

与其返回None,你可以引发一个错误,然后再捕捉它。

12 - 简单的for循环就够了,却使用while循环

如果你事先已经知道了迭代的次数,你就不需要使用while循环。

13 - 使用多层嵌套的if语句

多层嵌套的if语句会使人很容易搞错代码逻辑。

你可以用布尔运算符来代替多层嵌套条件语句,把它们结合起来。

14 - 使用全局变量

尽量不要使用全局变量。它们可能导致许多错误。全局变量可以从程序的多个部分同时访问,这很容易出错。

使用全局变量时会出现的典型错误是当一个函数在另一个函数需要正常更新数值之前就访问了其值。

15 - 不使用 get() 从字典中返回默认值

当你使用 get 时,python 会检查指定的 key 是否存在于 dictionary 中。如果它存在,那么 get() 将返回该键的值。如果该键不存在,get()返回第二个参数中指定的值。

16 - 使用不能有意义地处理异常的 try/except 块

应该避免使用try/except块并通过传递异常来忽略异常的模式。

17 - 喜欢使用from module import *

导入的数据应该始终是具体的。从模块中导入*是一种非常不好的做法,会污染命名空间。

18 -简单问题复杂化

类并不总是必要的。很多时候简单的函数就足够用了。

类通常有方法,这些方法是与特定类相关的函数,实现与类的对象相关的功能;但如果你想做的只是一些不那么复杂的事情,一个函数就足够了。

基本上,类是将函数(作为方法)和数据(作为属性)组合成一个围绕特定事物的逻辑实体的一种方式。如果你不需要这种分组,那么就没有必要去定义一个类。

参考资料:
https://docs.quantifiedcode.com/python-anti-patterns/index.html

https://deepsource.io/blog/8-new-python-antipatterns/

https://github.com/quantifiedcode/python-anti-patterns

https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments

https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments

https://towardsdatascience.com/data-scientists-your-variable-names-are-awful-heres-how-to-fix-them-89053d2855b

来源:36kr  译者:张茉茉

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值