@Lenciel

Getting Start With Python

刚刚开始使用 Python 开发的新手,遇到的第一个瓶颈(常常)就是缺乏对整个 Python 生态系统的理解。大家总是在网上搜索完成这件事或者那件事的「标准做法」,就像我们使用其他编程语言的时候常常需要掌握的一样。

在和几个朋友开始 iDVR 的开发的时候,我们在内部 Wiki 上维护过一个 Python 中常见问题列表。我们的目标是随着项目的不断进步,这个内部的 wiki 能够变成一个豪华的 Q&A 索引,但是事实证明项目一忙起来就没有人去写 Wiki 了。

最近陆续有朋友问一些 Python 相关的问题,想想其实要用三言两语说清楚并不容易,就决定在那份 wiki 的基础上自己写一个版本,给那些刚开始使用 Python(从来没有编过程的读起来可能没啥帮助)的同学。

Warning

Python 编程是个大话题,不是这里讨论的内容。如果你没有太多编程经验,应该先去看一点儿 Python 的入门书籍

另外,这片文章是基于 Windows(最好是 Win7 32bit)操作系统。潮人请不要问为啥不是 Mac 或者 Linux,因为本座主要用的机器(包括码这篇 Blog)都是用 Win7 32bit。不过除开环境搭建,这里提到的大部分概念对其他系统都是适用的。

如果你有环境搭建上的问题,最靠谱的提问的地方不是 CSDN 或者百度知道,是Stack Overflow

版本选择

本座在某软件公司面试的时候别人看到简历上有 Python 就问,Python3 用过吗?本座老实回答没有,结果面试官就停下他正在笔记本上 google 的微操,在脸上流露出一丝嘲讽…

不过,除了 Python 这门语言本身的开发者,大多数像本座这种使用 Python 的人,都没有紧跟 Python3 的步伐。Python3(或者叫 Py3K)是一个大多数我熟悉的包,框架和工具都还没有完备支持的版本(在未来的数年本座也看不到希望)。如果不是想研究 Python3 的新特性或者具体实现,个人觉得初学的开发者使用 2.7.x 是最安全的版本(本座还在用进 M 公司就装好的 2.6 版本)。

如果你比较熟悉 Python 但是不确定该不该升级到 Python3,不妨先去观赏一下Python 3 Wall of Shame (墙外,你们懂的)。

VM选择

Python 是脚本语言,因此需要 VM。CPython 是最主流的选择,也被当成其他 VM 实现时的参考。其他常见的还有用 Python 实现的PyPy,用 Java 实现的Jython 以及用 Microsoft .Net CLR 实现的IronPython。如果你不是非常非常确定你自己要选用别的,就安装 CPython 吧。

换句话说,前面这堆关于 VM 和版本选择的建议在你看来不知所云,你需要的就是CPython 2.7.x 版本

Python安装

下载之后的.exe 安装文件安装的时候有一个地方需要注意:如果你是在 Vista/Win7 这样的 C 盘权限控制异常严格的操作系统,最好用右键「Run as administrator」。这点也适用于你下载到的被其他人编译成 exe 发布的 Python package。嗯,什么是 package?

理解Package

Python 没有一个内置的 package 管理体系。实际上 Python 下面一个 package 是什么也是一个很「不具体」的概念。就像前面提到的,Python 下面的代码是以 module 为单位存在的。每个 module 既可以是一个只有一个函数的文件,也可以是一个包含了一个或者多个子 module 的目录。而 module 和 package 之间的区别是非常模糊的,每个 module 都可以被认为是一个 package。

和所有的编程环境一样,在 Python 下面有一些函数和类是全局可见的(strlenException等等),而另外一些需要通过import语句导入,比如:

>>>import os
>>>from os.path import basename, dirname

这里import被调用的时候,Python 是从哪里把这些 module 导入的呢?其实,在你安装 Python 的时候,module 的导入路径已经被自动设置过了。这个过程的具体实现是跟你运行的平台相关的,你可以通过sys.path来查看被设置的路径究竟是什么。比如本座的是:

['',
'C:\\Python26\\lib\\site-packages\\demjson-1.4-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\anyjson-0.2.5-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\pmw-1.3.2-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\paramiko-1.7.6-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\treewidgets-1.0a1-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\mechanize-0.2.3-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\pylint-0.22.0-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\logilab_astng-0.21.0-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\logilab_common-0.53.0-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\unittest2-0.5.1-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\virtualenv-1.5.1-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\django_staticfiles-0.3.2-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\django_attachments-0.3dev-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\django_ajax_validation-0.1.4-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\django_email_confirmation-0.2.dev4-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\distribute-0.6.10-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\django_timezones-0.2.dev1-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\django-1.3-py2.6.egg',
'C:\ \Python26\\lib\\site-packages\\rbtools-0.3.2-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\django_debug_toolbar-0.8.4-py2.6.egg',
'C:\\Python26\\lib\\site-packages\\suds-0.4-py2.6.egg',
'C:\\Windows\\system32\\python26.zip',
'C:\\Python26\\DLLs',
'C:\\Python26\\lib',
'C:\\Python26\\lib\\plat-win',
'C:\\Python26\\lib\\lib-tk',
'C:\\Python26',
'C:\\Python26\\lib\\site-packages',
'C:\\Python26\\lib\\site-packages\\PIL',
'C:\\Python26\\lib\\site-packages\\wx-2.8-msw-unicode']

Python 在导入的 module 的时候,是按照这个列表「自顶向下,见好就收」的方式运作的。也就是说如果你正好机器上有两个路径下面安装了同样名称的 module,先被搜索到的那个就会被导入。有的时候,你确信自己受到了这种机制的干扰,也可以通过下面的办法来 hack 回来:

sys.path.insert(, 'path\\to\\your\\packages')

进行一段时间的 Python 开发过后你总是会有很多的包,于是这个办法你会觉得非常的方便。但是,请结合后面的内容默默记住,不到万不得已,不要这样 hack。

The PYTHONPATH

PYTHONPATH是一个环境变量(可以 win+break 到高级设置里面去设置),可以简单的理解它就是 Windows 下面的PATH变量,不过只是对 Python 可见而已。在很多 Python 的教程里面,会说所有你想要让 Python 搜索 module 的路径,都应该加到这个变量对应的列表里面。

其实前面提到过,在 Python 安装的时候搜索路径已经被自动设置过一次。所以PYTHONPATH这个变量并不是必须加的。而且,作为开发环境满坑满谷的程序员,我们都喜欢把 Path 里面搞得干干净净的(比如本座干活都习惯在 bat 里面先设置相关的路径再起相应的 Eclipse)。这种一启动就 load 一堆的办法,自己喜欢也可以用,但是更推荐后面会说的virtualenv

第三方package

首先,入门之后要开始正经干活,你总是需要安装一些第三方的 Package。安装的办法有:

  1. 下载别人编译好的 Windows 的版本 exe
  2. 使用pip或者easy_install
  3. 自己从代码安装

三种办法干的事情都是类似的:下载 package 的依赖包,编译(需要的话)和拷贝目标文件到一个默认的第三方 Package 路径。那么,哪里去找安装需要的文件呢?一般来说:

  1. Google
  2. Python Package Index(or PyPI)
  3. 各种开源的代码库(Launchpad/GitHub/BitBucket
安装别人编译好的exe

注意下载的时候看清别人编译是在 32 还是 64bit 的 Windows,Python 的版本和你用的一致不一致。运行 exe 的时候注意右键 Run as Administrator。就这么简单。

使用pip

easy_install已经慢慢失宠了,主要介绍一下它的替代品:pip

pip是用来安装和管理 Python Package 的工具。它不是随 Python 默认安装的,因此需要额外安装。安装完毕之后我们就可以在命令行里面调用它来管理 package 了。比如你要安装pygame这个 package,只需要:

pip install pygame

而如果你想删除它的话,则运行:

pip uninstall pygame

pip默认会按照你指定的名字,搜索和安装最新的 stable 版本的包。但我们常常需要安装某个特定版本的包,这个时候需要你在命令行里面指定:

pip install pygame==version_number

如果你安装的版本不对,可以通过upgrade命令升级/降级到指定版本:

pip install pygame==version_number –upgrade

由于 Python 高度依赖开源团体,很多最新的 package 都没有在 PyPI 上面,我们常常需要从代码库直接安装 package。在pip下面可以直接

$ pip install git+http://somedomain.com/path/to/git-repo#egg=packagename
$ pip install hg+http://somedomain.com/path/to/hg-repo#egg=packagename
$ pip install svn+http://somedomain.com/path/to/svn-repo#egg=packagename

当然,前提是 git/hg/svn 这些工具你都安装好了并且在命令行里面能够执行。

上面这些egg是什么蛋呢?你可以认为它们就是 Package 源代码和一些 metadata 打成的压缩包。pip会读取egg里面的setup.py文件,然后安装egg到你的文件系统。

从Python源码安装

从源代码安装虽然是最复杂的,但是也没那么复杂。把你下载的源代码解压之后,找到setup.py所在的路径,然后运行下面的命令:

python setup.py install

也许你觉得这样也挺容易的,为啥要去用pip?因为:

  • 省去你上网找源代码,解压,安装这些动作
  • 更重要的是,pip不但装,而且管。你可以升级和降级一个 Package。
安装需要编译的package

这种一般是因为 Package 里面有 c/cpp 的代码。如果你能找到别人编译好的 exe 文件或者用pip,最好不要自己折腾。只有非常非常罕见的情况下你需要自己编译,过程中一般来说都会需要用cygwin之类的东西,在前面几次你可以多看看每个 Package 的readme关于编译的说明。

开发环境

virtualenv

virtualenv无疑是当前 Python 开发者心中「必知必会」类的工具了。virtualenv主要就是提供一个「独立的」Python 开发环境。为什么需要「独立」的开发环境?在不知道virtualenv之前 Python 包满坑满谷的本座自然是有很多槽可以吐,不过最好的答案是virtualenv的文档里面说的:

The basic problem being addressed is one of dependencies and versions, and indirectly permissions. Imagine you have an application that needs version 1 of LibFoo, but another application requires version 2. How can you use both these applications? If you install everything into /usr/lib/python2.7/site-packages (or whatever your platform’s standard location is), it’s easy to end up in a situation where you unintentionally upgrade an application that shouldn’t be upgraded.

简单来说,就是每个工程使用自己独立的virtualenv进行开发,所有该工程需要依赖的 Package 都安装在这个virutalenv里面。virutalenv的安装同样是使用pip

$ pip install virtualenv

然后就可以建立属于自己项目的开发环境:

D:\Lenciel\Temp mkdir my_project_env

D:\Lenciel\Temp\virtualenv --distribute my_project_env

  New python executable in my_project_env\Scripts\python.exe
  Installing distribute.................done.

建立的目录下面会自动的安装好pip等工具:

-my_project_env
  |-- Scripts # -- Python解释器的拷贝/pip脚本/activiate脚本/deactivate脚本等
  |-- Lib     # -- 所有库(包括激活后使用pip安装的库都会放在这里

在命令行执行 activate 脚本就可以激活你新建的环境。环境被激活后,主要通过下面两个功能保持独立完整性:

  1. 当你在被激活的环境里面使用pip安装一个 Package 的时候,它只会被安装在这个工作目录下面
  2. 当你import的时候,会先从工作目录下面去搜索,然后才会搜索系统目录下的

需要注意的是,在系统目录下面安装的包,对所有的virtualenv建立的环境都是可见的。如果你不想在你建立的virtualenv里面看到这些包,可以使用--no-site-packages参数建立开发环境:

$ virtualenv my_project_venv --no-site-packages

其他的工具

编辑器

在不同的项目里面本座用过 Vim/Sublime text 2/Eclipse+PyDev/PyCharm。现在 Vim 和 PyCharm 用得比较多一点,PyDev 一直有一些诡异的问题。比如那个「Unresolved import」从一开始到现在都好像没有被真正解决过。

编码规范

PEP 0008推荐了非常完整的一套编码规范,目的就是让全世界编写 Python 脚本的同学们用相同的方式去对齐代码,命名变量、类和函数。每个严肃的 Pythoner 都应该认真的学习和理解这些规范,并且贯彻执行。

Python标准库

Python 的标准库提供了相当完备的功能。就像 Java 的工程师需要熟悉系统自带的 API 文档一样,了解标准库的用法是非常有好处的。另外,这些标准库都是很好的范例,特别是在支持跨平台使用这方面。

官方文档在这里

嗯哼

前面列了不少东西,只是希望给新手一个上路指引。Python 下面有用的工具,有趣的包很多很多。随着你学习和应用这门语言,不断深入它,你会自己慢慢发觉那些你自己最需要/愿意熟悉的部分。

Python 的另外一大财富就是它的开源社区,条件成熟的时候,你也应该参与到这样的开发活动里面去。

最后是每个 Pythoner 都津津乐道的 Zen Of Python 送给刚开始学习的朋友。

>>>import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Happy Pythoning…!!

欢迎留言