写点什么

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:392154
用户头像
刘燕 InfoQ高级技术编辑

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

关注

评论 1 条评论

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

自助洗车怎么加盟?加盟流程介绍

共享电单车厂家

自助洗车加盟 自助洗车怎么加盟 自助洗车加盟流程

MOSN 1.0 发布,开启新架构演进

SOFAStack

开源 开发者 云原生 资讯 Go 语言

中国科技人,在残酷春天里翘首等待

脑极体

如何做好高并发系统设计,我总结了三点

架构精进之路

架构 高并发 4月日更 4月月更

一起看看自助洗车机投放场地怎么选

共享电单车厂家

自助洗车加盟 自助洗车机投放 自助洗车场地

超全MySQL笔记整理(面试题+笔记+思维导图),面试再也不怕被MySQL难倒了

Java架构追梦

Java java面试 后端开发

共建开源组件生态 2022 OpenHarmony组件大赛等你来

科技汇

火山引擎联合中国信通院发布《边缘云总体技术与测试要求》

火山引擎边缘云

边缘计算 标准化

从“打地鼠”到“造城池”,华为应用市场带来的安全隐私保护新范式

脑极体

暴打力扣:王者级《数据结构与算法笔记》,一路绿灯进字节Java岗

Java架构追梦

Java 算法 java面试 后端开发

免费ETL批量调度,数据仓库运维工具TASKCTL 8.0 环境使用安装

敏捷调度TASKCTL

大数据 DevOps 敏捷开发 ETL 自动化运维

火山引擎荣获中国信通院“2021边缘计算融合创新奖”

火山引擎边缘云

边缘计算 中国信通院

“消息驱动、事件驱动、流 ”基础概念解析

阿里巴巴云原生

阿里云 云原生 事件总线 EventBridge

自助共享洗车加盟都有什么条件

共享电单车厂家

自助洗车加盟条件 自助共享洗车加盟

Laxcus 6.0正式发布,启航国产化分布式操作系统新征程

LAXCUS分布式操作系统

数据库 大数据 分布式 操作系统 集群

企评家帮你打造企业价值评估,全面展示企业实力地位完善企业管理

企评家

24小时智能洗车机多少钱一台

共享电单车厂家

自助洗车机价格 24小时智能洗车机 智能洗车机多少钱

SIG 直播:如何使用 SMC-R 透明加速 TCP 应用? | 第15期

OpenAnolis小助手

TCP 龙蜥大讲堂 SMC-R SIG双周会

Next.js 在 Serverless 中从踩坑到破茧重生

CODING DevOps

Serverless 云开发 Cloud Studio 云端工作台

企评家帮助新三板企业进行投资价值评价分析

企评家

CI/CD概述,基于云效进行持续集成CI和持续部署CD(K8s)

阿里云云效

云计算 阿里云 持续集成 CI/CD 持续部署

时序数据库在核电安全领域的应用

CnosDB

IoT 时序数据库 开源社区 CnosDB infra

Java并发JUC(java.util.concurrent)集合不安全

芝士味的椒盐

Java java集合总结 java集合使用 Java集合框架

Java并发JUC(java.util.concurrent)线程池

芝士味的椒盐

Java SE Java多线程 Java 线程

实时云渲染有哪些优势?

3DCAT实时渲染

实时云渲染

企评家|高升控股股份有限公司成长性报告简述

企评家

企业评价 企业成长性 评价分析报告

Curve 基于 Raft 的写时延优化

网易数帆

开源 分布式 云原生 raft 存储

Kruise Rollout: 让所有应用负载都能使用渐进式交付

阿里巴巴云原生

阿里云 开源 Kubernetes 云原生 OpenKruise

云VR的未来发展方向

3DCAT实时渲染

云VR

立足西安,昇腾AI为开发者留下了脚印一串

这不科技

字节跳动Java后端岗,三轮面试,四个小时,灵魂拷问,结局我哭了但下次还敢

Java架构追梦

Java 程序员 后端开发 字节面试

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