Python 语句中有一些基本规则和特殊字符:
井号(#)表示之后的字符为 Python 注释
换行 (\n) 是标准的行分隔符(通常一个语句一行)
反斜线 ( \ ) 继续上一行,或转义
分号 ( ; )将两个语句连接在一行中
冒号 ( : ) 将代码块的头和体分开
语句(代码块)用缩进块的方式体现
不同的缩进深度分隔不同的代码块
Python 文件以模块的形式组织
Python 注释语句从 # 字符开始,注释可以在一行
的任何地方开始,解释器会忽略掉该行 # 之后的所有内容。要正确的使用注释
Python 语句,一般使用换行分隔,也就是说一行一个语句。一行过长的语句可以使用反斜
杠( \ ) 分解成几行,如下例:
# check conditions
if (weather_is_hot == 1) and \
(shark_warnings == 0):send_goto_beach_mesg_to_pager()
有两种例外情况一个语句不使用反斜线也可以跨行:
在使用闭合操作符时,单一语句可以跨多行,例如:在含有小括号、中括号、花括号时可以多行书写。
另外就是三引号包括下的字符串也可以跨行书写。
:)缩进相同的一组语句构成一个代码块,我们称之代码组。像 if、while、def 和 class 这样的复合语句,首行以关键字开始,以冒号( : )结束,该行之后的一行或多行代码构成代码组。我们将首行及后面的代码组称为一个子句(clause)。
对一个初次使用空白字符作为代码块分界的人来说,遇到的第一个问题是:缩进多大宽度才合适?两个太少,六到八个又太多,因此我们推荐使用四个空格宽度。需要说明一点,不同的文本编辑器中制表符代表的空白宽度不一,如果你的代码要跨平台应用,或者会被不同的编辑器读写,建议你不要使用制表符。使用空格或制表符这两种风格都得到了 Python 创始人 Guido van Rossum 的支持,并被收录到 Python 代码风格指南文档。
;)分号( ; )允许你将多个语句写在同一行上,语句之间用分号隔开,而这些语句也不能在这行开始一个新的代码块。Python 虽然允许但不提倡你这么做。
每一个 Python 脚本文件都可以被当成是一个模块。模块以磁盘文件的形式存在。当一个模块变得过大,并且驱动了太多功能的话,就应该考虑拆一些代码出来另外建一个模块。模块里的代码可以是一段直接执行的脚本,也可以是一堆类似库函数的代码,从而可以被别的模块导入(import)调用。模块可以包含直接运行的代码块、类定义、函数定义或这几者的组合。
Python 语言中, 等号(=)是主要的赋值运算符。
注意:赋值并不是直接将一个值赋给一个变量, 尽管你可能根据其它语言编程经验认为应该如此。在 Python 语言中,对象是通过引用传递的。在赋值时,不管这个对象是新创建的,还是一个已经存在的,都是将该对象的引用(并不是值)赋值给变量。如果此刻你还不是 100%理解清楚,也不用着急。
由于变量赋值是对象的引用,所以如果交换两个变量的值,可以直接交换,不需要第三个变量
x,y=y,x
而不必tmp=x,x=y,y=tmp这么麻烦
Python 的赋值语句不会返回值。类似下面的语句在 Python 中是
非法的:
>>> y=(x=1)File "", line 1y=(x=1)^
SyntaxError: invalid syntax
链式赋值没问题
>>> y = x = x + 1
>>> x, y
(2, 2)
等号可以和一个算术运算符组合在一起, 将计算结果重新赋值给左边的变量。这被称为增量赋值, 类似下面这样的语句:
x = x + 1
现在可以被写成:
x += 1
增量赋值通过使用赋值运算符,将数学运算隐藏在赋值过程当中
+= -= *= /= %= <<= >>= &= ^= |= **=
增量赋值相对普通赋值不仅仅是写法上的改变,最有意义的变化是第一个对象仅被处理一次。可变对象会被就地修改(无修拷贝引用), 不可变对象则和 A = A + B的结果一样,分配一个新对象
>>> x = y = z = 1
一个值为 1 的整数对象被创建,该对象的同一个引用被赋值给 x、y 和z 。也就是将一个对象赋给了多个变量。
另一种将多个变量同时赋值的方法我们称为多元赋值(multuple),采用这种方式赋值时, 等号两边的对象都是元组
>>> x, y, z = 1, 2, 'a string'
建议总是加上圆括号以使得你的代码有更高的可读性。
>>> (x, y, z) = (1, 2, 'a string')
可以理解为元组解析
标识符是电脑语言中允许作为名字的有效字符串集合。其中,有一部分是关键字,构成语言的标识符。这样的标识符是不能做它用的标识符的,否则会引起语法错误(SyntaxError 异常)。
Python 还有称为 built-in 标识符集合,虽然它们不是保留字,但是不推荐使用这些特别的名字
合法的 Python 标识符
Python 标识符字符串规则和其他大部分用 C 编写的高级语言相似:
第一个字符必须是字母或下划线(_)
剩下的字符可以是字母和数字或下划线
大小写敏感
一般来说,任何语言的关键字应该保持相对的稳定,但是因为 Python 是一门不断成长和进化的语言,关键字列表和 iskeyword()函数都放入了 keyword模块以便查阅。
>>> import keyword
>>> print keyword.iskeyword('in')
True
>>> print keyword.iskeyword('type')
False
>>> print keyword.iskeyword('for')
True
常用关键字,包括
1、内置常量:False、None、True
2、逻辑与 或 非 and or not,优先级:not and or
3、判断 与 循环:if elif else;is in;for while break continue
4、函数:def lambda;pass return yied
5、异常处理:try except finally raise assert
6、导入模块 包:import from
7、重命名:as
8、变量:global nonlocal
9、类:class
10、删除:del
11、上下文管理:with
除了关键字之外,Python 还有可以在任何一级代码使用的“内建”的名字集合,这些名字可以由解释器设置或使用。虽然 built-in 不是关键字,但是应该把它当作“系统保留字”,不做他用。然而,有些情况要求覆盖(也就是:重定义,替换)它们。Python 不支持重载标识符,所以任何时刻都只有一个名字绑定。我们还可以告诉高级读者 built-in 是__builtins__模块的成员,在你的程序开始或在交互解释器中给出>>>提示之前,由解释器自动导入的。把它们看成适用在任何一级 Python 代码的全局变量。
Python 用下划线作为变量前缀和后缀指定特殊变量
_xxx 无法通过’from module import *'导入
__xxx__系统定义名字
__xxx 类中的私有变量名
避免用下划线作为变量名的开始
因为下划线对解释器有特殊的意义,而且是内建标识符所使用的符号,我们建议程序员避免用下划线作为变量名的开始。一般来讲,变量名_xxx 被看作是“私有的”,在模块或类外不可以使用。当变量是私有的时候,用_xxx 来表示变量是很好的习惯。因为变量名__xxx__对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。
注释对于自己和后来人来说都是非常重要的,特别是对那些很久没有被动过的代码而言,注释更显得有用了。既不能缺少注释,也不能过度使用注释。尽可能使注释简洁明了,并放在最合适的地方。这样注释便为每个人节省了时间和精力。记住,要确保注释的准确性。
Python 还提供了一个机制,可以通过__doc__特别变量,动态获得文档字串。在模块,类声明,或函数声明中第一个没有赋值的字符串可以用属性 obj.__doc__来进行访问,其中 obj是一个模块,类,或函数的名字。这在运行时刻也可以运行。
因为缩进对齐有非常重要的作用,您得考虑用什么样的缩进风格才让代码容易阅读。在选择要空的格数的时候,常识也起着非常大的作用。当使用制表符 Tab 的时候,请记住不同的文本编辑器对它的设置是不一样。推荐您不要使用 Tab,如果您的代码会存在并运行在不同的平台上,或者会用不同的文本编辑器打开,推荐您不要使用 Tab。
好的判断也适用于选择标志符名称,请为变量选择短而意义丰富的标识符。虽然变量名的长度对于今天的编程语言不再是一个问题,但是使用简短的名字依然是个好习惯,这个原则同样使用于模块(Python 文件)的命名。
>>> import this
The Zen of Python, by Tim PetersBeautiful 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!
用模块来合理组织你的 Python 代码是简单又自然的方法。你应该建立一种统一且容易阅读的结构,并将它应用到每一个文件中去。下面就是一种非常合理的布局:
# (1) 起始行(Unix)
# (2) 模块文档
# (3) 模块导入
# (4) 变量定义
# (5) 类定义
# (6) 函数定义
# (7) 主程序
主程序代码通常都和你前面看到的代码相似,检查 __name__ 变量的值然后再执行相应的调用。主程序中的代码通常包括变量赋值, 类定义和函数定义,随后检查__name__来决定是否调用另一个函数(通常调用 main()函数)来完成该模块的功能。主
程序通常都是做这些事。不管用什么名字,我们想强调一点那就是:这儿是放置测试代码的好地方。大部分的 Python 模块都是用于导入调用的,直接运行模块应该调用该模块的回归测试代码。
时刻记住一个事实,那就是所有的模块都有能力来执行代码。最高级别的 Python 语句–也就是说, 那些没有缩进的代码行在模块被导入时就会执行, 不管是不是真的需要执行。由于有这样一个“特性”,比较安全的写代码的方式就是除了那些真正需要执行的代码以外, 几乎所有的功能代码都在函数当中。再说一遍, 通常只有主程序模块中有大量的顶级可执行代码,所有其它被导入的模块只应该有很少的顶级执行代码,所有的功能代码都应该封装在函数或类当中。
__name__ 指示模块应如何被加载由于主程序代码无论模块是被导入还是被直接执行都会运行, 我们必须知道模块如何决定运行方向。一个应用程序可能需要导入另一个应用程序的一个模块,以便重用一些有用的代码。这种情况下,你只想访问那些位于其它应用程序中的代码,而不是想运行那个应用程序。因此一个问题出现了,“Python 是否有一种方法能在运行时检测该模块是被导入还是被直接执行呢?” 答案就是…(鼓声雷动)…没错! __name__ 系统变量就是正确答案。
如果模块是被导入, __name__ 的值为模块名字
如果模块是被直接执行, __name__ 的值为 '__main__'
变量无须事先声明
变量无须指定类型
程序员不用关心内存管理
变量名会被“回收”
del 语句能够直接释放资源
在 Python 中,无需此类显式变量声明语句,变量在第一次被赋值时自动声明。和其他大多数语言一样,变量只有被创建和赋值后才能被使用。
还要注意一点,Python 中不但变量名无需事先声明,而且也无需类型声明。Python 语言中,对象的类型和内存占用都是运行时确定的。尽管代码被编译成字节码,Python 仍然是一种解释型语言。在创建–也就是赋值时,解释器会根据语法和右侧的操作数来决定新对象的类型。在对象创建后,一个该对象的引用会被赋值给左侧的变量。
作为一个负责任的程序员,我们知道在为变量分配内存时,是在借用系统资源,在用完之后, 应该释放借用的系统资源。Python 解释器承担了内存管理的复杂任务, 这大大简化了应用程序的编写。你只需要关心你要解决的问题,至于底层的事情放心交给 Python 解释器去做就行了。
要保持追踪内存中的对象, Python 使用了引用计数这一简单技术。也就是说 Python 内部记录着所有使用中的对象各有多少引用。一个内部跟踪变量,称为一个引用计数器。至于每个对象各有多少个引用, 简称引用计数。当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为 0 时, 它被垃圾回收。(严格来说这不是 100%正确,不过现阶段你可以就这么认为)
当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为 1。当同一个对象(的引用)又被赋值给其它变量时,或作为参数传递给函数, 方法或类实例时, 或者被赋值为一个窗口对象的成员时,该对象的一个新的引用,或者称作别名,就被创建
(则该对象的引用计数自动加 1)。
当对象的引用被销毁时,引用计数会减小。最明显的例子就是当引用离开其作用范围时,这种情况最经常出现在函数运行结束时,所有局部变量都被自动销毁,对象的引用计数也就随之减少。
当变量被赋值给另外一个对象时,原对象的引用计数也会自动减 1
foo = 'xyz'
bar = foo
foo = 123
当字符串对象"xyz"被创建并赋值给 foo 时, 它的引用计数是 1. 当增加了一个别名 bar时, 引用计数变成了 2. 不过当 foo 被重新赋值给整数对象 123 时, xyz 对象的引用计数自动减 1,又重新变成了 1.其它造成对象的引用计数减少的方式包括使用 del 语句删除一个变量(参阅下一节), 或者当一个对象被移出一个窗口对象时(或该容器对象本身的引用计数变成了 0 时)。 总结一下,一个对象的引用计数在以下情况会减少:一个本地引用离开了其作用范围。比如 foobar()(参见上一下例子)函数结束时。对象的别名被显式的销毁。
del y #or del x
对象的一个别名被赋值给其它的对象
x = 123
对象被从一个窗口对象中移除
myList.remove(x)
窗口对象本身被销毁
del myList# or goes out-of-scope
del 语句会删除对象的一个引用,它的语法是:
del obj1[, obj2[,... objN]]
例如,在上例中执行 del y 会产生两个结果:
从现在的名字空间中删除 y
x 的引用计数减一
引申一步, 执行 del x 会删除该对象的最后一个引用, 也就是该对象的引用计数会减为0, 这会导致该对象从此“无法访问”或“无法抵达”。 从此刻起, 该对象就成为垃圾回收机制的回收对象。 注意任何追踪或调试程序会给一个对象增加一个额外的引用, 这会推迟该对象被回收的时间。
不再被使用的内存会被一种称为垃圾收集的机制释放。
像上面说的, 虽然解释器跟踪对象的引用计数, 但垃圾收集器负责释放内存。垃圾收集器是一块独立代码, 它用来寻找引用计数为 0 的对象。它也负责检查那些虽然引用计数大于 0 但也应该被销毁的对象。
特定情形会导致循环引用。一个循环引用发生在当你有至少两个对象互相引用时, 也就是说所有的引用都消失时, 这些引用仍然存在, 这说明只靠引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。
当一个对象的引用计数变为 0,解释器会暂停,释放掉这个对象和仅有这个对象可访问(可到达)的其它对象。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(及未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环。
import os
ls = os.linesep
核心技巧:类似 os.linesep 这样的名字需要解释器做两次查询:
(1)查找 os 以确认它是一个模块,
(2)在这个模块中查找 linesep 变量。因为模块也是全局变量, 我们多消耗了系统资源。如果你在一个函数中类似这样频繁使用一个属性,我们建议你为该属性取一个本地变量别名。 变量查找速度将会快很多–在查找全局变量之前, 总是先查找本地变量。 这也是一个让你的程序跑的更快的技巧: 将经常用到的模块属性替换为一个本地引用。代码跑得更快,而也不用老是敲那么长的变量名了。在我们的代码片段中,并没有定义函数,所以不能给你定义本地别名的示例。不过我们有一个全局别名,至少也减少了一次名字查询
Python 代码风格指南(PEP8), Python 快速参考和 Python 常见问答都是开发者很重要的“工具”。
Debugger: pdb
Logger: logging
Profilers: profile, hotshot, cProfile
调试模块 pdb 允许你设置(条件)断点,代码逐行执行,检查堆栈。它还支持事后调试。
logging 模块定义了一些函数和类帮助你的程序实现灵活的日志系统。共有五级日志级别: 紧急, 错误,警告,信息和调试。
最早的 Python profile 模块是 Python 写成的,用来测试函数的执行时间,及每次脚本执行的总时间,既没有特定函数的执行时间也没有被包含的子函数调用时间。在三个 profile 模块中,它是最老的也是最慢的,尽管如此, 它仍然可以提供一些有价值的性
能信息。 hotshot 模块是在 Python2.2 中新增的,它的目标是取代 profile 模块, 它修复了profile 模块的一些错误, 因为它是用 C 语言写成,所以它有效的提高了性能。 注意 hotshot重点解决了性能测试过载的问题, 但却需要更多的时间来生成结果。Python2.5 版修复了hotshot 模块的一个关于时间计量的严重 bug。cProfile 模块是 Python2.5 新增的, 它用来替换掉已经有历史的 hotshot 和 profile 模块。被作者确认的它的一个较明显的缺点是它需要花较长时间从日志文件中载入分析结果, 不支持子函数状态细节及某些结果不准。它也是用 C 语言来实现的。