@Lenciel

How To Define Viewport For Mobile

概念:设备像素和CSS像素

首先要明白 CSS 像素和设备像素之间的区别。

设备像素是定义了我们使用的设备的分辨率,一般来说可以通过screen.width/height来得到。

如果我们在浏览器里面创建一个width:128px的元素,而我们的屏幕是 1024px 宽,那么在浏览器最大化的时候,浏览器的宽度应该是这个元素的八倍(大概八倍,暂时忽略那些 tricky 的 bits)。

如果用户使用滚轮放大或者缩小页面(Zooming),那么这个关系就会变化。一般来说,现在的浏览器对 Zomming 的实现都是通过伸缩像素,也就是当用户 Zoom 到 200%的时候,你的128px宽的元素宽度并不会变成256px,而是每个像素的宽度翻倍了:这个元素还是128px的宽度,但是占据了 256 个设备像素。

也就是说,zoom 到 200%其实会让一个 CSS 像素从一个设备像素变成四个设备像素的大小(长宽各翻一倍,面积就变成了 4 倍)。

         原始(zoom 100%)                 开始放大(zoom in)              开始缩小(zoom out)

csspixels_100  csspixels_in  csspixels_out

从这里可以看出,我们在使用 css 像素值在 css 文件里面定义宽度时,不需要考虑设备像素,因为在 zoom 的时候 css 像素值是不变的。用户在缩放的时候,浏览器负责放大缩小那些元素,但是整个 layout 是不变的。

屏幕大小

screen.widthscreen.height可以获取用户屏幕的尺寸。获得的尺寸值的单位是设备像素,换句话说,它们是不变的(可以看成是显示器的硬件指标,而不随浏览器窗口缩放而变化)。

desktop_screen

一般来说,用户的屏幕大小对我们是无用的信息。很少有人使用它(除开统计站点访问信息时)。

窗口大小

####

窗口大小表示了当前你的 CSS layout 可以占用的空间大小。可以通过window.innerWidthwindow.innerHeight来获取它们。

desktop_inner

很显然,窗口大小的单位是 CSS 像素值。当用户用滚轮放大缩小自己的窗口大小的时候,window.innerWidth/Height会相应的变化,这样你就知道自己的 layout 有多大空间可用了。

注意:Opera 是一个例外,它的window.innerWidth/Height不会因为缩放变化,而是取的设备像素值。这点对于桌面操作系统上的应用很讨厌,但是就像后面会说到的,对于手机来说很关键。

Scrolling offset

window.pageXOffset/pageYOffset代表了当前的 document 在横向和纵向上的偏移量。这样你就知道用户 scroll 了多少内容。

desktop_page

这些值也是 CSS 像素,理论上,当用户做缩放的时候,window.pageXOffset/pageYOffset也会变化。但是实际上,浏览器一般会在用户缩放的时候保证置顶的元素不变,所以在缩放的时候,一般来说这两个值基本是不变的。

概念:Viewport

Viewport的主要作用是限定<html>元素,也就是整个页面的最高级元素的大小。这样说比较含糊,我们来看一个详尽的例子:假设你有一个页面,侧栏的宽度是 10%。在你缩放浏览器窗口大小的时候,这个侧边栏也会自动缩放。这是如果实现的?

技术上讲,按照设置,侧边栏会自动占据自己的父元素(这里我们可以假设是<body>)宽度的 10%。那么<body>的宽度呢?又会取它的父元素<html>的宽度的 100%——因为理论上所有的 HTML 的 block-level 元素都会自动取父元素宽度的 100%(有些例外情况,我们这里忽略它们)。

那么<html>的宽度是怎么来的呢?所有的网页开发者都默认它就是浏览器的宽度。的确如此——在桌面浏览器上就是这样。实际上,就像我们已经提到过的,<html>的宽度是 viewport 限定的。在桌面上它就是整个浏览器窗口的宽度,而在手机上则要复杂得多。

手机浏览器

手机一个显而易见的特点就是屏幕小。如果我们原封不动的使用为桌面版浏览器开发的网站的文件,就会发现它们在手机浏览器上面目全非。但是大多数网站开发者是没有精力给手机用户专门维护另外一个网站的。因此手机浏览器制造商尽可能的通过技术手段让网站在手机上和在桌面浏览器里看起来「差不多」。

两个viewports

既然手机浏览器的 viewport 比较小,没法呈现所有 css 定义的元素,那么解决这个问题的办法就很明显了:扩大手机上的 viewport。最终,手机浏览器通过visual viewportlayout viewport的合作来解决问题。

George 在 Stack Overflow 上解释说:

layout viewport 就像一个不改变大小和形状的大的图片。想象一下你透过一个小窗口去看这个大的图片,因为窗框的遮挡,你只能看到大图片的一部分。这部分就是 visual viewport。你可以改变自己和这个窗口的位置(其实就是缩放),你也可以把窗口横放或者竖放,但是大图片本身(layout viewport)是不会改变的。

mobile_visualviewport

要注意,CSS layout,特别是用百分比定义的 layout,都是用 layout viewport 来算的,一般来说比 visual viewport 要宽很多。

因此,起始阶段<html>会占据整个 layout viewport 的宽度,你用 CSS 定义的元素的尺寸会比手机屏幕的尺寸要宽,这主要是要模拟网站在手机浏览器上的表现。

那么 layout viewport 究竟有多宽?这个要看具体的手机浏览器。Safari 的 iPhone 版为 980px,Opera 是 850px,Android 的 Webkit 是 800px,而 IE 是 974px。还有一些其他的浏览器除开特别的宽度之外有特别的行为,比如 Symbian 上的 Webkit 会尽量保持 layout viewport 和 visual viewport 相同。当 layout viewport 超过了 850px 的时候,两者才会变得不同。

缩放

很明显,visual 和 layout 两种 viewport 都使用了 CSS 像素,但是 layout viewport 是不会因为缩放操作变化的,而 visual viewport 会。很多的移动设备上的浏览器,在默认情况下都是以完全 zoom-out 的尺寸来显示页面的——结合前面的对两种 viewport 的简介,你可以认为这种时候你是把头伸到窗口(visual viewport)外在看窗外那副大图片(layout viewport),这种情况下两个 viewport 也是相等的(没有被窗框遮住的部分)。

因此,layout viewport 的宽和高等于在完全 zoom-out 的情况下 screen 上最大可显示的 CSS 像素值大小。因此在随后用户如果 zoom-in 了这个值是不会变化的。

下面的图表示了这个变化:在刚开始打开一个页面的时候,处于完全 zoom-out 的状态,layout viewport 的值等于 visual viewport 的值。随后,当用户 zoom-in(放大页面)时,每个 CSS 像素变成数倍于设备像素,而整个 layout viewport 的值不变(这样才保证你为页面定义的 CSS 长宽有意义),于是 layout viewport 的物理范围扩大了不少。

mobile_viewportzoomedout                              mobile_layoutviewport

另外要注意的就是 layout viewport 的宽度是保持不变的。也就是说在默认情况下,竖屏转横屏的时候,虽然 visual viewport 变化了,但是浏览器会自动的 zoom-in 一些来使得 layout viewport 和 visual viewport 仍然相等。这种情况下,layout viewport 的高度会变小,但是,对于 web 开发者而言,最重要的是宽度能保持不变。

mobile_viewportzoomedout            mobile_layoutviewport_la

测量layout viewport

document.documentElement.clientWidth/Height得到的是 layout viewport 的数值。并且,如前所述,横竖屏转换时,只会影响 Height 不会影响 Width 取值。

document.documentElement.clientWidht/Height

  • Layout viewport
  • 使用CSS像素值为单位
  • 支持Opera、iPhone、Android、Symbian、Bolt、MicroB、Skyfire和Obigo
  • 有一些问题:
  1. Samsung的Webkit的viewport只有当显式指定了的时候才有效,否则返回的是元素的长宽 </div>
  2. Firefox返回的是以设备像素值为单位的结果
  3. IE返回1024×768,正确的信息要通过document.body.clientWidth/Height获取

测量visual viewport

window.innerHeight/Width返回的是 visual viewport。很显然,当用户缩放的时候,这两个值会变化,因为有更多或者更少的 CSS 像素适配到屏幕可见的部分中。

window.innerHeight/Width

  • visual viewport
  • 使用CSS像素值为单位
  • 支持iPhone、Symbian、BlackBerry
  • 有一些问题:
  1. Samsung的Webkit返回的是layout viewport的取值只有当显式指定了的时候才有效,否则返回的是元素的长宽 </div>
  2. Firefox和Opera返回的是以设备像素值为单位的结果
  3. IE不支持,正确的信息要通过document.documentElement.offsetWidth/Height获取
  4. 其他的如Iris、Skyfire或者Obigo是有返回值到乱来的

手机屏幕大小

前面提到过,在桌面系统,screen.width/hieght 返回屏幕的大小,但是和桌面系统一样,开发者都不关心这个值:因为物理上屏幕多大并不重要,我们更关心多少个 CSS 像素能弄进这个屏幕。

<html>元素

通过document.documentElement.offsetWidth/Height可以获取<html>元素的 CSS 像素单位的尺寸。

缩放比例

当 screen.width 和 window.innerWidth 在你工作的平台都得到了正确实现的时候,两者相除就可以得到缩放的比例大小。

Media query

Media query 的思想就是定义只在页面大于,等于或者是小于一定尺寸的时候,才被执行的 CSS 规则。比如:

div.sidebar {
    width: 300px;
}

@media all and (max-width: 400px) {
    // styles assigned when width is smaller than 400px;
    div.sidebar {
        width: 100px;
    }

}

规则指定了当宽度小于 400px 的时候 sidebar 是 100px,其余时候是 300px。

需要注意的是在定义 media query 的时候可以用两套长宽:css 里的 width/height 表示的是 documentElement. clientWidth/Height 取值,也就是 viewport,单位是 css 像素。css 里的 device-width/height 表示的是 screen.width/height,单位是物理像素。

那么使用哪种值来定义 media query 甚至用不用它都是让人疑惑的问题。比如你用 device-width 的话 ,其实效果和使用来定义是类似的。但是用 width,仅仅是一个模糊的像素宽度,更加缺乏在各种设备上具体效果的准确预期。目前来看,用 media query 应该能比较正确的区分运行环境是桌面,手机还是平板,但是并不太能细致到是那款平板或者是什么手机。

Meta viewport

终于可以来讨论了。这个最早是 Apple 的一个扩展,但是现在已经被各种浏览器抄去了。它主要的作用是用来重定义 layout viewport 的大小。

我们通过实例来解释:假设你开发了一个网站,没有写 css,因此所有的元素没有 width 属性。打开页面的时候,它们会默认 zoom-out 到最大,看起来像这样:

mq_none

用户肯定会 zoom-in,这时因为很多浏览器会保持每个元素的宽度仍然是 100%的 layout viewport,因此,很多文字就撑到 visual viewport 外面去了变得不可见了(Android 原生的 Webkit 在这种情况下会自动调整 text-containing 元素的宽度来适配屏幕宽度)。

mq_none_zoomed

为了避免这种情况,你可能会加上 html {width:320px}的 css 规则。这样<html>的宽度,也就是大多数未定义宽度的元素继承到的宽度,是 320px。这样的规则可以解决用户 zoom-in 放大页面之后溢出的问题,但是当用户刚打开页面的时候,大概又会看的下图的效果(因为 300 个 CSS 像素值在完全 zoom-out 的情况下是很窄的):

mq_html300

为了解决这种初始和用户缩放之后的不和谐,Apple 定义了一个新的 meta 值。你可以用

<meta name="viewport" content="width=320"/>

来确定layout viewport的宽度。这样在初始的时候,整个页面看起来仍然是非常正确的:

mq_yes

meta 中定义的 layout viewport 宽度甚至可以是 device-width,也就是以设备像素值标定的screen.width取值。但是这里有一个陷阱,就是有时候正式的screen.width的取值并不是真的有实际意义,因为像素值可能太高。比如 Nexus One 的屏幕宽度是 480px,但是 Google 的工程师觉得用device-width来定义viewport就让layout viewport取值 480px 太宽了,于是它们让 device-width 的返回值打了个 2/3 的折扣,只有 320px,和 iphone 一致。这样一来,device-width的取值又有点儿 CSS 像素值的意思了。这种 resolution 标称的像素值和device-width的返回值之间的关系被称为CSS pixel density

</table>

Device resolution (px) device-width/ device-height (px)
iPhone 320 x 480 320 x 480, portrait/landscape mode
iPhone 4 640 x 960

320 x 480, in both portrait and landscape mode

CSS pixel density  = 2 </td> </tr>

iPad 1 and 2 768 x 1024 768 x 1024, portrait/landscape mode
new iPad 1536 x 2048

768 x 1024, portrait/landscape mode

CSS pixel density = 2 </td> </tr>

Samsung Galaxy S I and II 480 x 800

320 x 533, portrait mode

CSS pixel density = 1.5 </td> </tr>

Samsung Galaxy S III 720 x 1280 360? x 640?, portrait mode
HTC Evo 3D 540 x 960

360 x 640, portrait mode

CSS pixel density = 1.5 </td> </tr>

Amazon Kindle Fire 1024 x 600 1024 x 600, landscape mode

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…!!