给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文件中的特定数据的实现代码
Mar 24 Python
Python的Bottle框架中返回静态文件和JSON对象的方法
Apr 30 Python
python写入中英文字符串到文件的方法
May 06 Python
python函数形参用法实例分析
Aug 04 Python
python3编码问题汇总
Sep 06 Python
浅谈使用Python内置函数getattr实现分发模式
Jan 22 Python
python散点图实例之随机漫步
Aug 27 Python
python 解压pkl文件的方法
Oct 25 Python
python引用(import)某个模块提示没找到对应模块的解决方法
Jan 19 Python
Python正则表达式匹配日期与时间的方法
Jul 07 Python
tensorflow 实现从checkpoint中获取graph信息
Feb 10 Python
Django 解决由save方法引发的错误
May 21 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
自动生成文章摘要的代码[PHP 版本]
2007/03/20 PHP
PHP基础学习小结
2011/04/17 PHP
PHP中echo和print的区别
2014/08/28 PHP
php使用fopen创建utf8编码文件的方法
2014/10/31 PHP
PHP实现HTML生成PDF文件的方法
2014/11/07 PHP
PHP+Ajax实时自动检测是否联网的方法
2015/07/01 PHP
详解PHP中的Traits
2015/07/29 PHP
浅谈PHP错误类型及屏蔽方法
2017/05/27 PHP
关于laravel后台模板laravel-admin select框的使用详解
2019/10/03 PHP
PHP随机生成中文段落示例【测试网站内容时使用】
2020/04/26 PHP
Javascript中匿名函数的多种调用方式总结
2013/12/06 Javascript
jQuery实现新消息闪烁标题提示的方法
2015/03/11 Javascript
辨析JavaScript中的Undefined类型与null类型
2016/05/26 Javascript
JS中使用FormData上传文件、图片的方法
2016/08/07 Javascript
JavaScript数据类型学习笔记分享
2016/09/01 Javascript
微信小程序实现跑马灯效果完整代码(附效果图)
2018/05/30 Javascript
vue-router路由懒加载的实现(解决vue项目首次加载慢)
2018/08/28 Javascript
angular4中*ngFor不能对返回来的对象进行循环的解决方法
2018/09/12 Javascript
vue axios请求频繁时取消上一次请求的方法
2018/11/10 Javascript
Vue2(三)实现子菜单展开收缩,带动画效果实现方法
2019/04/28 Javascript
深入Python解释器理解Python中的字节码
2015/04/01 Python
对python中的argv和argc使用详解
2018/12/15 Python
face++与python实现人脸识别签到(考勤)功能
2019/08/28 Python
python实现图片插入文字
2019/11/26 Python
解决keras加入lambda层时shape的问题
2020/06/11 Python
基于Python的身份证验证识别和数据处理详解
2020/11/14 Python
利用CSS的Sass预处理器(框架)来制作居中效果
2016/03/10 HTML / CSS
html5 移动端视频video的android兼容(去除播放控件、全屏)
2020/03/26 HTML / CSS
ALEX AND ANI:手镯,项链,耳环和更多
2017/04/20 全球购物
美国精品地毯网站:Boutique Rugs
2020/03/04 全球购物
什么是"引用"?申明和使用"引用"要注意哪些问题?
2016/03/03 面试题
前处理班长职位说明书
2014/03/01 职场文书
安全横幅标语
2014/06/09 职场文书
2015届本科毕业生自我鉴定
2014/09/27 职场文书
个人买房协议书范本
2014/10/06 职场文书
2014年镇党建工作汇报材料
2014/11/02 职场文书