给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解析html开发库pyquery使用方法
Feb 07 Python
Python with的用法
Aug 22 Python
Python入门_条件控制(详解)
May 16 Python
Python学习教程之常用的内置函数大全
Jul 14 Python
python探索之BaseHTTPServer-实现Web服务器介绍
Oct 28 Python
python类的方法属性与方法属性的动态绑定代码详解
Dec 27 Python
django 实现编写控制登录和访问权限控制的中间件方法
Jan 15 Python
python中将两组数据放在一起按照某一固定顺序shuffle的实例
Jul 15 Python
对python中assert、isinstance的用法详解
Nov 27 Python
基于python使用tibco ems代码实例
Dec 20 Python
TensorFlow 显存使用机制详解
Feb 03 Python
python连接mongodb集群方法详解
Feb 13 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
数据库相关问题
2006/10/09 PHP
人大复印资料处理程序_输入篇
2006/10/09 PHP
PHP通过正则表达式下载图片到本地的实现代码
2011/09/19 PHP
PHP CLI模式下的多进程应用分析
2013/06/03 PHP
Js 获取HTML DOM节点元素的方法小结
2009/04/24 Javascript
JS动态添加option和删除option(附实例代码)
2013/04/01 Javascript
url参数中有+、空格、=、%、&、#等特殊符号的问题解决
2013/05/15 Javascript
ECMAScript5(ES5)中bind方法使用小结
2015/05/07 Javascript
使用coffeescript编写node.js项目的方法汇总
2015/08/05 Javascript
JavaScript的jQuery库中ready方法的学习教程
2015/08/14 Javascript
实例讲解js验证表单项是否为空的方法
2016/01/09 Javascript
基于canvas实现的绚丽圆圈效果完整实例
2016/01/26 Javascript
vue.js表格分页示例
2016/10/18 Javascript
jQuery时间日期三级联动(推荐)
2016/11/27 Javascript
详解Angular2表单-模板驱动的表单(Template-Driven Forms)
2017/08/04 Javascript
Angular17之Angular自定义指令详解
2018/01/21 Javascript
解决Angular.js中使用Swiper插件不能滑动的问题
2018/02/26 Javascript
通过vue手动封装on、emit、off的代码详解
2019/05/29 Javascript
Vue实现图片与文字混输效果
2019/12/04 Javascript
vue-cli设置publicPath小记
2020/04/14 Javascript
JS的时间格式化和时间戳转换函数示例详解
2020/07/27 Javascript
vue项目中openlayers绘制行政区划
2020/12/24 Vue.js
[54:41]2018DOTA2亚洲邀请赛3月30日 小组赛B组 VGJ.T VS paiN
2018/03/31 DOTA
Python中shutil模块的学习笔记教程
2017/04/04 Python
Python调用ctypes使用C函数printf的方法
2017/08/23 Python
Python实现Mysql数据统计及numpy统计函数
2019/07/15 Python
如何使用python代码操作git代码
2020/02/29 Python
CSS3 圆角效果
2009/07/15 HTML / CSS
Manduka官网:瑜伽垫、瑜伽毛巾和服装
2018/07/02 全球购物
书香家庭事迹材料
2014/05/09 职场文书
青年文明号口号
2014/06/17 职场文书
人力资源管理专业求职信
2014/07/23 职场文书
英文辞职信范文
2015/05/13 职场文书
大学生团支书竞选稿
2015/11/21 职场文书
详解python中[-1]、[:-1]、[::-1]、[n::-1]使用方法
2021/04/25 Python
mysql序号rownum行号实现方式
2022/12/24 MySQL