给Python入门者的一些编程建议


Posted in Python onJune 15, 2015

Python是一种非常富有表现力的语言。它为我们提供了一个庞大的标准库和许多内置模块,帮助我们快速完成工作。然而,许多人可能会迷失在它提供的功能中,不能充分利用标准库,过度重视单行脚本,以及误解Python基本结构等。本文是一个关于Python新手可能会陷入的一些陷阱的不完全列表。

不知道Python版本

这是一个在StackOverflow上反复出现的问题。许多人能写出在某个版本上完美工作的代码,但在他们在自己的系统上安装有不同版本的Python。要确保你知道你正在使用的Python版本。

你可以通过下边的代码查看Python版本:
 

$ python --version
Python 2.7.9

不使用版本管理器

pyenv是一个极好的管理不同Python版本的工具,但很不幸,它只工作在*nix系统上。在Mac系统上,你可以简单地通过brew install pyenv安装它,在Linux上,也有一个自动安装程序。

沉迷于一行程序

许多人热衷于一行程序带来的兴奋感。即使他们的一行解决方案比一个多行解决方案低效,他们也会吹嘘。

Python中的一行程序在本质上意味着具有多个表达式的复杂推导。例如:
 

l = [m for a, b in zip(this, that) if b.method(a) != b for m in b if not m.method(a, b) and reduce(lambda x, y: a + y.method(), (m, a, b))]

老实讲,我编造了上面的例子。但我看到很多人都写类似的代码。这样的代码在一个星期后就会变得难以理解。如果你想做一些稍微复杂的事情,例如根据条件简单地在一个列表或集合中添加一个元素,你可能就会犯错误。

单行代码并不是什么成就,是的,他们可能看起来很灵活,但不是什么成就。想象一下,这就像是你在打扫房间时把所有的东西都塞进你的衣橱。好的代码应该是干净的,易于阅读的和高效的。

利用错误的方式初始化一个集合

这是一个更微妙的问题,可能让你措手不及。集合推导很像列表推导。
 

>>> { n for n in range(10) if n % 2 == 0 }
{0, 8, 2, 4, 6}
>>> type({ n for n in range(10) if n % 2 == 0 })

上面就是集合推导的一个例子。集合就像列表,也是一个容器。所不同的是,一个集合中不能有任何重复的值,而且是无序的。看到集合推导人们经常错误地认为{}能初始化一个空集合。但其实不然,它初始化一个空字典。
 

>>> {}
{}
>>> type({})

如果你想初始化一个空集合,可以简单地调用set()方法。
 

>>> set()
set()
>>> type(set())

注意一个空集合用set()表示,但是一个包含一些元素的集合就就要用花括号包围元素来表示。
 

>>> s = set()
>>> s
set()
>>> s.add(1)
>>> s
{1}
>>> s.add(2)
>>> s
{1, 2}

这和直觉是相反的,因为你期望类似于set([1, 2])的一些东西。

误解GIL

GIL(全局解释器锁)意味着在Python程序中,任意一个时间点只能有一个线程在运行。这意味着当我们创建一个线程并希望它并行运行时,它并不会那样。Python解释器实际的工作是在不同的运行线程之间快速进行切换。但这只是对实际发生事情的一个非常简单的解释,实际情况要复杂的多。有很多种并行运行的实例,例如使用本质为C扩展的各种库。但运行Python代码时,大部分时间里它不会并行执行。换句话说,Python中的线程并不像Java或C++中的线程。

许多人会尝试为Python辩解,说这些都是真正的线程。这确实是真的,但并不能改变这样一个事实:Python处理线程的方式和你期望的方式是不同的。Ruby语言也有相同的情况(Ruby也有一个解释器锁)。

指定的解决方案是使用multiprocessing模块。multiprocessing模块提供Process类,它是一个对fork的很好的覆盖。然而,fork过程比一个线程的代价高得多,所以你可能不会每次都能看到性能上的提升,因为不同的process之间需要做大量的工作来进行相互协调。

然而,这个问题并不存在于每一个Python的实现版本中。例如,Python的一个实现PyPy-stm就试图摆脱GIL(仍未稳定)。建立在其他平台,如JVM(Jython)或CLR(IronPython),上的Python实现,也没有GIL的问题。

总之,使用Thread类时要多加小心,你得到的可能不是你想要的。

使用老式类

在Python 2中,有两种类型的类,分别为“老式”类和“新式”类。如果你使用Python 3,那么你默认使用“新式”类。为了确保在Python2中使用“新式”类,你需要让你新创建的每一个类都继承object类,且类不能已继承了内置类型,例如int或list。换句话说,你的基类、类如果不继承其他类,就总是需要继承object类。
 

class MyNewObject(object):
# stuff here

这些“新式”类解决一些老式类的根本缺陷,这一点我们不需要深入了解。然而,如果有人感兴趣,他们可以在相关文档中找到相关信息。

按错误的方式迭代

对于这门语言的新手来说,下边的代码是非常常见的:
 

for name_index in range(len(names)):
print(names[name_index])

在上边的例子中,没有必须调用len函数,因为列表迭代实际上要简单得多:
 

for name in names:
print(name)

此外,还有一大堆其他的工具帮助你简化迭代。例如,可以使用zip同时遍历两个列表:
 

for cat, dog in zip(cats, dogs):
print(cat, dog)

如果你想同时考虑列表变量的索引和值,可以使用enumerate:
 

for index, cat in enumerate(cats):
print(cat, index)

在itertools中也有很多有用的函数供你选择。然而请注意,使用itertools函数并不总是正确的选择。如果itertools中的一个函数为你试图解决的问题提供了一个非常方便的解决办法,例如铺平一个列表或根据给定的列表创建一个其内容的排列,那就用它吧。但是不要仅仅因为你想要它而去适应你代码的一部分。

滥用itertools引发的问题出现的过于频繁,以至于在StackOverflow上一个德高望重的Python贡献者已经贡献他们资料的重要组成部分来解决这些问题。

使用可变的默认参数

我多次见到过如下的代码:
 

def foo(a, b, c=[]):
# append to c
# do some more stuff

永远不要使用可变的默认参数,可以使用如下的代码代替:
 

def foo(a, b, c=None):
if c is None:
c = []
# append to c
# do some more stuff

与其解释这个问题是什么,不如展示下使用可变默认参数的影响:
 

In[2]: def foo(a, b, c=[]):
... c.append(a)
... c.append(b)
... print(c)
...
In[3]: foo(1, 1)
[1, 1]
In[4]: foo(1, 1)
[1, 1, 1, 1]
In[5]: foo(1, 1)
[1, 1, 1, 1, 1, 1]

同一个变量c在函数调用的每一次都被反复引用。这可能有一些意想不到的后果。

总结

这些只是相对来说刚接触Python的人可能会遇到的一些问题。然而请注意,可能会遇到的问题远非就这么些。然而另一些缺陷是人们像使用Java或C++一样使用Python,并且试图按他们熟悉的方式使用Python。所以作为本篇文章的一个延续,尝试深入一些东西,例如Python的super函数。看看类方法、静态方法和 __slots__等。

Python 相关文章推荐
Python help()函数用法详解
Mar 11 Python
python实现kNN算法
Dec 20 Python
对TensorFlow的assign赋值用法详解
Jul 30 Python
Python小工具之消耗系统指定大小内存的方法
Dec 03 Python
python try 异常处理(史上最全)
Mar 07 Python
Python restful框架接口开发实现
Apr 13 Python
Matplotlib中%matplotlib inline如何使用
Jul 28 Python
Python3爬虫中识别图形验证码的实例讲解
Jul 30 Python
Pygame框架实现飞机大战
Aug 07 Python
python工具——Mimesis的简单使用教程
Jan 16 Python
拒绝盗图!教你怎么用python给图片加水印
Jun 04 Python
python百行代码实现汉服圈图片爬取
Nov 23 Python
Python修改MP3文件的方法
Jun 15 #Python
Python从MP3文件获取id3的方法
Jun 15 #Python
python简单实现基于SSL的IRC bot实例
Jun 15 #Python
Python中datetime常用时间处理方法
Jun 15 #Python
Python实现简单截取中文字符串的方法
Jun 15 #Python
构建Python包的五个简单准则简介
Jun 15 #Python
Python中文字符串截取问题
Jun 15 #Python
You might like
一个图形显示IP的PHP程序代码
2007/10/19 PHP
PHP substr 截取字符串出现乱码问题解决方法[utf8与gb2312]
2011/12/16 PHP
一个PHP针对数字的加密解密类
2014/03/20 PHP
PHP中的事务使用实例
2015/05/26 PHP
利用XMLHTTP传递参数在另一页面执行并刷新本页
2006/10/26 Javascript
JavaScript 继承详解 第一篇
2009/08/30 Javascript
JavaScript中获取未知对象属性的代码
2011/04/27 Javascript
JavaScript中变量提升 Hoisting
2012/07/03 Javascript
Jquery事件的连接使用示例
2013/06/18 Javascript
js加减乘除丢失精度问题解决方法
2014/05/16 Javascript
JS实现可调整倒计时间代码分享
2015/08/18 Javascript
详解js中构造流程图的核心技术JsPlumb
2015/12/08 Javascript
基于jQuery实现复选框是否选中进行答题提示
2015/12/10 Javascript
jQuery删除节点用法示例(remove方法)
2016/09/08 Javascript
微信小程序 HTTPS报错整理常见问题及解决方案
2016/12/14 Javascript
JavaScript实现简易的天数计算器实例【附demo源码下载】
2017/01/18 Javascript
基于JavaScript实现下拉列表左右移动代码
2017/02/07 Javascript
微信小程序实战之自定义模态弹窗(8)
2017/04/18 Javascript
详解webpack3编译兼容IE8的正确姿势
2017/12/21 Javascript
Vue实现多标签选择器
2019/11/28 Javascript
js实现限定区域范围拖拉拽效果
2020/11/20 Javascript
基于ajax实现上传图片代码示例解析
2020/12/03 Javascript
[03:12]2016完美“圣”典风云人物:单车专访
2016/12/02 DOTA
Python中函数参数匹配模型详解
2019/06/09 Python
Anaconda+Pycharm环境下的PyTorch配置方法
2020/03/13 Python
Python爬虫实现HTTP网络请求多种实现方式
2020/06/19 Python
芬兰汽车配件商店:Autonvaraosat24
2017/01/30 全球购物
理肤泉加拿大官网:La Roche-Posay加拿大
2018/07/06 全球购物
篝火晚会策划方案
2014/05/16 职场文书
个人安全生产承诺书
2014/05/22 职场文书
汽车维修求职信
2014/06/15 职场文书
写给父母的感谢信
2015/01/22 职场文书
学校中秋节活动总结
2015/03/23 职场文书
2015年七七事变78周年纪念活动方案
2015/05/06 职场文书
Python机器学习之基础概述
2021/05/19 Python
python3操作redis实现List列表实例
2021/08/04 Python