0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何让你的Python编程中不出现反模式?避免初学Python常见的错误

马哥Linux运维 来源:未知 作者:易水寒 2018-09-24 10:38 次阅读

这篇文章收集了我在Python新手开发者写的代码中所见到的不规范但偶尔又很微妙的问题。

本文的目的是为了帮助那些新手开发者渡过写出丑陋的Python代码的阶段。

对于那些新手开发者,总有一些使用反模式的理由,我已经尝试在可能的地方给出了这些理由。

但通常这些反模式会造成代码缺乏可读性、更容易出bug且不符合Python的代码风格。

迭代

range的使用

Python编程新手喜欢使用range来实现简单的迭代,在迭代器的长度范围内来获取迭代器中的每一个元素:

应该牢记:range并不是为了实现序列简单的迭代。相比那些用数字定义的for循环,虽然用range实现的for循环显得很自然,但是用在序列的迭代上却容易出bug,而且不如直接构造迭代器看上去清晰:

range的滥用容易造成意外的大小差一(off-by-one)错误,这通常是由于编程新手忘记了range生成的对象包括range的第一个参数而不包括第二个,类似于java中的substring和其他众多这种类型的函数。那些认为没有超出序列结尾的编程新手将会制造出bug:

不恰当地使用range的常见理由:1.需要在循环中使用索引

这并不是一个合理的理由,可以用以下方式代替使用索引:

2.需要同时迭代两个循环,用同一个索引来获取两个值。

这种情况下,可以用zip来实现:

3.需要迭代序列的一部分。在这种情况下,仅需要迭代序列切片就可以实现,注意添加必要的注释注明用意:

有一个例外:

当你迭代一个很大的序列时,切片操作引起的开销就比较大。

如果序列只有10个元素,就没有什么问题;但是如果有1000万个元素时,或者在一个性能敏感的内循环中进行切片操作时,开销就变得非常重要了。

这种情况下可以考虑使用xrange代替range [1]。

在用来迭代序列之外,range的一个重要用法是当你真正想要生成一个数字序列而不是用来生成索引:

正确使用列表解析

如果你有像这样的一个循环:

你可以使用列表解析来重写:

为什么要这么做?

一方面你避免了正确初始化列表可能带来的错误,另一方面,这样写代码让看起来很干净,整洁。

对于那些有函数式编程背景的人来说,使用map函数可能感觉更熟悉,但是在我看来这种做法不太Python化。

其他的一些不使用列表解析的常见理由:

1. 需要循环嵌套。

这个时候你可以嵌套整个列表解析,或者在列表解析中多行使用循环:

使用列表解析:

注意:在有多个循环的列表解析中,循环有同样的顺序就像你并没有使用列表解析一样。

2. 你在循环内部需要一个条件判断。

你只需要把这个条件判断添加到列表解析中去:

一个不使用列表解析的合理的理由是你在列表解析里不能使用异常处理。

如果迭代中一些元素可能引起异常,你需要在列表解析中通过函数调用转移可能的异常处理,或者干脆不使用列表解析。

性能缺陷

在线性时间内检查内容

在语法上,检查list或者set/dict中是否包含某个元素表面上看起来没什么区别,但是表面之下却是截然不同的。

如果你需要重复检查某个数据结构里是否包含某个元素,最好使用set来代替list。(如果你想把一个值和要检查的元素联系起来,可以使用dict;这样同样可以实现常数检查时间。)

Python中set的元素和dict的键值是可哈希的,因此查找起来时间复杂度为O(1)。

应该记住:

创建set引入的是一次性开销,创建过程将花费线性时间即使成员检查花费常数时间。

因此如果你需要在循环里检查成员,最好先花时间创建set,因为你只需要创建一次。

变量泄露

循环

通常说来,在Python中,一个变量的作用域比你在其他语言里期望的要宽。

例如:在Java中下面的代码将不能通过编译:

然而在Python中,同样的代码总会顺利执行且得到意料中的结果:

这段代码将会正常运行,除非子y为空的情况下,此时,循环永远不会执行,而且processList函数的调用将会抛出NameError异常,因为idx没有定义。

如果你使用Pylint代码检查工具,将会警告:使用可能没有定义的变量idx。

解决办法永远是显然的,可以在循环之前设置idx为一些特殊的值,这样你就知道如果循环永远没有执行的时候你将要寻找什么。

这种模式叫做哨兵模式。那么什么值可以用来作为哨兵呢?

C语言时代或者更早,当int统治编程世界的时候,对于需要返回一个期望的错误结果的函数来说为通用的模式为返回-1。

例如,当你想要返回列表中某一元素的索引值:

通常情况下,在Python里None是一个比较好的哨兵值,即使它不是一贯地被Python标准类型使用(例如:str.find [2])

外作用域

Python程序员新手经常喜欢把所有东西放到所谓的外作用域——python文件中不被代码块(例如函数或者类)包含的部分。

外作用域相当于全局命名空间;为了这部分的讨论,你应该假设全局作用域的内容在单个Python文件的任何地方都是可以访问的。

对于定义整个模块都需要去访问的在文件顶部声明的常量,外作用域显得非常强大。

给外作用域中的任何变量使用有特色的名字是明智的做法,例如,使用IN_ALL_CAPS 这个常量名。 这将不容易造成如下bug:

如果你看的近一点,你将看到print_file函数的定义中用filenam命名参数名,但是函数体却引用的却是filename。

然而,这个程序仍然可以运行得很好。

为什么呢?

在print_file函数里,当一个局部变量filename没有被找到时,下一步是在全局作用域中去寻找。

由于print_file的调用在外作用域中(即使有缩进),这里声明的filename对于print_file函数是可见的。

那么如何避免这样的错误呢?

首先,在外作用域中不是IN_ALL_CAPS这样的全局变量就不要设置任何值[3]。

参数解析最好交给main函数,因此函数中任何内部变量不在外作用域中存活。

这也提醒人们关注全局关键字global。如果你只是读取全局变量的值,你就不需要全局关键字global。

你只有在想要改变全局变量名引用的对象时有使用global关键字的必要。

代码风格

向PEP8致敬

PEP 8是Python代码的通用风格指南,你应该牢记在心并且尽可能去遵循它,尽管一些人有充分的理由不同意其中一些细小的风格,例如缩进的空格个数或使用空行。

如果你不遵循PEP8,你应该有除“我只是不喜欢那样的风格”之外更好的理由。下边的风格指南都是从PEP8中摘取的,似乎是编程者经常需要牢记的。

测试是否为空

如果你要检查一个容器类型(例如:列表,词典,集合)是否为空,只需要简单测试它而不是使用类似检查len(x)>0这样的方法:

如果你想在其他地方保存positive_numbers是否为空的结果,可以使用bool(positive_number)作为结果保存;bool用来判断if条件判断语句的真值。

测试是否为None

如前面所提到,None可以作为一个很好的哨兵值。那么如何检查它呢?

如果你明确的想要测试None,而不只是测试其他一些值为False的项(如空容器或者0),可以使用:

如果你使用None作为哨兵,这也是Python风格所期望的模式,例如在你想要区分None和0的时候。

如果你只是测试变量是否为一些有用的值,一个简单的if模式通常就够用了:

例如:如果期望x是一个容器类型,但是x可能作另一个函数的返回结果值变为None,你应该立即考虑到这种情况。你需要留意是否改变了传给x的值,否则可能你认为True或0. 0是个有用的值,程序却不会按照你想要的方式执行。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3440

    浏览量

    92382
  • 代码
    +关注

    关注

    30

    文章

    4555

    浏览量

    66749
  • Range
    +关注

    关注

    0

    文章

    8

    浏览量

    11164
  • python
    +关注

    关注

    51

    文章

    4675

    浏览量

    83466

原文标题:Python编程中的反模式

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    代码又双叕错误python17个常见失误不仅新手会犯,你也可能会

    初学者来说,想要弄懂Python的某些错误信息还是有困难的,下面罗列了一些常见的运行时错误
    的头像 发表于 09-19 11:08 3335次阅读

    python新手常见错误汇总

    对于新手,初学Python时,总会遇到这样那样的报错,想要弄懂Python错误信息的含义可能还不知道怎么做,这里列出了一些比较常见
    发表于 01-26 16:41

    Python初学者选择集成开发环境必看

    编程语言在进行编程时,都需要借助一定的集成开发环境和代码编辑器,Python编程亦如此,以下是在Python
    发表于 06-20 17:09

    关于《Python编程入门系列教程》说明及教程汇总

    成为了我们了解人工智能的首选编程软件。二、关于教程本教程以纯软件的编程为主,并不涉及任何的硬件部分。程序编写部分以Mind+软件的“Python
    发表于 06-23 10:10

    我没学过编程,能否学会Python

    Python是一种计算机程序设计语言。可能已经听说过很多种流行的编程语言,比如非常难学的C语言,非常流行的Java语言,适合初学者的Basic语言,适合网页
    发表于 07-14 14:49

    我没学过编程,能否学会Python

    Python是一种计算机程序设计语言。可能已经听说过很多种流行的编程语言,比如非常难学的C语言,非常流行的Java语言,适合初学者的Basic语言,适合网页
    发表于 07-25 10:21

    Python编程实用指南

    Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言。通过 Python 编程,我们能够解决现实生活的很多任务。本书是一本面向实践的
    发表于 09-27 06:21

    初学常见Python运行错误及其示例归纳

    初学者来说,想要弄懂Python的某些错误信息还是有困难的,下面罗列了一些常见的运行时错误: 1.忘记在if, elif, else, f
    发表于 11-15 13:35 2422次阅读

    Python编程常见的3个错误

    以下列出了学习 Python 时犯的三种错误: 1.可变数据类型作为函数定义中的默认参数 2.可变数据类型作为类变量 3. 可变的分配错误
    发表于 03-21 11:46 1103次阅读

    Python编程中犯的三种错误,让你浪费一下午时间

    为了让初学 Python 的程序员避免犯同样的错误,以下列出了我学习 Python 时犯的三种错误
    发表于 07-07 08:55 5702次阅读

    新手学习Python有哪些常见错误

    学习Python的过程中发现一些常见错误,或许大家也会 遇到,这里在此总结如下:
    发表于 10-30 19:45 4次下载

    Python入门应该避免什么样的错误

    在分享每个Python新手应该知道的4个常见错误之前,请确保您熟悉以下文章中的一些Python内置功能。
    的头像 发表于 04-06 12:03 1892次阅读

    初学者开发人员都会犯的7个Python错误

    这篇文章主要介绍了七个初学者常犯的Python调试错误,并告诉大家如何去避免这些错误
    的头像 发表于 06-23 15:19 2243次阅读
    <b class='flag-5'>初学</b>者开发人员都会犯的7个<b class='flag-5'>Python</b><b class='flag-5'>错误</b>

    Python新手经常会犯那些错误

    初学Python 时,想要弄懂Python错误信息的含义可能有点复杂。这里列出了常见的的一些让你程序crash 的运行时
    发表于 03-12 16:20 16次下载
    <b class='flag-5'>Python</b>新手经常会犯那些<b class='flag-5'>错误</b>

    Python中有哪些常见错误和异常

    python常见异常类型 在程序运行过程中,总会遇到各种各样的问题和错误。 有些错误是我们编写代码时自己造成的,比如语法错误、调用
    的头像 发表于 03-04 16:58 3818次阅读