写点什么

Python 开发人员常犯的这 7 个错误,可能是“致命”的

  • 2020-10-28
  • 本文字数:2402 字

    阅读完需:约 8 分钟

Python 开发人员常犯的这7个错误,可能是“致命”的

本文最初发表于 Towards Data Science 博客,经原作者 Anupam Chugh 授权,InfoQ 中文站翻译并分享。


Python 无疑是当今使用最广泛的编程语言。它之所以如此流行,很大程度上是由于简单的语法和可读性,这使得它非常容易使用。初学者之所以喜欢 Python,是因为它给人一种感觉就像是用英语写一段伪代码一样。


但是,无论你有多么丰富的经验,也无论你已经研究过多少种语言,切换到 Python 并不能保证你能做到平稳过渡。具有面向对象编程背景的开发人员很容易忽略 Python 的惯用特性。这样一来,他们很可能会误用编程结构,从而可能出现难以发现的、不可预见的错误。更糟糕的是,这些错误大多很难发现,并且可能会给生产带来麻烦。


在本文中,我将列出程序员(通常是菜鸟)可能会犯的常见错误。此外,我还将介绍如何避免这些错误,以便编写出更好的、无错误的 Python 代码。闲话少叙,言归正题。

错误一:编写过于风格化的代码

这是 Python 初学者的一个典型特征。为了编写类似伪英语的高级代码,他们的代码库中会出现以下类型的代码片段:


if x == 1 or 2
复制代码


奇怪的是,这可能看起来并不那么糟糕。基本上,这段代码的意思是变量 x 需要为 1 或 2 才能满足条件。但是,这样的代码片段会过于风格化,并会带来可读性问题。


下面这段替代代码,检查列表中的值,更容易让人理解。


if x in [1,2]
复制代码

错误二:无必要的比较运算符:None 和 Zero

具有 Java 背景的程序员都知道需要多少 null 检查(特别是在 Java 8 之前的版本中)。所以,在 Python 中,使用像下面这样的比较运算符也就不足为奇了:


a == Noneb != None
复制代码


但在上面的例子中,实际上,我们可以利用 Python 编写代码的方式来增强可读性:


a is Noneb is not None
复制代码


同样,对于 0,值得注意的是,在条件逻辑中实际上并不需要使用比较运算符。0 被解释为 false,而非零数字被视为 true。

错误三:使用长长的条件按位逻辑链

在大多数语言(包括 Swift、Java、Kotlin)中,我们都知道要用以下方式编写某些比较逻辑:


if a < b and b < c
复制代码


与不能在非关联优先级中使用相邻运算符的大多数语言不同,Python 提供了编写链式赋值的能力,如下段代码所示:


if a < b < c
复制代码


这样写,就可以避免按位运算符。

错误四:使用 type() 代替 isinstance(),反之亦然

typeisinstance()是 Python 中用于类型检查的两个广泛使用的内置函数。


通常,开发人员刚入门 Python 时,会将这两个函数视为相似的函数,并将它们互换着使用。现在,由于type()isinstance()有一些细微的区别,这可能会埋下无法预料的错误。


isinstance()函数用于检查对象是否为指定类的实例,同时还要负责继承。另一方面,type()只检查引用类型是否相同并丢弃子类型。


因此,下面的代码使用type()isinstance()分别得到了不同的结果:


class Vehicle:passclass Car(Vehicle):passisinstance(Car(), Vehicle) # returns Truetype(Car()) == Vehicle # returns False
复制代码


类似地,下面的代码将布尔值视为 int 的实例(因为 true 和 false 基本上被视为 1 和 0),但是使用不同的类型函数就给出了不同的结果。


type(True) == int # falseisinstance(True, int) # trueisinstance(False, int) # true
复制代码


因此,理解 Python 的这两种类型检查器函数之间的区别是很重要的,不要将它们相互混淆。

错误五:混淆作用域中的局部变量和全局变量

Python 中的作用域规则看起来非常简单,但很容易被误解。例如,以下代码在函数中使用了全局变量:


a = 10def printMe():print(a)printMe() # prints 10
复制代码


现在,如果我们只是通过修改函数中的变量来稍微调整上面的代码,就会得到一个错误的结果:


a = 20def printA():print(a)a = 10print(a) # gives 20printA() # gives error as a is referenced before assigned
复制代码


一旦我们修改了函数中的全局变量,Python 就会将其视为局部变量,从而遮蔽了全局变量。甚至在复制之前的 print 语句也没有执行。


为确保这种名称冲突不会导致错误,我们可以在局部函数中的全局变量后面附加一个global关键字。更好的做法是将全局变量(如果你实际需要使用的话)放在一个单独的类中,这样,你就可以始终使用带有类名的全局变量了。

错误六:可变的默认参数

使用默认参数,是 Python 中的一种常见习惯。它有助于防止在调用时函数中出现一长串参数。


现在,在 Python 中,列表、字典和集合都是可变类型。因此,设置默认值可能会导致意外的结果,如下所示:


def addToList(x, a=[]):a.append(x)return alistOne = addToList(5)#prints [5]anotherList = addToList(10)# [5, 10]
复制代码


如你所见,第二个列表包含之前添加的元素,因为函数中的可变默认参数跨状态存储这些元素。


Python 中可变默认对象的问题在于,它是在定义函数时计算的。这会导致变异值也保留之前的内容。


为避免此类严重错误,请将None设置为默认值,并在函数中分配可变变量,如下所示:


def addElement(x, a=None):if not a:a = []a.append(x)return a
复制代码

错误七:忽略多重集成和方法解析顺序

与大多数编程语言不同,Python 支持多重继承。这意味着,在具有继承的类中,方法和类变量是根据继承类时指定的顺序执行的。


初学者往往会忽略这个概念,尤其是当他们只使用单一继承时。


因此,在下面这段代码中,当调用类 C 的方法时,将使用超类 B 各自的方法:


>>> class A(object):...     def me(self):print("class A")>>> class B(A):...     def me(self):print("class B")class C(B, A):passc = C()c.me() # prints class B
复制代码


因此,Python 中继承类的顺序很重要,因为它是用来解析方法的。

总结

Python 使用起来似乎非常简单,但是人们很容易被来自其他编程语言的概念所困扰,这就会导致奇怪的错误和崩溃。我希望本文所列举的错误能够帮助你厘清概念,从而写出健壮的 Python 代码。


作者介绍:


Anupam Chugh,视技术和代码为毕生追求。拥有 200 万阅读量的作家,现为 iOS 开发者。


原文链接:


https://towardsdatascience.com/7-deadly-sins-python-developers-do-eb53d8a880


2020-10-28 14:392088
用户头像
刘燕 InfoQ高级技术编辑

发布了 1112 篇内容, 共 516.8 次阅读, 收获喜欢 1972 次。

关注

评论 1 条评论

发布
用户头像
代码缩进可以调整下
2020-10-29 10:57
回复
没有更多了
发现更多内容

架构师训练营第十周总结

吴传禹

极客大学架构师训练营

第10周 作业1

Yangjing

极客大学架构师训练营

架构1期 第十周作业

haha

第六周作业

Jack

动态规划解决爬楼梯算法,彻底搞懂AppStore证书体系、彻底搞懂控制反转IoC,依赖注入DIP, John 易筋 ARTS 打卡 Week 28

John(易筋)

ARTS 打卡计划 动态规划解决爬楼梯 AppStore证书体系 控制反转IOC 依赖注入DIP

架构师训练营第 1 期 -- 第十周学习总结

发酵的死神

极客大学架构师训练营

区块链将如何改变住房市场

CECBC

区块链 住房记录

哪些数据将成为区块链系统的关键数据?

CECBC

区块链

架構師訓練營 week10 總結

ilake

Python进阶——什么是迭代器?

Kaito

Python

南海将打造“区块链+”金融科技产业高地

CECBC

区块链 金融

互联网络上遇到数据延迟维护清算中取不出该怎么解决?

Geek_a6658e

【第九周】性能优化(三)

云龙

深入掌握底层源码常见的 CAS 原子编程

马丁玩编程

架构 CAS

第10周 作业2

Yangjing

极客大学架构师训练营

MYSQL数据类型

少林寺三毛

MySQL

架构师入门学习之感悟六

笑春风

【第九周】课后作业

云龙

架構師訓練營 week10 作業

ilake

架构师训练营第十周作业

架构师训练营第六周作业2

韩儿

架构师训练营第十周作业

吴传禹

极客大学架构师训练营

【第十周】模块分解

云龙

模块拆分第十周作业「架构师训练营第 1 期」

天天向善

Logstash使用

Rayzh

ELK

极客时间架构师培训 1 期 - 第 10 周作业

Kaven

微服务手册:API接口9个生命节点,构建全生命周期管理

互联网应用架构

微服务 APi设计 API网关

架构师训练营 - 第十周作业

一个节点

极客大学架构师训练营

架构师训练营 - 第十周总结

一个节点

极客大学架构师训练营

第五周作业

Jack

极客时间架构师训练营 1 期 - 第 10 周总结

Kaven

Python 开发人员常犯的这7个错误,可能是“致命”的_AI&大模型_Anupam Chugh_InfoQ精选文章