Python学习笔记

使用Python解释器
调用Python解释器

Ubuntu下,Python解释器的默认安装位置为/usr/bin/python;Windows下是C:\python27。需要将其添加到环境变量PATH中,然后打开Shell窗口:

参数传递

调用解释器时,脚本名、参数传入字符串列表sys.argv中,该列表至少有一个元素。

  1. 没有给定脚本名、参数时,sys.argv[0] = ''
  2. 脚本名指定为标准输入时,sys.argv[0] = '-'
  3. 使用-c参数调用解释器时,sys.argv[0] = '-c'
  4. 使用-m参数调用解释器时,sys.argv[0]  = 模块全名
交互模式

从终端读取命令并执行,称为交互模式,以主提示符(>>>)为依据执行,输入多行结构,则需要附加从属提示符(...),例如:

解释器及其环境
错误处理

有错误发生时,解释器在stderr上打印错误信息、调用栈跟踪。在交互模式下,会返回主提示符;如果从文件输入执行,则以非零状态退出。
使用try的except子句可以捕获异常

执行Python脚本

Linux下,Python脚本可以直接执行(需要chmod +x):

在Windows下,安装程序会把*.py与python.exe关联,可以双击执行,*pyw类似,但是不显示控制台窗口

交互执行文件

如果需要让解释器在每次启动时均执行一个脚本,可以设置环境变量:PYTHONSTARTUP,这类似于Linux Shell的.profile文件

本地化模块

钩子方法sitecustomize、usercustomize用于提供本地化。

Python语言基础
基于缩进的代码风格

Python的每个语句以换行符结束,如果太长,可以使用续行符(反斜杠)跨行

使用三引号定义的字符串、列表、元组或字典分布在多行上时,不需要使用续行符。

缩进用于表示不同的代码块,如函数体、条件语句、循环和类。代码块中首条语句的缩进可以任意的,但是后续语句必须与之保持一致。

如果函数体、分支、循环等较短,可以放在一行,不需要缩进:

应当使用空格,而不是制表符进行缩进。

标识符和保留字
标识符

变量标识符仅支持:数字、下划线、A-Za-z,并且数字不能作为标识符的开头。标识符区分大小写。

以下划线开始或结束的标识符具有特殊含义:

  1. 以单下划线开头的标识符不能通过from module import *导入
  2. __func__用于定义特殊方法
  3. __priv用于定义类私有成员
基础数据类型
数字(int、Long、float、complex、bool)
布尔值

标识符True和False被解释为布尔值,其整数值分别是1和0。

序列类型

字符串、列表、元组,统称为序列类型。序列类型具有共同的特征:

  1. 支持索引访问,例如s[0]
  2. 支持切片运算符,例如s[0:5],对于可变序列,还可以删除切片,例如del s[i:j]
  3. 使用len(s)可以返回序列长度
  4. 使用min(s)、max(s)可以返回序列中元素的最小最大值
  5. 使用all(s)、any(s)可以检查是否每个元素、存在任何元素为True
字符串(str、unicode)

支持双引号或者单引号的字符串。Python字符串是不可变的。

Python 2.0以后引入Unicode,来表示Unicode字符串,必要时可以与原始字符串进行转换:

利用str()函数进行转换时,会使用默认编码(通常是ASCII):

可以使用encode方法来获取特定编码的16进制转写:

列表(list)
元组(tuple)

在圆括号里面包含一组值,即为元组。元组创建后,不能修改其内容(替换、添加或者删除元素)。使用元组代替小列表,更加节约内存

集合(set、frozenset)

集合是无序的、不包含重复元素的对象组:

字典(dict)

字典就是关联数组(散列表)。字符串、元组等可以作为散列键,但是可变对象例如列表、字典则不可以作为键。

None

None是一个特殊的类型,用于表示null对象。
如果一个函数没有显式的返回值,则自动返回None。布尔求值时为False

流程控制
if分支结构
for循环结构
函数
函数的属性
 属性 说明 
__doc__ 文档字符串
__name__ 函数名称 
__dict__ 包含函数属性的字典
__code__ 字节编译的代码
__defaults__ 默认参数的元组
__globals__ 定义函数的全局命名空间,即定义了函数的模块中所有全局变量/函数构成的字典
__closure__ 嵌套作用域相关数据的元组
方法的属性
__class__ 定义方法的类
__func__ 实现方法的函数对象
__self__ 与方法相关的实例,如果是非绑定方法,则为None
常用内置函数
 函数 说明 
abs (x) 返回绝对值
all(s) 如果可迭代的s中的所有值都为True,则返回True
any(s) 如果可迭代的s中的任意值为True,则返回True
ascii(x) 类似repr(),创建对象的可打印格式,但是只使用ASCII字符,非ASCII字符使用转义序列
bin(x) 返回一个字符串,其中包含整数二进制形式
bool([x]) 转换为布尔型
bytearray ([x]) 可变字节数组,x可能是范围从0到255的可迭代整数序列、8位字符串或字节字面量
bytearray(s,encoding) 从字符串创建字节数组,使用指定的编码
bytes ([x]) 表示不变字节数组
chr(x) 将整数值转换为单字符的字符串。在Python 2中,x必须在0 <= x <= 255范围内
classmethod(func) 用于创建类方法,@classmethod装饰器隐式调用它
cmp(x, y) 比较两个对象,如果x>y返回正数,相等返回0
compile(string) 编译字符串为代码
complex(real,img) 创建复数 
delattr(obj, attr) 等同于del obj.attr
dict([m]) 创建字典
dir([object]) 返回属性名的有序列表。用户可以通过定义__dir__()方法改变此方法的行为
divmod(a, b) 返回商和余数 
enumerate(iter) 给定可迭代对象iter,返回新迭代器,迭代元素形式为(index,el)的元组
eval(expr) 计算表达式的值
exec(code) 执行指定的代码 
filter(function, iterable) 在Python2中,创建来自iterable的元素的列表,对这些元素调用function结果为True则包含在结果列表中
float([x])  创建浮点数
format (val, [,fmt_spec]) 格式化字符串
frozensett[items]) 不变集合对象
getattr(obj, name,default) 返回对象的一个命名属性的信
globals() 返回代表当前模块全局命名空间的字典
hasattr(object, name) 如果object具有属性name,则返回True
hash(object) 返回对象的整数散列值
hex(x) 根据整数x创建一个十六进制字符串
id(object) 返回object对象的唯一标识,通常为内存地址
input([prompt]) 在Python 2中,该函数打印一个提示符,读取输入行并通过eval对其进行处理
int(x [,base]) 创建整数
isinstance(obj, cls) 如果obj是cls或者其子类的实例
issubclass(class1, class2) 如果class1是class2的子类,或者class1是基于抽象基类class2册的,则返回True
iter(object [,sentinel]) 返回object的迭代器,如果不指定sentinel,则object必须具有__iter__或者__getitem__方法
len(s) 返回s中包含的项数,s是列表、元组、字符串、集合或字典
list([items])  根据可迭代对象items创建列表
locals() 返回当前函数的本地命名空间构成的字典
long([x [,base]]) 在Python 2中表示长整数的类型。为了可移植性考虑,应当避免直接使用long
map(function, items, ...) 在Python 2中,该函数将function应用到items的每一项并返回结果列表,Python3则返回迭代器
max(s [, args, ...])

如果只有一个参数s,该函数返回s中各项的最大值,s可以是任意可迭代的对象。如果有多个参数,它返回参数中的最大值。min(s [, args, ...])类似

next(s [, default]) 返回迭代器s中的下一项。如果该迭代器没有下一项,则引发Stopiteration异常(除非指定default)
object() Python中所有对象的基类。可以调用它创建一个实例
oct (x) 将整数转换为一个八进制字符串
open(file [,mode[,bufsize]]) 在Python2中,打开文件返回一个新文件对象
ord(c) 返回字符c的整数序值。如果是普通字符,返回范围在[0,255]内的值。如果是单个Unicode字符, 通常返回范围在[0,65535]的值
pow(x, y [, z]) 返回x ** y。如果提供了z,则该函数返回(x ** y) % z
property ( [fget [, fset [,fdel [,doc]]]]) 创建类的property属性。get是返回属性值的函数,fset设置属性值,而fdel删除一个属性。doc表示文档字符
range([start, ] stop [, step]) 在Python 2中,该函数创建一个完全填充的、从start到stop的整数列表
raw_input ([prompt]) Python 2函数,从标准输入读取一行输入并将其作为字符串返回
repr(object) 返回的字符串表示形式。在大多数情况下,返回的字符串是可以传递到eval()的表达式
reversed (s) 创建序列s的逆序迭代器。只有当s实现了序列方法__len__()、__getitem()__才可用
round(x [, n]) 将浮点数舍五入到最近的10的负n次幂倍数后再四舍五入 
set([items]) 创建一个使用从可迭代对象items得到的各项来填充的桌合
setattr(object, name, value) 设置对象的属性。name是字符串。与object.name = value相同
slice([start,] stop [, step]) 返回表示指定范围内整数的切片对象。等同于扩展切片语法 [ i: j: k]
staticmethod (func) 创建在类中使用的静态方法。通过@staticmethod装饰器隐式调用该函数
str([object]) 表示字符串的类型。在Python 2中,一个字符串包含8位字符
sum(items,[, initial]) 计算从可迭代对象items中得到的所有项的总数。initial是初始值,默认是0
super(type [,object]) 返回表示type基类的对象。该对象的主要用途是调用基类中的方法
tuple([items]) 表示元组的类型。如果提供了items,则它是用于填充该元组的可迭代对象
type(object) 返回对象的类型
type (name, bases, dict) 创建一个新type对象(相当于定义一个新类)
unichr (x) 将整数或长整数换为一个Unicode字符
vars([object]) 返回object的符号表(通常在它的__dict__属性中)
xrange([start,] stop [, step]) 表示从start到stop的整数值范围的类型,该范围不包括start和stop。step是可选的步进值
zip([s1 [, s2[,..]]]) 在Python 2中,返回一些元组的列表,其中第n个元组是(sl[n], s2[n],…)。生成的列表被截取为最短参数序列的长度。如果没有给定参数,则返回一个空列表
方法

所谓方法是指在类定义中定义的函数。包含实例方法、类方法、静态方法三种:

生成器(Generator)

如果一个函数里面具有yield关键字,则称为生成器。调用生成器得到返回值是一个迭代器对象,这一调用本身不会执行生成器的任何代码。

如果生成器中存在return语句,则执行到return时抛出StopIteration并终止迭代。

协程

上面的生成器,实际上是一种协程,协程与普通函数(Subroutine/function)的执行方式有很大的不同:

  1. 子例程的起始处是惟一的入口点,一旦退出即完成了子例程的执行,子例程的一个实例只会返回一次
  2. 协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是一种对等关系
  3. 协程的起始处是第一个入口点,协程返回点之后(yield之后一句)是接下来的入口点
  4. 子例程的生命期遵循后进先出(最后一个被调用的子例程最先返回),而协程的生命周期完全由他们的使用的需要决定

下面是一个使用协程的例子:两个吃货到餐馆用餐,每次上完菜后就马上被他们吃掉,并继续索要新的食物,一个厨师、一个侍者为他俩服务:

类与对象
新式类和旧式类

所谓新式类是指从object或者其它新式类衍生出的类,而旧式类是Python2中没有明确指定基类的类。Python3中只有新式类。

Python中所有变量皆为对象。使用 __func__这样形式的函数名来实现特殊方法(例如list的__add__实现了+运算符)。

类型对象的属性
 属性 说明 
__doc__ 文档字符串
__name__ 类的名称
__bases__ 基类的元组
__dict__ 保存类方法、变量的字典
__module__ 定义类的模块名称
__abstractmethods__ 抽象方法的集合
影响对象行为的特殊方法
 属性 说明 
__new__(cls [,*args [,**kwargs]]) 类方法,用于创建实例
__init__(self [,*args [,**kwargs]]) 初始化对象属性,在创建对象后立即被调用
__del__(self) 销毁实例时调用
__format__(self, format_spec) 格式化后的表示
__repr__(self) 字符串表示,某些类允许使用 eval(repr(o)) 创建对象
__str__(self)

返回对象的字符串表示,该方法在Python2中返回的是“字节”,在Python3中返回的是字符

在Python2中,print语句和str()函数会调用此方法

__unicode__(self)

返回对象的字符串表示,该方法返回的是“字符”。在Python2中,为了编码相关的兼容性,你应当把对象格式化代码放在该方法里,而把__str__创建为一个存根方法:

在Python2中,unicode()函数会调用此方法

在Python3中,此方法没有价值

__bool__(self) 真值测试
__hash__(self) 计算整数散列值
__lt__

小于,类似还有__le__、__gt__、__ge__、__eq__、__ne__
如果要用作字典键,或者根据==比较大小,则必须实现__eq__

__instancecheck__(cls,object) 修改isinstance(object,cls)的行为
__subclasscheck__(cls,sub) 修改issubclass(sub,cls)的行为
__getattribute__(self,name) 返回属性self.name时调用,调用此方法时,Python尚未查找对象的真实属性
__getattr__(self,name) 仅仅在常规方式找不到属性时调用
__setattr__(self, name, value) 设置self.name=value时调用
__delattr__(self, name) 删除self.name时调用
__len__(self) 返回长度
__getitem__(self,key) 获得self[key]
__setitem__(self,key,value) 设置self[key] = value
__delitem__(self,key) 删除self[key]
__contains__(self,obj) 如果包含,则返回真
__iter__()

如果对象支持迭代,从该方法返回迭代器对象,迭代器必须实现next()方法

__next__() Python3的迭代器方法,在Python2中为next()
__add__(self, other) self + other
__sub__(self, other) self - other
__mul__ (self, other) self * other
__div__ (self, other) self / other
__floordiv__(self, other) self // other
__mod__ (self, other) self % other
__pow__(self, other, [,modulo]) self ** other, pow(self,other,modulo)
__lshift__(self, other) self << other
__rshift__(self, other) self >> other
__and__(self, other) self  & other
__or__(self, other) self  | other
__xor__(self, other) self  ^ other
__r**(self, other) other ** self 
__i**(self, other) self **= other
__neg__(self) -self  
__pos__(self) +self
__abs__(self) abs(self)
__invert__(self) ~self
__int__(self) int(self)
__long__(self) long(self)
__float__(self) float(self)
__complex(self) complex(self)
__call__(self[, *args[,*kwargs]]) 函数对象
对象实例的属性
 属性 说明 
__class__ 实例所属的类,可以使用tpye(o)得到
__dict__ 所有实例变量构成的字典
异常

如果一个Python程序出现错误,则会引发异常,打印类似下面的信息:

错误信息中包含了错误的类型、出错的代码位置。如果不做任何处理,异常会导致程序终止,除非使用try-except语句:

未捕获的异常将向上传递,如果到程序最顶级仍然没有处理,则导致解析器终止并打印消息。可以把未捕获的异常传递给用户自定义的 sys.excepthook() 函数进行处理。

可以使用try-except-else语句块,else在没有引发异常时执行;可以使用try-except-finally语句块,finally总是执行:

内置异常

Python预定义了以下异常:

异常 描述

BaseException

GeneratorExit

Keyboardlnterrupt

SystemExit

Exception

StopIteration

StandardError

ArithmeticError

FloatingPointError

ZeroDivisionError

AssertionError

AttributeError

EnvironmentError

IOError

OSError

EOFError

ImportError

LookupError

IndexError

KeyError

MemoryError

NameError

UnboundLocalError

ReferenceError

RuntimeError

NotImplementedError

SyntaxError

IndentationError

TabError

SystemError

TypeError

ValueError

UnicodeError

UnicodeDecodeError

UnicodeEncodeError

UnicodeTranslateError

所有异常的根类

由生成器的 close() 方法引发

由键盘中断(通常为Ctrl+C)生成

程序退出/终止

所有非退出异常的基类

引发后可停止迭代

所有内置异常的基类

算术异常的基类

浮点操作失败

对0进行除或取模操作

由assert语句引发

当属性名称无效时引发

发生在Python外部的错误

I/O或文件相关的错误

操作系统错误

到达文件结尾时引发

import语句失败

索引和键错误

超出序列索引的范围

字典键不存在

内存不足

无法找到局部或全局名称

未綁定的局部变量

销毁被引用对象后使用的弱引用

一般运行时错误

没有实现的特性解析错误

语法错误

缩进错误

使用不一致的制表符(由-tt选项生成)

解释器中的非致命系统错误

给操作传递了错误的类型

无效类型

Unicode错误

Unicoed解码错误

Unicode编码错误

Unicode转换错误

自定义异常

可以创建以Exception为父类的新类,作为自定义异常类:

断言与__debug__

assert语句用于在程序中设置断言,格式为:

 使用-O选项使解释器运行于最优模式时,不会执行断言代码。

除非使用-O选项,内置只读变量 __debug__ 的值均为true,可以用于程序调试。

上下文管理协议

该机制主要用于在Python中安全的进行资源(数据库连接、事务、文件句柄等)管理。

使用with语句,可以在一个“上下文管理器”对象的控制下执行一系列的语句:

上下文管理器对象必须实现:

 方法 说明 
__enter__(self) 进行一个新上下文时调用此方法,返回值存入 as 后面指定的变量 
__exit__(self, type, value, tb) 离开一个上下文是调用此方法,如果发生异常,则type、value、tb分别为异常类型、值、跟踪信息
模块

随着程序规模的扩大,有必要根据功能不同把代码分散在不同的文件中,作为单独的模块,并在需要使用时进行导入。

在Python中,模块名就是相应脚本文件的basename。当导入一个模块时,自动创建一个名字空间来存放模块定义的对象,默认此名字空间与模块名相同:

模块的属性
 属性 说明 
__doc__ 模块的文档字符串
__dict__ 与模块相关的字典
__name__ 模块的名称
__file__ 用户加载模块的文件
__path__ 完全限定的包名
基础运算和表达式
运算符优先级(从高到低)
运算符 说明 
(...) 、 [...] 、 {...} 创建元组、列表或字典
s[i]、s[i:j] 索引、切片
s.attr 属性导航符
f(...) 函数调用
+x、-x、~x 一元运算符
x**y 乘方(右结合性)
x*y、x/y、x//y、x%y 乘、除、截断除、取余
x+y、x-y 加、减
x<<y、x>>y 移位
x&y 按位与
x^y 按位异或
x|y 按位或
x < y、x <= y、
x > y、 x >= y、 
x == y、 x != y 
x is y、 x is not y 、
x in s、x not in s

比较、序列成员检查

is    用于对象同一性检查
==  则用于值相等性检查

not x 逻辑非
x and y 逻辑与
x or y 逻辑或
lambda args:expr lambda表达式
数学运算

在其它语言里面比较少见的数学运算符有:截断除法(//)、乘方(**)。

内置函数包括:绝对值(abs)、商与余数(divmod)、四舍五入(round)等。

序列操作

序列包括:字符串、列表和元组。支持以下运算符或者函数:

运算符或函数  说明 
s + r 连接两个相同类型的序列
s * n 生成s的n个副本,浅复制
v1,v2... = s 把序列解包为若干对象,变量个数必须和元素个数一致
s[i] 索引,返回第i+1个元素
s[i:j] 切片
s[i:j:stride] 扩展切片,stride为步进值,可以跳过若干对象,结果索引为i, i+stride, i + 2*stride直到j未知
x in s   以及 x not in s 从属关系判断
for x in s 迭代
all(s) 是否所有元素均为True,不适用于字符串
any(s) 是否存在元素为True,不适用于字符串
len(s) 长度
min(s)  max(s) 最值
sum(s[,initial])  求和
以下为可变序列(即列表)支持的操作
s[i] = x 按索引赋值,如果i为负数,则从结尾算起
s[i:j] = x 按切片赋值,x的个数必须与切片中元素个数一致
s[i:j:stride] = x 扩展切片赋值,x的个数必须与切片中元素个数一致
del s[i] 删除一个元素
del s[i:j] 删除一个切片
del [i:j:stride] 删除一个扩展切片
字典操作
操作  描述 
x = d[k] 通过键进行查找
d[k] = x 通过键进行赋值
del d[k] 通过键进行删除
k in d 测试某个键是否存在 字典中的项数
len(d) 字典的条目个数
集合操作

set和frozenset用于支持常见的集合操作:

 操作 描述 
s | t 并集
s & t 交集
s - t 差集
s ^ t 对称差集
len(s) 集合中条目个数
max(s) 最大值
min(s) 最小值
类型转换
 转换函数 说明 
int(x [, base]) 将x换为一个整数。如果x是一个字符串,base用于指定基数 
float(x) 将x换为一个浮点数
Complex (real [, imag]) 创建一个复数
str(x) 将对象x转换为字符串表示
repr(x) 将对象x转换为一个表达式字符串,可以通过eval还原
format (x [, fmt_spec]) 将对象x转换为格式化字符串,该函数调用x的__format__()方法
eval(str) 对字符串求值并返回对象
tuple(s)   将s转换为元组
list(s) 将s转换为列表
set(s) 将s转换为集合
dict(d) 将d转换为字典,d是(key,value)形式的序列对象
frozenset(s) 将s转换为不可变集合
chr(x) 将整数转换为字符
unichr(x) 将整数转换为Unicode字符
ord (x) 将字符转换为其整数值
hex(x) 将整数转换为十六进制字符串 
bin (x) 将整数转换为二进制字符串
oct (x) 将整数转换为八进制字符串
 函数与函数编程
变量作用域
  1. 每次执行函数时,自动创建局部命名空间,其内包括函数参数、函数体内定义的变量
  2. 解释器解析变量名称时,首先从局部命名空间开始;如果找不到,则搜索函数的全局命名空间(定义该函数的模块);仍然找不到,则搜索内置命名空间;再找不到则NameError
  3. 除非在函数里使用global语句,否则不会改变全局命名空间变量的值,但是可以访问全局变量的值
  4. Python支持嵌套函数定义,使用词法作用域来绑定嵌套函数中的变量——首先检查其局部作用域,然后检查外部嵌套函数的作用域,以此类推。在Python2中,只能对局部作用域、全局作用域进行变量赋值,对外部嵌套函数中定义的变量进行赋值是不支持的,Python3可以使用nonlocal语句解决此问题
  5. 使用尚未赋值的局部变量,导致UnboundLocalError
函数对象与闭包

作为First-class对象的函数,可以当作数据传递给其他函数。把函数作为数据来处理时,它自动携带定义函数的上下文信息(变量)。

将函数主体语句、语句的执行环境上下文信息一起打包时,得到的对象称为闭包。由于任意函数都携带定义其它的模块的全局命名空间信息(__globals__),因此本质上任何Python函数都是闭包。

装饰器

装饰器是一个函数,用于包装一个函数或者类,目的是修改、增强被包装对象的功能。特殊符号@用于表示装饰器语法:

可以声明多个装饰器,每个装饰器必须独占一行,最后定义的装饰器,最先包装到原始函数上:

装饰器可以带有参数:

装饰器也可以应用于类,这样的装饰器应当返回类对象作为结果。 下面是一个例子:

生成器与yield

使用yield关键字可以定义一个生成器对象,该对象本质上是一个函数,其生成一个值序列,在迭代中使用:

当生成器函数遇到return语句或者StopIteration异常,则其退出。

可选的,调用 close() 方法可以关闭生成器,当yield语句遭遇GeneratorExit异常时会自动调用。

协程与yield

yield语句可以作为右值,通过该方式使用yield的函数称为协程:

由于协程使用时next()调用必不可少,因此可以使用类似下面的装饰器自动完成:

协程一般是无限期运行的,可以调用close()方法显式关闭。关闭后,再调用send()会导致StopIteration异常。注意close方法会在协程内部引发GeneratorExit异常。

使用throw()方法可以在协程内部触发异常,该异常在yield语句处出现。使用throw()给协程发送异步信号是不安全的。

如果协程的yield提供值,那么将自动返回给send()方法的返回值:

当通过使用yield返回值、throw()时需要注意,send()给协程的值将作为throw()的返回值返回。

生成器与协程的使用场景
  1. 数据流处理程序,类似于Unix Shell管道
  2. 实现某种形式的并发,例如使用某个任务管理器,把数据分发给数百个执行各种具体任务的协程
列表包含

Python使用“列表推导”运算符,来把函数应用到列表的每个项,并根据结果创建新的列表,例如下面的操作:

可以使用列表推导改写为:

列表推导的语法如下:

注意:Python2的列表推导迭代变量在当前作用域中求值,在列表推导完毕后,其值仍然保留。在Python3中,迭代变量是私有的,推导结束即无效。

生成器表达式

生成器表达式与列表包含非常类似,但是它只是生成获取结果的规则,而不是生成结果本身:

lambda表达式

使用lambda语句可以创建匿名函数:

lambda语句中不能出现多条语句或者非表达式语句(for、while等)。作用域规则与函数相同

递归

sys.getrecursionlimit ()返回当前解释器对递归深度的限制,默认1000。

递归不能用在生成器函数、协程中。

函数属性

可以为函数指定任意属性,这些属性会包含在__dict__属性中。

eval()、exec()、compile()

eval、exec均在调用者的变量作用域中执行,可选的globals、locals用于映射全局、局部名字空间。

对于反复执行的代码,最好使用compile将其编译为字节码,提高性能:

类与面向对象编程
  1. Python没有类作用域一说,如果需要访问对象的其它属性,必须以self.开头
  2. 派生类不会自动调用基类的__init__方法
  3. super(cls, instance).*** 用于在基类上执行属性查找,Python3直接简化为 super().***
  4. Python支持多重继承,示例:
  5. 通常应当避免使用多重继承。有时,可以使用多重继承来定义混入类(mixin)。混入类定义了需要混合到其它类中的一组方法,以添加功能,它通常假定其它方法/属性存在,并以这些方法/属性为基础构建新的逻辑
  6. 动态绑定(多态性):不考虑实例的类型的情况下使用实例,只要以obj.attr的形式访问属性,解释器就会按实例本身内部 - 实例的类定义 - 基类的类定义的顺序来搜索attr,返回第一个匹配。此绑定过程的关键在于它与obj的类型独立,只要它就有attr属性,就可成功绑定(鸭子类型识别)。动态绑定可以用于组件解耦,例如,可以针对具有某个方法集的对象编写代码,而不需要考虑其属于何种类型。
  7. 静态方法是普通的函数,只是定义在类的名字空间中;类方法的第一个参数是当前类本身;实例方法的第一个参数是当前对象本身
属性(property)

property是一种特殊的属性,在访问时,会计算它的值:

向property添加setter、deleter方法,可以实现设置、删除属性操作:

另外,getter、setter、deleter风格的方法也是支持的,但是建议使用装饰器,更加简洁,不会显示大量的getter、setter等方法:

数据封装与私有属性

Python约定以__开头的方法为私有方法,这些方法会自动变形: __func 变形为 __classname__func 的形式。
这不是一种严格的信息隐藏机制。

对象内存管理

Python使用引用计数作为基础垃圾回收算法。每个对象都具有一个引用计数,将对象赋给一个新变量、将其放入容器时,均会导致该计数增加,使用 del 语句或者超过变量作用范围、重新变量赋值时,引用计数则减小

引用计数算法具有无法处理循环引用的天生缺陷,为此Python解释器会定期执行一个“循环检测器”,搜索不可访问的对象循环引用,并删除之。

Python类实例的创建包括两个步骤:

  1. 使用特殊的 __new__() 方法创建实例
  2. 使用 __init__() 方法初始化实例

其中类方法__new__方法很少需要用户定义,除了以下两个场景:

  1. 在继承一个不变类型的基类时,用于修改对象的值
  2. 定义元类时使用

实例创建完毕后,Python将管理其引用计数,当引用计数为0时,实例立即被销毁,其 __del__() 方法被调用。通常没必要定义__del__,因为无法保证解释器在退出时调用了该方法,如果需要资源清理,自定义close()方法并手工调用是最好的。

定义了__del__() 的对象无法被Python的循环垃圾收集器回收。可以使用 weakref.ref() 引用解决此问题——在不增加引用计数的情况下创建对象的引用。

__slots__

通过定义特殊变量 __slots__ ,类可以限制设置合法属性名称:

设置__slots__的类实例不再使用字典存储实例数据,而是改为数组,因此可以减少内存占用、执行时间。

没有必要在__slots__里面添加方法、property的名字,因为他们是定义在类上而不是实例级别的。

类型、类成员测试
抽象基类

使用abc模块可以定义抽象基类,该模块由元类ABCMeta与一组装饰器组成,使用方法如下:

抽象类不能实例化,到导致TypeError,抽象的派生类只要没有全部实现抽象方法、属性,则同样不能实例化。

抽象基类支持对已经存在的类进行注册——使其属于该类(isinstance、issubclass返回期望的结果),但是该注册不会检查目标子类是否实现了抽象基类的任何抽象方法、属性:

使用抽象基类注册机制,可以重新组织已有类型的层次结构,例如numbers模块把数字类型重新整理,而默认他们都是继承object类的

元类

元类知道如何创建、管理类:

 使用class语句定义新类时,解释器内部事件序列可以使用下面的代码示意:

通过指定元类,可以改变类对象创建的行为:

  1. 设置类变量__metaclass__可以显式指定元类
  2. 如果没有显示指定元类,则使用基类元组的第一个条目的元类作为新类的元类
  3. 如果没有指定基类,则使用默认值: types.ClassType ,即Python2.2以后的type作为元类

元类通常在框架组件中使用,通常可以继承 type 并重新实现 __init__() 、 __new__ 等方法,来扩展新的元类,下面的元类要求所有类定义必须提供文档字符串:

模块、包与分发

大规模的Python程序通常以模块、包的形式组织。Python的包常常和目录对应,模块则和文件对应。

模块与import语句

任何Python源文件均可以作为模块来使用,对于一个名字为util.py的源文件,可以使用import util语句来将其作为模块加载。加载模块时,Python解释器将:

  1. 创建新的命名空间,用作在相应源文件中定义的所有对象的容器。源文件中定义的函数、方法在使用global语句时,将访问该命名空间
  2. 在新创建的命名空间中执行模块中包含的代码——import导致模块中所有语句被执行
  3. 在调用函数中创建名称来引用模块命名空间,该名称默认与模块名称一致:

用于引用模块的名称可以使用as限定符修改,新名称只限定使用了import语句的源文件或者上下文:

模块也是Python的First class对象,因此可以分配给变量,存放在列表等数据结构中。

从模块导入指定符号

使用from语句可以将模块中指定的具体符号引入当前命名空间中,访问这些导入的符号时,不需要模块名字空间前缀:

在模块中定义列表 __all__ ,可以精确限制import *能导入的符号。

你也可以使用from从某个包中导入模块、从包中导入(定义在包的__init__.py中的)函数:

相对导入

从以点号开头的路径进行导入,表示相对于当前包寻找模块或符号:

以主程序形式运行

模块可以在import时在自己的名字空间中运行(以库模块方式),也可以以主程序的方式运行。

每个模块均可以访问变量 __name__ ,该变量用于确定当前模块在哪个模块内部运行,解释器的顶级模块的名称为__main__ ,在命令行指定或者直接输入的程序将在__main__模块中运行:

模块搜索路径

 加载模块时,解释器在列表sys.path中搜索字典列表,sys.path的第一项内容是空字符串,表示当前正在使用的字典,可能包括的其他条目有:字典名称、zip归档文件、.egg文件。各条目在sys.path中出现的顺序决定了模块加载时搜索的顺序。

egg文件只是添加了版本号、依赖项的zip文件,使用zip文件的示例如下:

使用归档文件时需要注意:

  1. python不会创建.pyc、.pyo文件,应当提前创建并放在归档文件中,避免加载模块时性能下降
  2. C编写的共享库、扩展模块无法从归档文件中导入
模块加载和编译

可使用import加载的模块包括四类:

  1. Python源代码,即.py文件
  2. 编译为共享库或者DLL的C/C++扩展
  3. 一组包含模块的包
  4. 使用C编写并链接到Python解释器的内置模块

在加载模块foo时,在顺序sys.path下每个目录中搜索以下文件:

  1. 包定义:目录foo
  2. 已编译扩展:foo.pyd、foo.so、foomodule.so、foomodule.dll
  3. foo.pyo:使用-O、-OO选项时
  4. foo.pyc
  5. foo.py、foo.pyw

py文件在首次被import时,会被编译为字节码并写入为.pyc文件,后续导入时,缺省直接使用.pyc文件,除非py的修改日期更新(会自动重新生成pyc)。

如果使用-O,则会创建pyo,并且删除行号、断言、其它调试信息。如果使用-OO,则还会删除文档字符串。

使用包可以把若干模块划分为一组,可以解决不同应用程序中模块名称的命名空间冲突问题。

通过创建与包名字一致的目录,并且在该目录下编写 __init__.py ,即可创建包。包内可以放入其它源文件、编译后的扩展、子包。

包的导入与模块导入类似,都是使用import语句。第一次导入包的任何部分,均会执行对应的__init__.py,父包的__init__.py比子包的先执行。

基于distutils进行分发

本节以工程my-autosizer v0.1.0为例进行阐述

步骤概述

你可以使用distutils模块来分发Python程序给其他人使用,步骤如下:

  1. 相关文件有序的组织到工程目录中,这些文件包括:模块、包、脚本、支持文档等。使用Pycharm时,这个目录就是Project的根目录。工程布局示例:
  2. 在工程目录下编写安装脚本setup.py
  3. 可选的,编写一个安装配置文件
  4. 创建源码分发包(source distribution)
  5. 可选的,创建更多的二进制分发包(binary distributions)
编写setup.py

安装脚本是构建、分发、安装等一系列活动的中心所在,最简单的例子如下:

创建分发包

调用setup.py,传入不同的相应参数,可以创建分发包,这些分发包自动保存到工程目录的dist子目录下:

安装包

在客户机上,可以解压并安装上面的分发包:

调用 install 子命令后:

  1. 模块、包通常安装到Python库的 site-packages 下
  2. 脚本在Unix下通常安装到Python解释器二进制文件所在目录
  3. 脚本在Windows下通常安装到 %PYTHON_HOME%\Scripts 目录

你也可以调用pip命令,直接安装压缩包:

发布到PyPI

Python Package Index (PyPI)存储基于distutils分发的软件包的元数据信息,如果作者愿意,软件包本身也可以存放在上面。

通过 register 和 upload 子命令,你可以把元数据推送到PyPI上去。PyPI允许你提交某个软件包的任意数量的版本,你还可以覆盖既有的版本。

执行下面的命令注册软件包的元数据:

执行下面的命令上传软件包: 

register和upload命令会检查 $HOME/.pypirc 文件,从中得到用户名、密码、仓库URL信息,该文件内容格式如下:

Section头中的文字,是仓库的名称,可以作为 -r 参数使用: 

基于setuptools进行分发

Setuptools对distutils进行了一系列的增强,特别是,它能很好的处理包之间的依赖关系。下面是Setuptools的主要特性:

  1. 可以在构建阶段,利用easy_install自动查找、下载、安装、升级依赖。这些依赖可以通过HTTP、HTTPS、SVN、SourceForge等方式得到
  2. 创建EGG——单文件的、可导入的分发格式
  3. 对访问位于压缩文件中的数据文件提供增强支持
  4. 自动包含源码树中所有包,不必在setup.py中逐个指定
  5. 自动包含相关的文件到源码发布包中,不需要MANIFEST.in
  6. 对于工程中的每一个__main__函数,自动生成包装脚本或者Windows的exe文件
  7. 支持上传EGG或者源码发布包到PyPI
  8. 创建能自动发现扩展的可扩展程序
简单例子

Setuptools保证了和distutils的兼容性,包括安装脚本的文件名、API风格都是一样的。Setuptools对API进行了很多扩展:

执行同样的命令,可以打源码发布包:

源码发布包会以tar.gz格式存放在dist子目录,同时,根目录会出现一个文件夹:myautoresizer.egg-info 

扩展/改变的setup调用关键字
关键字 说明
include_package_data 如果设置为True,依据MANIFEST.in,包中的所有数据文件被包含到构建树中
exclude_package_data 一个字典,Key为包名称,Value为需要排除掉的文件名通配符的列表
package_data 一个字典,Key为包名称,Value为需要包含在构建树中的文件名通配符的列表
如果使用include_package_data,你不需要指定该选项,除非你需要包含setup脚本运行过程中生成的文件
zip_safe 布尔值,提示当前工程是否可以安全的安装并从一个压缩文件中运行,如果不指定此选项,那么bdist_egg子命令必须分析整个工程,来寻找可能的问题
install_requires 字符串或者列表,指定该包所依赖的其它包及其版本
entry_points

一个字典,Key为扩展点组( entry point group)的名称,Value为定义扩展点的字符串或者列表

扩展点用于支持服务/插件的自动发现

extras_require 一个字典,Key为工程额外特性的名称,Value为支持此额外特性需要安装的依赖
python_requires 对Python版本的要求
setup_requires 一个字符串或者列表,为了能让安装脚本运行,所需要的依赖
dependency_links 一组URL由于搜索setup_requires、tests_require的依赖
namespace_packages

用于命名工程的“命名空间包”的字符串列表。这个命名空间包(例如倒写的域名)可能被多个工程使用

EGG运行时系统能够自动合并具有共同命名空间包的子包,只要命名空间包的__init__.py不包含任何代码

使用find_packages()

对于大型工程来说,手工列出packages需要很大的工作量,此时可以使用find_packages()函数:

自动脚本创建

distutils打包、安装脚本的方式比较笨拙:

  1. 脚本的名称不能很好的匹配Windows/Linux的扩展名习惯
  2. 你需要编写单独的脚本,仅仅为了容纳一个__main__函数。my-autosizer的v0.1.0中的ma_*.py脚本就是因此而存在

要让setuptools自动创建脚本,只需要:

让EGG可以直接执行

 添加此配置后,目标EGG增加可执行权限后,即可直接在Unix-like系统上执行。

声明依赖

对于依赖版本的限制,你可以使用以下操作符: < > <= >= == != 

安装第三方库

PyPI网站包含大量的第三方扩展资源。对于没有依赖的简单库,可以使用脚本 python setup.py install 安装。

对于依赖关系复杂的库,最好使用setuptools提供的easy_install脚本,只需要输入 easy_install pkgname 即可安装指定的软件包,会自动从PyPI下载合适的软件、 依赖项。

posted @ 2017-07-26 19:03  天涯海角路  阅读(204)  评论(0)    收藏  举报