给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的string模块中的Template类字符串模板用法
Jun 27 Python
python 采集中文乱码问题的完美解决方法
Sep 27 Python
Python DataFrame 设置输出不显示index(索引)值的方法
Jun 07 Python
windows下pycharm安装、创建文件、配置默认模板
Jul 31 Python
Python3.6中Twisted模块安装的问题与解决
Apr 15 Python
NumPy中的维度Axis详解
Nov 26 Python
django 中使用DateTime常用的时间查询方式
Dec 03 Python
Python使用循环神经网络解决文本分类问题的方法详解
Jan 16 Python
python梯度下降算法的实现
Feb 24 Python
PyTorch之nn.ReLU与F.ReLU的区别介绍
Jun 27 Python
在django中实现choices字段获取对应字段值
Jul 12 Python
Python操作word文档插入图片和表格的实例演示
Oct 25 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
一个用于MySQL的PHP XML类
2006/10/09 PHP
使ecshop模板中可引用常量的实现方法
2011/06/02 PHP
APACHE的AcceptPathInfo指令使用介绍
2013/01/18 PHP
PHP中include与require使用方法区别详解
2013/10/19 PHP
浅谈thinkphp5 instance 的简单实现
2017/07/30 PHP
php图片裁剪函数
2018/10/31 PHP
JQuery 学习技巧总结
2010/05/21 Javascript
jquery $.getJSON()跨域请求
2011/12/21 Javascript
JS获取select的value和text值的简单实例
2014/02/26 Javascript
jQuery Form表单取值的方法
2017/01/11 Javascript
利用Vue实现移动端图片轮播组件的方法实例
2017/08/23 Javascript
使用vue与jquery实时监听用户输入状态的操作代码
2017/09/19 jQuery
详解如何模拟实现node中的Events模块(通俗易懂版)
2019/04/15 Javascript
微信小程序整合使用富文本编辑器的方法详解
2019/04/25 Javascript
Vue-cli3简单使用(图文步骤)
2019/04/30 Javascript
vue路由 遍历生成复数router-link的例子
2019/10/30 Javascript
VSCode 配置uni-app的方法
2020/07/11 Javascript
[02:26]2018DOTA2亚洲邀请赛赛前采访-Newbee篇
2018/04/03 DOTA
[00:30]明星选手化身超级英雄!2018DOTA2亚洲邀请赛全明星赛来临!
2018/04/06 DOTA
Python实现的监测服务器硬盘使用率脚本分享
2014/11/07 Python
python 读取DICOM头文件的实例
2018/05/07 Python
浅谈python中np.array的shape( ,)与( ,1)的区别
2018/06/04 Python
如何在Django中设置定时任务的方法示例
2019/01/18 Python
浅谈python3.6的tkinter运行问题
2019/02/22 Python
通过python实现弹窗广告拦截过程详解
2019/07/10 Python
用OpenCV将视频分解成单帧图片,图片合成视频示例
2019/12/10 Python
基于Pycharm加载多个项目过程图解
2020/01/19 Python
python读取文件指定行内容实例讲解
2020/03/02 Python
CSS3 @font-face属性使用指南
2014/12/12 HTML / CSS
HTML5 Canvas 旋转风车绘制
2017/08/18 HTML / CSS
日语专业推荐信
2013/11/12 职场文书
英语专业应届生求职信范文
2013/11/15 职场文书
特教教师先进事迹
2014/05/21 职场文书
食品安全主题班会
2015/08/13 职场文书
Python Numpy之linspace用法说明
2021/04/17 Python
Golang 如何实现函数的任意类型传参
2021/04/29 Golang