Python
Table of Contents
- 1. Python 简介
- 2. 词汇和语法约定
- 3. 类型与对象
- 4. 运算符和表达式
- 5. 程序结构与控制流
- 6. 函数及函数编程
- 6.1. 函数定义(def)
- 6.2. 函数返回值
- 6.3. 参数传递方式(传对象引用)
- 6.4. 参数默认值
- 6.5. 可变长参数(
*
) - 6.6. 关键字参数
- 6.7. Unpacking Argument Lists
- 6.8. Function Annotations(为函数参数和返回值标记类型)
- 6.9. 作用域规则(静态作用域)
- 6.10. 闭包
- 6.11. Decorators(
@
) - 6.12. 生成器与 yield
- 6.13. 协程与 yield 表达式
- 6.14. 列表推导式(List Comprehensions)
- 6.15. 生成器表达式
- 6.16. 声明式编程(Declarative Programming)
- 6.17. lambda 运算符
- 7. 类(class)
- 8. 模块和包
- 9. Python 标准库
- 10. pip 基本使用
- 11. Tips
1. Python 简介
Python was conceived in the late 1980s, and its implementation was started in December 1989 by Guido van Rossum at CWI in the Netherlands as a successor to the ABC language capable of exception handling and interfacing with the Amoeba operating system. Python supports multiple programming paradigms, including object-oriented, imperative and functional programming or procedural styles.
Python 历史版本如表 1 所示。
Version | Release date |
---|---|
Python 1.0 | January 1994 |
Python 1.5 | December 31, 1997 |
Python 1.6 | September 5, 2000 |
Python 2.0 | October 16, 2000 |
Python 2.1 | April 17, 2001 |
Python 2.2 | December 21, 2001 |
Python 2.3 | July 29, 2003 |
Python 2.4 | November 30, 2004 |
Python 2.5 | September 19, 2006 |
Python 2.6 | October 1, 2008 |
Python 2.7 | July 3, 2010 |
Python 3.0 | December 3, 2008 |
Python 3.1 | June 27, 2009 |
Python 3.2 | February 20, 2011 |
Python 3.3 | September 29, 2012 |
Python 3.4 | 2014-03-17 |
Python 3.5 | 2015-09-13 |
Python 3.6 | 2016-12-23 |
参考:
本文主要摘自《Python 参考手册(第 4 版,Python Essential Reference)》
Python Essential Reference, 4th Edition (online pdf version)
https://docs.python.org/2/tutorial/index.html
https://docs.python.org/3/tutorial/index.html
1.1. Hello world 程序
Python 语句可直接在解释器的交互模式中执行,如:
$ python3 Python 3.4.2 (default, Oct 8 2014, 10:45:20) [GCC 4.9.1] on linux Type "help", "copyright", "credits" or "license" for more information. >>> print("hello world") hello world >>> exit()
用 python
命令可执行保存在文件中的 Python 脚本,如:
$ cat hello.py #!/usr/bin/env python print("hello world") $ python hello.py hello world
1.2. 查看帮助
1.2.1. shell 中使用 pydoc 命令
使用 pydoc
可以查看 Python 的帮助文档。如 pydoc if
,又如 pydoc def
。
1.2.2. 交互方式下使用 help 函数
在交互方式下使用 help 函数,可以查看模块,模块中函数等的帮助文档。如:
>>> import sys >>> help(sys.exit) Help on built-in function exit in module sys: exit(...) exit([status]) Exit the interpreter by raising SystemExit(status). If the status is omitted or None, it defaults to zero (i.e., success). If the status is an integer, it will be used as the system exit status. If it is another kind of object, it will be printed and the system exit status will be one (i.e., failure).
2. 词汇和语法约定
2.1. 程序基本结构
Python 程序中的每个语句都以换行符结束。特别长的语句可以使用续行符(\
)来分成几行。要在一行上放置多条语句,可以使用分号(;
)隔开各条语句。
2.1.1. 注释
和 Shell 类似,Python 中用 #
表示注释。
2.1.1.1. 中文注释
如果要在 python2 的脚本文件里面写中文注释,则必须要在第一行或者第二行声明文件编码的注释,否则 python2 会报错。
声明文件编码的形式为:
#!/usr/bin/env python # -*- coding: utf-8 -*-
参考:
PEP 263 -- Defining Python Source Code Encodings: https://www.python.org/dev/peps/pep-0263/
2.1.2. 缩进要一致
缩进的空格(或制表符)数目可以是任意的,但是在整个块中的缩进必须一致:
if a: statement1 statement2 else: statement3 statement4 # 缩进不一致,错误!
尽管允许用制表符指示缩进,但这不是一个好习惯(推荐只使用空格作为缩进字符)。
2.2. 变量
Python 是动态语言(变量名没有类型,值有类型)。同一变量名可以(在程序运行的不同阶段)代表不同类型的值。
Python 中变量在赋值中同时生成,无需声明。
$ python3 -q >>> a = 1; >>> print(a) 1 >>> a = "test" >>> print(a) test
2.2.1. 交互模式中最后一次运算结果
以交互方式使用 Python 时,特殊变量 _
保存着最后一次运算的结果。
>>> 1 + 3 4 >>> _ + 4 8 >>> print(_) 8
2.3. 数字字面量
内置的数字字面量分为 4 种类型:
(1) 布尔值,标识符 True
和 False
被解释为布尔值,其整数值分别是 1 和 0
(2) 整数,其位数是任意的,如 1234
,又如 12345678901234567890
。要使用八进制、十六进制或二进制指定整数,可以在值的前面加上 0
, 0x
, 或 0b
。例如 0644
, 0x100fea8
, 0b11101010
(3) 浮点数,如 123.34
,又如 1.234e+02
(4) 复数,如 1.2 + 12.34j
2.4. 字符串字面量
字符串用单引号('),双引号(")。反斜杠(\)用来转义特殊字符,比如换行符、反斜杠本身、引号以及其他非打印字符。
a = 'Hello world' b = "Hello world" c = 'Hello "world"' # 单引号中可以直接包含双引号 d = "Hello 'world'" # 双引号中可以直接包含单引号 e = 'Hello \'world\'' # 如果要在单引号中包含单引号,则需要转义 f = "Hello \"world\"" # 如果要在双引号中包含双引号,则需要转义 g = 'Hello \nworld' # 这里 \n 是换行符,如果想原封不动输出 \n,则可以使用后面的 raw strings
也可以用三引号( '''
或 """
)来定义 ASCII 字符串。 三引号字符串的好处是:字符串中可以包含不必转义的“换行符”和“引号”。 使用它可以方便地创建多行字符串,如:
s="""line 1 line 2 line 3""" print(s)
相邻两个字符串字面量(两者之间可以包含空格或者换行)会自动进行 concatenation,当然也可以使用 +
进行 concatenation,如:
a = "hello""world" # 相当于 "helloworld" b = "hello" "world" # 可以用空格(或者换行)分隔,相当于 "helloworld" c = 'hello' "world" # 相当于 "helloworld" d = "hello" + "world" # 使用 + 也可以 concatenate 两个字符串
使用 len()
可以使用计算字符串中“字符个数”;如果想计算所占“字节数”,则使用 encode('utf-8')
转换为 bytes 后再使用 len()
计算,如:
a = "hello" print(len(a)) # 输出 5 b = "汉字" print(len(b)) # 输出 2 print(len(b.encode('utf-8'))) # 输出 6
2.4.1. raw strings
字符串前加 r
或者 R
前缀表示 raw strings。在 raw strings 中, \
没有特殊含义,如 r"\n"
表示包含两个字符(即字符 \
和字母 n
)的字符串。raw strings 可以简化正则表达式的书写。
2.5. bytes 字面量
bytes 字面量使用前缀 b
或者 B
表示,如:
a = b'abcd' # 使用双引号也行,即 b"abcd" 也行 print(a.hex()) # 输出 61626364 b = b'ABCD' print(b.hex()) # 输出 41424344 c = b'\0' print(c.hex()) # 输出 00
在 bytes 字面量只能包含 ascii 字符,所以大于等于 128 的表示需要使用 \x
转义,如:
a = b'\xde\xad\xbe\xef' print(a.hex()) # 输出 deadbeef
2.5.1. bytes 和字符串的相互转换
在 bytes 上调用方法 decode 可以转移为字符串,而在字符串上调用方法 encode 可以转移为 bytes,如:
a = b'XYZ' # hex 表达为 58595a # bytes 转换为 hex string b = a.hex() # str 58595a # 从 hex string 构造 bytes c = bytes.fromhex("58595a") # b'XYZ' print("a =", a) # a = b'XYZ' print("b =", b) # b = 58595a print("c =", c) # c = b'XYZ' # bytes 解码为 str d = b'XYZ'.decode("ascii") # str XYZ # str 编码为 bytes e = 'XYZ'.encode("ascii") # e 为 b'XYZ', hex 表达为 58595a print("d =", d) # d = XYZ print("e =", e) # e = b'XYZ'
2.6. 容器
2.6.1. 列表([]
)
列表用 []
表示,一个列表中的元素不要求同类型,可以是任意的 Python 对象。
列表实例:
>>> names = [ "Dave", "Mark", "Ann", "Phil" ] >>> names[0] = "Jeff" # 访问元素(首元素下标为0) >>> names.append("Kate") # append 增加元素 >>> print(names) ['Jeff', 'Mark', 'Ann', 'Phil', 'Kate'] >>> names.insert(2, "Sydney") # insert 在某个位置插入一个元素 >>> print(names) ['Jeff', 'Mark', 'Sydney', 'Ann', 'Phil', 'Kate'] >>> print(names[1:3]) # 切片操作符(:)访问子列表 ['Mark', 'Sydney'] >>> "-".join(names) # 字符串函数 join 可以把列表元素按指定的分隔符连接 'Dave-Mark-Ann-Phil' >>> ":".join(names) 'Dave:Mark:Ann:Phil' >>> a = [1,2,3] + [4,5] # 可用 + 合并两个列表 >>> print(a) [1, 2, 3, 4, 5] >>> b = [1,2,3] >>> b.extend([4,5,6,7]) # extend 可以用另一列表扩展一个列表 >>> print(b) [1, 2, 3, 4, 5, 6, 7] >>> c = [10,20,30,40,50] >>> c[:3] # 取前 3 个元素,如果 list 不够 3 个也不会报错,返回所有 [10, 20, 30] >>> c[-4:] # 取后 4 个元素,如果 list 不够 4 个也不会报错,返回所有 [20, 30, 40, 50]
使用 list(range(x,y))
可以生成从 x
到 y-1
的列表,如:
>>> a = list(range(2, 11)) # 生成 2 到 10 组成的列表 >>> print(a) # [2, 3, 4, 5, 6, 7, 8, 9, 10]
使用 for
循环可以遍历列表,使用内置函数 enumerate
可以遍历列表的同时获得索引。如:
items = [1, 2, 3, 4, 5] for item in items: print(item) items = [1, 2, 3, 4, 5] for index, item in enumerate(items): # 返回两个值,前一个为索引,后一个为列表中的元素 print(index) print(item)
2.6.1.1. 遍历列表时删除元素
遍历列表,当元素满足指定条件时删除该元素。这如何实现呢?
一般,我们采用“从后往前”的遍历方式:
# 从后往前遍历列表 for i in range(len(list1) - 1, -1, -1): # 从后往前遍历(方式1) #for i in reversed(range(0, len(list1))): # 从后往前遍历(方式2),用内置函数reversed实现反转 #for i in range(0, len(list1))[::-1]: # 从后往前遍历(方式3),用slice操作实现反转 if check(list1[i]): del list1[i]
也可以采用“从前往后”的遍历方式,只是代码看起来有点啰嗦:
# 从前往后遍历列表 i = 0 n = len(list1) while i < n: if check(list1[i]): del list1[i] n = n - 1 else: i = i + 1
参考:https://stackoverflow.com/questions/6022764/python-removing-list-element-while-iterating-over-list
2.6.2. 元组(()
)
元组(tuple)类型和列表关系很密切,通过用 ()
中将一系列逗号分割的值括起来可以得到一个元组。元组支持大多数列表的操作,比如索引,切片和连结。
元组是只读的,创建元组后,不可修改其中的元素,也不能添加或删除元素。
>>> a = (1, 4, 5, -9, 10) >>> type(a) <type 'tuple'> >>> print(a[0]) 1 >>> print(a[0:2]) (1, 4)
即使没有圆括号,Python 通常也能识别出元组。如:
>>> address = 'www.python.org', 80 >>> type(address) <type 'tuple'> >>> print(address[0]) www.python.org >>> print(address[1]) 80
2.6.3. 集合(set)
集合用于包含一组无序的对象,集合中的元素不能重复。要创建集合,可使用 set()
函数。如:
>>> s = set([3, 5, 9, 10]) # 创建一个数值集合 >>> t = set("Hello") # 创建一个字符的集合,这个集合中有4个元素:H, e, l, 0 >>> list(s) # 通过 list 可以把 set 转换为列表 [9, 10, 3, 5] >>> list(t) # 通过 list 可以把 set 转换为列表 ['e', 'H', 'l', 'o'] >>> sorted(s) # 如果转换为一个排序的列表,则可以使用 sorted [3, 5, 9, 10] >>> sorted(t) # 如果转换为一个排序的列表,则可以使用 sorted ['H', 'e', 'l', 'o']
集合支持一系列标准操作,包括并集、交集、差集和对称差集。如:
a = t | s # t和s的并集 b = t & s # t和s的交集 c = t - s # 求差集(项在t中,但不是s中) d = t ^ s # 对称差集(项在t或s中,但不会同时出现在二者中)
使用 add()
和 update()
可以在集合中添加新项:
t.add('x') # 添加一项 s.update([10, 37, 42]) # 在s中添加多项
使用 remove()
可以删除一项:
t.remove('H')
2.6.4. 字典({key:value}
)
字典就是一个关联数组(或称为哈希表)。它是一个通过关键字索引的对象的集合。使用 {}
来创建一个字典。
a = { "username" : "beazley", "home" : "/home/beazley", "uid" : 500 } print(a["username"]) # 输出 'beazley' if "username" in a: # 用 in 关键字测试字典中 key 是否存在。 username = a["username"] else: username = "unknown user" del a["username"] # 用 del 删除字典中元素 a["alias"] = "bz" # 往 dict 中增加新对象
使用 keys
方法可以得到所有的键,使用 items
方法可以遍历字典。如:
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'} >>> knights.keys() dict_keys(['gallahad', 'robin']) >>> for k, v in knights.items(): ... print(k, v) ... gallahad the pure robin the brave
3. 类型与对象
Python 程序中保存的所有数据都是围绕“对象”这个概念来构建的。对象包括一些基本的数据类型,如数字、字符串、列表和字典。也可以通过类的形式创建用户定义的对象。
3.1. 对象基本概念
程序中存储的所有数据都是对象。 每个对象都有一个“身份(identity)”,一个“类型(type)或者称为类别(class)”和一个“值(value)”。 例如,执行 a = 42
这行代码时,就会使用值 42 创建一个整数类型对象, 对象身份(identity)可以看作是指向它在内存中所处位置的指针, 这个例子中 a 就是引用这个具体位置的名称。
3.1.1. 对象身份(id()
)和对象类型(type()
)
内置函数 id()
可返回一个对象的身份(identity),返回值为整数。这个整数通常对应于该对象在内存中的位置,不过这是和具体的实现相关。 is
运算符用于比较两个对象的身份。
内置函数 type()
可返回一个对象的类型。由于对象的类型本身也是一个对象(该对象的定义是唯一的,对于某个类型的所有实例都是相同的),因此,类型之间可以使用 is
运算符进行比较。
下面的例子说明了比较两个对象的不同方式:
# Compare two objects def compare(a,b): if a is b: # a and b are the same object statements if a == b: # a and b have the same value statements if type(a) is type(b): # a and b have the same type statements
3.2. 可变对象(mutable)和不可变对象(immutable)
前面说过,每个对象都有一个“身份(identity)”,一个“类型(type)或者称为类别(class)”和一个“值(value)”。
对象被创建之后,它的身份(identity)和类型(或称为类别)就不可以改变。如果对象的值是可以修改的,则称为可变对象(mutable)。如果对象的值不可以修改,则称为不可变对象(immutable)。 Python 中常见 immutable/mutable 对象如表 2 所示。
immutable types | mutable types |
---|---|
int, float, long, complex | byte array |
str | list |
bytes | set |
tuple | dict |
frozen set |
下面是不可变对象(int)的简单测试:
>>> x = 1 >>> type(x) <type 'int'> >>> id(x) 140377823681400 >>> x += 3 >>> id(x) 140377823681328 # 和上一个id(x)的输出不同。由于int是“不可变”的,当执行x += 3时,无法修改x关联的对象,变量x会关联到另一个对象上
下面是可变对象(list)的简单测试:
>>> x = [1, 2, 3] >>> type(x) <type 'list'> >>> id(x) 4317624728 >>> x += [4, 5] >>> id(x) 4317624728 # 和上一个id(x)的输出相同。由于list是“可变”的,当执行x += [4, 5]时,可直接修改x关联的对象
关于可变对象和不可变对象的另一个测试:
def append_to_sequence (myseq): myseq += (9,9,9) return myseq tuple1 = (1,2,3) # tuples are immutable list1 = [1,2,3] # lists are mutable tuple2 = append_to_sequence(tuple1) list2 = append_to_sequence(list1) print(tuple1) # outputs (1, 2, 3) print(tuple2) # outputs (1, 2, 3, 9, 9, 9) print(list1) # outputs [1, 2, 3, 9, 9, 9] print(list2) # outputs [1, 2, 3, 9, 9, 9]
3.2.1. a=a+b (assignment-statements)和 a+=b (augmented assignment-statements)的区别
Python 中 =
是 assignment-statements,可理解为名字绑定,将其右边表达式的结果(值或对象)绑定到其左边的名字上。
而 +=
, -=
, <<=
等称为 augmented assignment-statements,它对左边对象尽可能地进行 in-place 操作。如果左边对象是 mutable 的,则直接原地修改对象的值;如果左边对象是 immutable 的,则无法进行 in-place 操作,这时和 assignment-statements 类似。
>>> x = [1, 2] >>> type(x) <type 'list'> >>> id(x) 4317624584 >>> x = x + [3, 4] >>> id(x) 4317624728 # 和上一个id(x)的输出不同,说明执行x = x + [3, 4]后(名字重新绑定),变量x关联到了不同的对象 >>> x += [5, 6] >>> id(x) 4317624728 # 和上一个id(x)的输出相同,说明执行x += [5, 6]后(原地修改),变量x关联的对象在内存中的位置没有变化
参考:https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements
3.3. 属性和方法
大多数对象都拥有大量特有的数据属性和方法。“属性”就是与对象相关的值。方法就是被调用时将在对象上执行某些操作的函数。使用点运算符(.
)可以访问属性和方法。如:
a = 3 + 4j # 创建一个复数(对象) r = a.real # 获得实部(复数对象的属性之一) b = [1, 2, 3] # 创建一个列表(对象) b.append(7) # 使用列表对象的append方法添加一个新元素
3.3.1. 列出对象的属性和方法(dir()
)
使用 dir()
函数可以列出对象的属性和方法。 。 如:
>>> items = [23, 45] >>> dir(items) # dir() 函数可以列出对象上的所有可用方法 ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
3.4. First-Class Objects
Python 中的所有对象都是“第一类的”。能够命名的所有对象都可以当作数据处理。例如,下面给出了一个包含两个值的字典:
items = { 'number' : 42, 'text' : "Hello World" }
给这个字典添加一些不平常的项,便可以理会到“所有对象是第一类的”这句话的含义,例如:
items["func"] = abs # Add the abs() function import math items["mod"] = math # Add a module items["error"] = ValueError # Add an exception type nums = [1,2,3,4] items["append"] = nums.append # Add a method of another object
在这个例子中,items 字典包含一个函数、一个模块、一个异常和另一个对象的一个方法。我们可以使用 items 的字典查询代替原始名称,代码依然有效,例如:
>>> items["func"](-45) # 执行abs(-45) 45 >>> items["mod"].sqrt(4) # 执行math.sqrt(4) 2.0 >>> try: ... x = int("a lot") ... except items["error"] as e: # 等同于except ValueError as e: ... print("Couldn't convert") ... Couldn't convert >>> items["append"](100) # 执行nums.append(100) >>> nums [1, 2, 3, 4, 100]
3.5. 表示数据的内置类型
Python 中表示数据的内置类型如表 3 所示。
Type Category | Type name | Desciption |
---|---|---|
None | type (None) | The null object None |
Numbers | int | Integer |
long | Arbitrary-precision integer (Python 2 only) | |
float | Floating point | |
complex | Complex number | |
bool | Boolean (True or False) | |
Sequences | str | Character string |
unicode | Unicode character string (Python 2 only) | |
list | List | |
tuple | Tuple | |
xrange | A range of integers created by xrange() (In Python 3, it is called range.) | |
Mapping | dict | Dictionary |
Sets | set | Mutable set |
frozenset | Immutable set |
4. 运算符和表达式
4.1. 算术运算符
算术运算符如表 4 所示。
Operation | Result |
---|---|
x + y | sum of x and y |
x - y | difference of x and y |
x * y | product of x and y |
x / y | quotient of x and y |
x // y | floored quotient of x and y |
x % y | remainder of x / y |
-x | x negated |
+x | x unchanged |
abs(x) | absolute value or magnitude of x |
int(x) | x converted to integer |
float(x) | x converted to floating point |
complex(re, im) | a complex number with real part re, imaginary part im. im defaults to zero. |
c.conjugate() | conjugate of the complex number c |
divmod(x, y) | the pair (x // y, x % y) |
pow(x, y) | x to the power y |
x ** y | x to the power y |
下面例子演示了 /
运算符和 //
运算符的不同:
>>> 5/2 # 返回浮点数 2.5 >>> 5//2 # 相除后,取整数部分 2 >>> type(5/2) <class 'float'> >>> type(5//2) <class 'int'>
当我们想有得到相除后的整数部分时,应该使用 x//y
,而不是 int(x/y)
,它们有区别:
>>> int(999999999999999999999999/3) # 使用 / 运行得到浮点数,再转 int 会有精度损失 333333333333333327740928 >>> 999999999999999999999999//3 # 使用 // 直接得到 int,不会有精度损失 333333333333333333333333
4.2. 布尔表达式和真值
and
, or
和 not
关键字构成了布尔表达式。它们的行为如表 5 所示。
Operator | Description |
---|---|
x or y | 如果 x 为 False,则返回 y;否则返回 x |
x and y | 如果 x 为 False,则返回 x;否则返回 y |
not x | 如果 x 为 False,则返回 True;否则返回 False |
注 1: 使用表达式来判断 True 或者 False 值时,“True,任意非零数字、非空字符串、非空列表、非空元组或非空字典”都将返回 True,而“False,零、空字符串、空列表、空元组或空字典”都将返回 False。
注 2: and
和 or
都具有“短路求值”特性。
4.3. 比较运算符
比较运算符返回布尔值。如表 6 所示。
Operation | Meaning |
---|---|
< | strictly less than |
<= | less than or equal |
> | strictly greater than |
>= | greater than or equal |
== | equal |
!= | not equal |
is | object identity |
is not | negated object identity |
4.4. 条件表达式
常见的编程模式是根据表达式的结果,有条件地进行赋值,例如:
if a <= b: minvalue = a else: minvalue = b
使用“条件表达式”可以简化上面代码,例如:
minvalue = a if a <= b else b # 条件表达式实例
在“条件表达式”中,首先求值的是中间的条件。如果结果为 True,再对 if 语句左面的表达式求值,否则就会对 else 后面的表达式求值。
使用条件表达式时应该格外仔细,因为它们可能导致混淆,特别是将其嵌套或者与其他复杂表达式混在一起的时候。然而,条件表达式在列表推导中特别有用,例如:
values = [1, 100, 45, 23, 73, 37, 69] clamped = [x if x < 50 else 50 for x in values] print(clamped) # 输出 [1, 50, 45, 23, 50, 37, 50]
4.5. 对象的相等性(==)和相同性(is)测试
使用 ==
可以判断两个对象的值是否相同,使用 is
可以判断两个对象是否是同一个对象(内存地址是否相同)。
对于自定义的类,通过实现 __eq__
方法可定制 ==
的形为。如:
class Test(object): def __init__(self, attr1, attr2): self.attr1 = attr1 self.attr2 = attr2 def __str__(self): return str(self.__dict__) def __eq__(self, other): return self.__dict__ == other.__dict__ t1 = Test("foo", 3) t2 = Test("foo", 3) t3 = Test("bar", 3) print(t1) print(t2) print(t3) print(t1 == t2) # True print(t2 == t3) # False
参考:https://docs.python.org/3.5/reference/datamodel.html#basic-customization
4.6. 字符串格式化
4.6.1. %-format(不推荐)
取模运算符 s % d
可以生成格式化的字符串,其中 s
是一个格式字符串,而 d
是一个对象组或映射对象(字典),形为类似于 C 语言的 sprintf 函数。如:
a = 42 b = 13.142783 s1 = "a is %d" % a # s1 为 "a is 42" s2 = "%10d %f" % (a, b) # s2 为 " 42 13.142783"
注:这是古老的字符串格式化方法,不再推荐使用。
4.6.2. format 函数
使用字符串的 s.format(*args, *kwargs)
方法可以格式化字符串。如:
name = 'world' prog = 'Python' str1 = 'Hello {0}, {1}!'.format(name, prog) print(str1) # 输出 Hello world, Python!
除了在 {}
中使用数字外,还可以直接使用名字。如:
str1 ='Hello {name}, {prog}!'.format(name='world', prog='Python') print(str1) # 输出 Hello world, Python!
4.6.3. Template Strings
下面是 Template Strings 的使用实例:
import string str1 = string.Template('Hello $name, This is $prog!') print(str1.substitute(name='world', prog='Python')) # 输出 Hello world, This is Python!
4.6.4. f-strings
在 Python 3.6 中,引入了 f-strings。如:
value = 4 * 20 str1 = f'The value is {value}.' print(str1) # 'The value is 80.'
4.7. 切片操作
切片操作可以应用于有序对象(如 string,tuple 或 list),用于从有序对象中选出指定范围的元素。切片的基本语法为:
[start : stop : stride] # 分别表示“开始下标”(包含),“结束下标”(不包含)和“步进”
说明 1:“步进”和紧根在前面的冒号可以省略,默认步进为 1。
说明 2:“开始下标”省略时,默认为 0;“结束下标”省略时,默认为有序对象的长度。
说明 3:Python 中切片操作得到的是有序对象的复本;而 NumPy 中的切片操作得到的是原始数组的一个视图,也就是说它与原始数组共享同一块数据空间。
下面是一些切片操作的基本实例:
>>> word='Python3' >>> word[1:3] # 从下标1(包含)开始,到下标3(不包含)结束 'yt' >>> word[:3] # 从下标0(包含)开始,到下标3(不包含)结束 'Pyt' >>> word[1:] # 从下标1(包含)开始,到下标7(不包含)结束 'ython3' >>> word[1:5:2] # 从下标1(包含)开始,到下标5(不包含)结束,步进为2 'yh' >>> word[::1] # 相当于复制数组 'Python3' >>> word[::-1] # 相当于复制倒序后的数组 '3nohtyP'
参考:https://docs.python.org/3/reference/expressions.html?#slicings
5. 程序结构与控制流
5.1. 条件语句
条件语句的基本格式如下所示:
if a < b: z=b else: z=a
如果某个子句不需要任何操作,可以使用 pass
语句。如:
if a < b: pass else: z=a
通过使用 or
, and
和 not
关键字你可以建立任意的条件表达式。如:
if b >= a and b <= c: print("b is between a and c") if not (b < a or b > c): print("b is still between a and c")
用 elif
语句可以检验多重条件。如:
if a == '+': op = PLUS elif a == '-': op = MINUS elif a == '*': op = MULTIPLY else: raise RuntimeError, "Unknown operator"
5.2. 循环语句
可以使用 while
或 for
语句实现循环。 break
语句用于立刻中止循环。 continue
语句用于直接进入下一次循环(忽略当前循环的剩余语句)。
while expression: statements
while 语句循环执行块中的语句,直到表达式为假。
for i in s: statements
for 语句反复迭代一个序列中的元素,直到迭代完序列中最后一个元素。
下面是 for 语句的一些例子:
for i in range(0, 5): print(i) # 依次输出 5 个数,从 0 到 4 # 遍历 list 的例子 items = ["a", "b", "c"] for item in items: print(item) for index, item in enumerate(items): # 返回两个值,前一个为索引,后一个为列表中的元素 print(index) # 输出每个索引 print(item) # 输出每个元素 # 遍历 dict 的例子 my_dict = {'color': 'blue', 'fruit': 'apple', 'pet': 'dog'} for key in my_dict: # 直接遍历 dict,会返回 key print(key, '->', my_dict[key]) for key, value in my_dict.items(): # 返回两个值,前一个为 key,后一个为 value print(key, '->', value)
注:如果你想退出一个多重循环,可以使用异常。Python 不提供 goto 语句。
5.3. 异常
使用 try
和 except
语句来捕获并处理异常。
try: f = open("file.txt","r") except OSError as err: print(err)
如果想捕获所有异常,则可以这样:
try: whatever() except: # 捕获所有异常 print("Caught it!")
如果想捕获所有异常的同时还打印出异常,则可以这样:
try: whatever() except BaseException as e: # 捕获所有异常 print(e)
所有异常中包含了 KeyboardInterrupt 异常,如果我们不小心捕获并吞下了这个异常,则可能导致程序不能人为退出了。更常见的是捕获 Exception,如:
不过,一般情况下,我们
try: whatever() except Exception as e: # 捕获绝大部分异常(不包含 SystemExit,KeyboardInterrupt 异常) print(e)
Python 中异常层次如下所示:
BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Exception +-- StopIteration +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError | +-- ZeroDivisionError +-- AssertionError +-- AttributeError +-- BufferError +-- EOFError
参考:https://docs.python.org/3.9/library/exceptions.html#exception-hierarchy
5.3.1. raise
raise
语句用来有意引发异常,你可以使用内建异常来引发异常。如:
raise Exception('spam')
5.4. 上下文管理器与 with 语句
with
语句是从 Python 2.5 开始引入的一种与异常处理相关的语句(Python 2.5 中要通过 from __future__ import with_statement
导入后才可以使用),从 Python 2.6 开始缺省可用。
正确地管理各种系统资源(如文件、锁、连接),在涉及异常时通常是一个棘手的问题:引发的异常可能导致控制流跳过负责释放关键资源的语句。
先看一个使用 with 的例子:
with open("debuglog","a") as f: f.write("Debugging\n") ## statements f.write("Done\n")
这将保证离开 with 后,文件会被关闭。
我们先介绍上下文管理器的概念。 如果对象实现了 __enter__()
和 __exit__()
方法,这个对象就称为上下文管理器(Context Manager)。
with 语句的功能是:在执行语句体之前会调用上下文管理器的 __enter__()
方法,执行完语句体之后会执行 __exit__()
方法。
参考:https://docs.python.org/2/reference/compound_stmts.html#the-with-statement
6. 函数及函数编程
6.1. 函数定义(def)
在 Python 中,使用 def
语句来创建函数。调用函数和 C 语言类似。
函数实例:
def remainder(a,b): q = a/b r = a - q*b return r
Python 中不支持函数重载。
6.2. 函数返回值
当函数中省略 return
语句时,默认返回 None
。如:
>>> def foo(): ... x=1 # 函数foo没有return语句 ... >>> a=foo(); >>> print(a) None >>> type(a) <type 'NoneType'>
使用元组可以让函数返回多个值。如:
def divide(a,b): q = a // b r = a - q*b return (q,r) # 不写小括号,也行 return q, r # 把函数的多个返回值放到单独的变量中: quotient, remainder = divide(1456, 33) # 1456=44*33+4, quotient=44, remainder=4 (quotient, remainder) = divide(1456, 33) # 和上一行作用相同
6.3. 参数传递方式(传对象引用)
Python 不允许程序员选择采用传值还是传引用。Python 参数传递采用的是“传对象引用(call by object reference)”的方式。实际上,这种方式相当于传值和传引用的一种综合。 如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象——相当于通过“传值”来传递对象。
a = [1, 2, 3, 4, 5] def square(items): for i,x in enumerate(items): items[i] = x * x square(a) # a 会被修改 print(a) # a 变为了 [1, 4, 9, 16, 25]
6.4. 参数默认值
如果在函数定义时为参数赋值(这就是参数的默认值),则这个参数是可选的,调用函数可以省略它。
def add(x, y=2): return x+y add(1, 10) # return 11 add(1) # return 3
定义函数后,默认的参数值会传递给以值的方式提供的对象。如:
a = 10 def foo(x=a): return x a = 5 # 给a重新赋值 print(foo()) # 输出10(默认值没有变)
6.4.1. 函数参数默认值仅执行一次(“可变对象”为默认值时应注意)
函数参数默认值仅执行一次。当默认值为“可变对象”时,这可能会带来副作用。如:
def f(a, L=[]): L.append(a) return L print(f(1)) # output [1] print(f(2)) # output [1, 2] print(f(3)) # output [1, 2, 3]
如果你希望在不同的函数调用中不要共享函数参数默认值,你可以这样写:
def f(a, L=None): if L is None: L = [] L.append(a) return L print(f(1)) # output [1] print(f(2)) # output [2] print(f(3)) # output [3]
6.5. 可变长参数(*
)
如果给最后一个参数名加上星号(*
),函数就可以接受任意数量的参数。如:
def mySum(*args): sum = 0 for i in args: # args is 'tuple' sum = sum + i return sum print(mySum(1, 2, 3, 4)) # output 10
在 Python3 中,可变长参数后,还可以出现带有默认值的参数。如:
>>> def concat(*args, sep="/"): # Python 2中不合法,Python 3中合法 ... return sep.join(args) ... >>> concat("earth", "mars", "venus") 'earth/mars/venus' >>> concat("earth", "mars", "venus", sep=".") 'earth.mars.venus'
6.6. 关键字参数
调用函数时,可以显式地命名每个参数并为其指定一个值,这称为关键字参数。使用关键字参数时,参数的顺序无关紧要。位置参数和关键字参数可以同时使用,不过要求位置参数先出现。如:
def foo(w, x, y, z): statements # 关键字参数调用 foo(w='hello', x=3, y=22, z=[1,2]) foo(w='hello', z=[1,2], x=3, y=22) # 和上行相同。关键字参数的顺序无关紧要 foo('hello', 3, y=22, z=[1,2]) # 和上面两行相同,位置参数和关键字参数可以同时使用(前提是位置参数先出现) foo('hello', 3, z=[1,2], y=22) # 和上面三行相同
6.6.1. 用字典保存额外关键字参数(**
)
如果函数定义的最后一个参数以两个星号(**
)开头,可以把所有额外的关键字参数都放入一个字典中并把这个字典传递给函数。如果要编写的函数接受大量可扩展的配置选项作为参数,则使用 **
开头的参数就非常方便。如:
def make_table(data, **parms): fgcolor = parms.pop("fgcolor") # parms is 'dict' bgcolor = parms.pop("bgcolor") border = parms.pop("border", 1) width = parms.pop("width", 100) if parms: raise TypeError("Unsupported configuration options %s" % list(parms)) make_table(items, fgcolor="black", bgcolor="white") make_table(items, fgcolor="black", bgcolor="white", border=1, width=400)
**
和可变长参数列表(*
)可以一起使用,不过要求 **
参数出现在最后。如:
# Accept variable number of positional or keyword arguments def spam(*args, **kwargs): # args is a tuple of positional args # kwargs is dictionary of keyword args ...
这其他了函数编写包装器和代理时经常使用 *args
和 **kwargs
(名字 args 和 kwargs 换为其它名字也行)。例如下面的 callfunc()函数接受参数的任意组合,并把它们传递给 func()函数。
def callfunc(*args, **kwargs): func(*args, **kwargs)
6.7. Unpacking Argument Lists
使用 *
和 **
可以进行 Unpacking Argument Lists,它使得为函数提供函数更加方便。下面通过两个例子来介绍什么是 Unpacking Argument Lists。
def fun(a, b, c, d): print(a) print(b) print(c) print(d) my_list=['value1', 'value2', 'value3', 'value4'] #fun(my_list) # 会报错 TypeError: fun() takes exactly 4 arguments (1 given) fun('value1', 'value2', 'value3', 'value4') fun(*my_list) # Unpacking Argument Lists,和上一行作用相同
再看一个 Unpacking Argument Lists 的例子。
def fun(a, b, c, d): print(a) print(b) print(c) print(d) my_dict={'a':'value1', 'b':'value2', 'c':'value3', 'd':'value4'} #fun(my_dict) # 会报错 TypeError: fun() takes exactly 4 arguments (1 given) fun(a='value1', b='value2', c='value3', d='value4') fun(**my_dict) # Unpacking Argument Lists,和上一行作用相同
参考:https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists
6.8. Function Annotations(为函数参数和返回值标记类型)
可以为用户自定义函数的参数和返回值增加类型说明,如:
>>> def f(ham: str, eggs: str = 'eggs') -> str: ... print("Annotations:", f.__annotations__) ... print("Arguments:", ham, eggs) ... return ham + ' and ' + eggs ... >>> f('spam') Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>} Arguments: spam eggs 'spam and eggs'
6.8.1. 使用 mypy 进行静态类型检测
为函数参数和返回值增加的类型说明后,使用工具 mypy 可以对程序进行静态类型检测。mypy 的安装和基本使用如下:
$ pip3 install mypy $ mypy program1.py
下面是一个有问题的代码:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- def f(ham: str, eggs: str = 'eggs') -> str: print("Annotations:", f.__annotations__) print("Arguments:", ham, eggs) return ham + ' and ' + eggs f(100) # 这是错误的!我希望在运行前就发现它
上面代码中 f(100)
是错误的,参数类型为 str,但传参却为 int,使用工具 mypy 可以在运行之前就发现该问题:
$ mypy program1.py program1.py:9: error: Argument 1 to "f" has incompatible type "int"; expected "str" Found 1 error in 1 file (checked 1 source file)
6.9. 作用域规则(静态作用域)
每次执行一个函数时,就会创建新的局部命名空间。该命名空间代表一个局部环境,其中包含函数参数的名称和在函数体内赋值的变量名称。 Python 采用 Lexical scoping(“词法作用域”,也可称为“静态作用域”),即解析这些名称时,解释器将首先搜索局部作用域,然后由内而外一层层检查外部嵌套函数定义的作用域。如果找不到匹配,最后将搜索全局命名空间和内置命名空间。
def fun(): a = 1 def display(): print(a) return display a = 2 f = fun() f() # 由于Python采用“静态作用域”,会去函数定义环境(而不是调用链)中寻找变量,所以这里会输出1(而不是2)。
注:除词法作用域外,还存在“动态作用域”,采用“动态作用域”的语言在解析名称时,会一层层往往外查找函数的调用链(而不是函数的定义环境),这会使得程序的调试变得更加复杂,且容易出错。
6.9.1. 给全局作用域变量重新赋值(global)
要想在一个函数内部改变一个全局变量的值,需要在函数内部使用 global
声明变量:
a = 1.1 b = 2.2 def foo(): global a a = 11.11 # modify global variable a b = 22.22 return foo() print(a) # output 11.11 print(b) # output 2.2 (b在函数foo中没有声明为global)
6.9.2. 给外部函数中的变量重新赋值(nonlocal)
Python2 只支持在最里层的作用域(即局部变量)和全局命名空间(使用 global)中给变量重新赋值。Python2 中,内部函数不能给定义在外层函数中的局部变量重新赋值。
def outer(): x = 1 def inner(): x = 2 print(x) # 输出2 inner() print(x) # 输出1,外层的x没有被inner函数修改 outer()
在 Python2 中,如果想要修改外层函数中的局部变量,可以把要修改的值放在列表或字典中。参考:http://stackoverflow.com/questions/8447947/is-it-possible-to-modify-variable-in-python-that-is-in-outer-but-not-global-sc
在 Python3 中,引入了 nonlocal 语句,可以给外部函数中的变量重新赋值。如:
def outer(): x = 1 def inner(): nonlocal x # Python 3中增加了nonlocal x = 2 print(x) # 输出2 inner() print(x) # 输出2,外层的x被inner函数修改了 outer()
6.10. 闭包
如果函数(要满足后面条件只能是嵌套函数)访问的变量中存在变量满足条件:即不是全局变量又不是局部变量,这时函数和变量一起构成“闭包”。
>>> def make_printer(msg): ... def printer(): ... print(msg) # msg不是全局变量,也不是函数printer的局部变量 ... return printer ... >>> printer1 = make_printer('Foo!') # printer1是闭包 >>> printer1.__closure__ (<cell at 0x10ad83638: str object at 0x10ad82c60>,) >>> printer1() Foo!
下面的 printer2 不是闭包。
>>> def make_printer(msg): ... def printer(msg=msg): ... print(msg) # msg是printer的参数,它存在于printer的局部作用域中 ... return printer ... >>> printer2 = make_printer('Foo!') # printer2不是闭包 >>> printer2.__closure__ # 没有输出 >>> printer2() Foo!
参考:http://stackoverflow.com/questions/4020419/why-arent-python-nested-functions-called-closures
6.10.1. 闭包实例
如里需要在一系列函数调用中保持某个状态,使用闭包是一种非常高效的方式。如:
def countdown1(n): def next(): nonlocal n # Need Python 3 r = n n -= 1 return r return next # Example use next = countdown1(10) while True: v = next() print(v) if not v: break
上面代码将输出:
$ python3 test.py 10 9 8 7 6 5 4 3 2 1 0
不使用闭包,用类和对象也可以实现类似的功能。如:
class Countdown(object): def __init__(self,n): self.n = n def next(self): r = self.n self.n -= 1 return r # Example use c = Countdown(10) while True: v = c.next() print(v) if not v: break
有测试表明,上面例子中,增大起始值时(如把 10 换为一个非常大的数), 使用闭包的版本比使用对象的版本其运行速度要快很多。
6.11. Decorators(@
)
装饰器(Decorators)是一个函数,其主要用途是包装另一个函数或类。 这种包装的首要目的是透明地修改或增强被包装对象的行为。表示装饰器的语法是特殊符号 @
。装饰器的语义为把函数替换为经过装饰器处理后的函数,看起来像:
@trace def square(x): return x*x ######## 上面的语义相当于 ######## def square(x): return x*x square = @trace(square)
装饰器本身是函数,下面是它的实例:
enable_tracing = True if enable_tracing: debug_log = open("debug.log","w") def trace(func): # 装饰器的实现 if enable_tracing: def callf(*args, **kwargs): debug_log.write("Calling %s: %s, %s\n" % (func.__name__, args, kwargs)) r = func(*args, **kwargs) debug_log.write("%s returned %s\n" % (func.__name__, r)) return r return callf else: return func @trace def square(x): return x*x square(3)
运行上面程序,生成的 debug.log 内容如下:
$ cat debug.log Calling square: (3,), {} square returned 9
6.12. 生成器与 yield
函数使用 yield
关键字可以定义“生成器”对象。生成器是一个函数,它生成一个值的序列,以便在迭代中使用。
下面是生成器的例子:
def countdown(n): print("Counting down from %d" % n) while n > 0: yield n n -= 1 return
和普通函数不同,调用上面生成器时,其中的代码不会开始执行,它会返回一个生成器对象。只有在生成器对象上第一次调用 next()
(在 Python3 中,请使用 __next__()
)时才开始执行生成器中代码。
>>> c=countdown(10) # 调用 countdown(10) 时,并不会开始执行生成器代码 >>> c.next() # 在Python 3中,请使用c.__next__() Counting down from 10 10 >>> c.next() 9 >>> c.next() 8
在生成器对象上调用 next()
时,生成器函数将不断执行语句,直到遇到 yield
语句为止。再次调用 next()
时会继续执行直到又遇到 yield
语句。
通常,不会在生成器对象上直接调用 next()
方法,而是在 for
语句, sum()
函数或者一些使用序列的其它操作中使用它。例如:
for n in countdown(10): print(n)
又如:
a = sum(countdown(10)) print(a) # output 55
6.12.1. 生成器实例
生成器是基于处理管道、流或数据流编写程序的一种极其强大的方式。
下面用生成器实现了类似`tail -f 1.log`的功能:
# python实现 `tail -f 1.log` import time def tail(f): f.seek(0,2) # Move to EOF while True: line = f.readline() # Try reading a new line of text if not line: # If nothing, sleep 0.1 second and try again time.sleep(0.1) continue yield line logs = tail(open("1.log")) import sys for line in logs: sys.stdout.write(line)
下面是另一个生成器,用于在很多行中查找特定的子字符串:
def grep(lines, searchtext): for line in lines: if searchtext in line: yield line
我们可以把前面的两个生成器(tail 和 grep)合并在一起,创建一个简单的处理管道,实现类似`tail -f 1.log | grep python`功能:
# python实现 `tail -f 1.log | grep python` logs = tail(open("1.log")) pylines = grep(logs, "python") import sys for line in pylines: sys.stdout.write(line)
6.13. 协程与 yield 表达式
在函数内,yield 语句可以作为表达式出现在赋值运算符右边, 例如:
def receiver(): print "Ready to receive" while True: n = (yield) print("Got %s" % n)
以这种方式使用 yield 语句的函数称为“协程” ,它的执行是为了响应发送给它的值。它的行为也十分类似于生成器,例如:
>>> r = receiver() >>> r.next() # 向前执行到第一条yield语句(在Python3中是r.__next__()) Ready to receive >>> r.send(1) Got 1 >>> r.send(2) Got 2 >>> r.send("Hello") Got Hello
在这个例子中, 对 next()的初始调用是必不可少的,这样协程才能执行可通向第一个 yield 表达式的语句。在这里协程会挂起,等待相关生成器对象 r 的 send()
方法给它发送一个值。传递给 send()
的值由协程中的 (yield)
表达式返回。接收到值后,协程就会执行语句,直至遇到下一条 yield 语句。
下面是 yield 表达式的另一个例子:
def grep(pattern): print("Searching for %s" % pattern) while True: line = (yield) if pattern in line: print(line)
测试如下:
>>> search = grep('coroutine') >>> search.next() Searching for coroutine >>> search.send('Test') >>> search.send('I love you') >>> search.send('I love coroutine') I love coroutine
参考:https://docs.python.org/2/reference/expressions.html#yield-expressions
6.13.1. 用装饰器保证对 next()的初始调用
在协程中需要首先调用 next()
这件事情很容易被忽略,经常成为错误出现的根源。因此,建议使用一个能自动完成该步骤的装饰器来包装协程。
def coroutine(func): def start(*args, **kwargs) g = func(*args, **kwargs) g.next() return g return start
使用这个装饰器就可以像下面这样编程和使用协程:
@coroutine def receiver(): print("Ready to receive") while True: n = (yield) print("Got %s" % n) # 实例 r = receiver() r.send("Hello world") # 注意:无需初始调用.next()方法
6.13.2. (yield value)
如果 yield 表达式中提供了值,协程可以使用 yield 语句同时接收和发出返回值,例如:
def line_splitter(delimiter=None): print("Ready to split") result = None while True: line = (yield result) result = line.split(delimiter)
在这个例子中,我们使用协程的方式与前面相同。但是,现在调用 send()方法也会生成一个结果,例如:
>>> s = line_splitter(",") >>> s.next() Ready to split >>> s.send("A,B,C") ['A', 'B', 'C'] >>> s.send("100,200,300") ['100', '200', '300']
send()方法的返回值是传递给下一条 yield 语句的值。
6.14. 列表推导式(List Comprehensions)
函数的常用操作是将函数应用给一个列表的所有项,并使用结果创建一个新列表,例如:
nums = [1, 2, 3, 4, 5] squares = [] for n in nums: squares.append(n * n)
这种操作很常见,因此出现了叫做“列表推导(List Comprehensions)”的运算符,例如:
nums = [1, 2, 3, 4, 5] squares = [n * n for n in nums] # 比上面代码块更加简洁
列表推导式用来基于一个列表创建另一个列表 ,它的一般语法为:
[expression for item1 in iterable1 if condition1 for item2 in iterable2 if condition2 ... for itemN in iterableN if conditionN ]
这种语法大致上等价于以下代码:
result = [] for item1 in iterable1: if condition1: for item2 in iterable2: if condition2: ... for itemN in iterableN: if conditionN: result.append(expression)
下面是列表推导式的一些例子:
>>> vec = [-2, -1, 0, 1, 2] >>> [x*2 for x in vec] # create a new list with the values doubled [-4, -2, 0, 2, 4] >>> [x for x in vec if x >= 0] # filter the list to exclude negative numbers [0, 1, 2] >>> [abs(x) for x in vec] # apply a function to all the elements [2, 1, 0, 1, 2] >>> [(x,y) for x in [-3,5,-10,7] for y in 'abc' if x > 0] [(5, 'a'), (5, 'b'), (5, 'c'), (7, 'a'), (7, 'b'), (7, 'c')]
注:List Comprehensions 并不是 Python 所独有,其它语言也有类似结构。参考:https://en.wikipedia.org/wiki/List_comprehension
6.14.1. Set and Dicionary Comprehensions
和列表推导式类似,还有“集合推导式”和“字典推导式”。如:
>>> {x*x for x in {1,2}} # 集合推导式 set([1, 4]) >>> {x: x*2 for x in (1, 2, 3)} # 字典推导式 {1: 2, 2: 4, 3: 6}
6.15. 生成器表达式
生成器表达式是一个对象,它执行的计算与列表推导式相同,但会迭代地生成结果。 生成器表达式的语法也与列表推导式相同,但要用圆括号代替方括号。 生成器表达式的语法为:
(expression for item1 in iterable1 if condition1 for item2 in iterable2 if condition2 ... for itemN in iterableN if conditionN)
和列表推导式不同, 生成器表达式不创建列表或者不立即对圆括号内的表达式求值。生成器表达式会创建一个通过迭代并按照需要生成值的生成器对象。 例如:
>>> a = [1, 2, 3, 4] >>> b = (10*i for i in a) >>> b <generator object <genexpr> at 0x103377c30> >>> b.next() 10 >>> b.next() 20 >>> b.next() 30
生成器表达式和列表推导式的差异在于: 列表推导式会一次创建好包含结果数据的列表;而生成器表达式只是创建生成器。 在某些应用中,生成器表达式会更加高效且内存占用更少。例如:
# Read a file f = open("data.txt") # Open a file lines = (t.strip() for t in f) # Generator expression. Read lines, strip trailing/leading whitespace comments = (t for t in lines if t[0] == '#') # Generator expression. All comments for c in comments: print(c)
需要说明的是,在上面的 lines 和 comments 都是生成器表达式。 在上面的处理过程中绝对没有把整个文件加载到内存中。 因此,这里一种从很大的 Python 源文件中提取注释的高效方法。
生成器表达式不会创建序列形式的对象。不能对它进行索引,也不能进行任何常规的列表操作,例如 append()。但是,使用内置的 list()
函数可以将生成器表达式转换为列表。如:
clist = list(comments) # comments is generator expression
6.16. 声明式编程(Declarative Programming)
什么是声明式编程(Declarative Programming)呢?声明式编程是和命令式编程(Imperative Programming)相对的概念。C++、Java 等都属命令式编程风格的语言;而 SQL 语言就是声明式编程风格的语言。
声明式编程风格是特点是: 只需要声明你想要得到的结果,而不用显式指定控制流程。 比如下面 SQL 语句:
SELECT * from dogs INNER JOIN owners WHERE dogs.owner_id = owners.id
也可以用命令式编程方式实现上面 SQL 语句的逻辑:
// javasript code //dogs = [{name: 'Fido', owner_id: 1}, {...}, ... ] //owners = [{id: 1, name: 'Bob'}, {...}, ...] var dogsWithOwners = [] var dog, owner for(var di=0; di < dogs.length; di++) { dog = dogs[di] for(var oi=0; oi < owners.length; oi++) { owner = owners[oi] if (owner && dog.owner_id == owner.id) { dogsWithOwners.push({ dog: dog, owner: owner }) } }} }
声明式编程的好处在于: 代码看起来更加简洁;且它在更高的层次进行了抽象(你只用声明想得到什么,而不用关心怎么得到),这样底层编译器可以帮我们进行优化。
6.16.1. Python 声明式编程实例
列表推导式和生成器表达与声明式编程的操作有很强的联系。
假设文件 portfolio.txt 的内容如下:
AA 100 32.20 IBM 50 91.10 CAT 150 83.44 MSFT 200 51.23 GE 95 40.37 MSFT 50 65.10 IBM 100 70.44
我们想要把第二列和第三列相乘后再求和。下面解法是声明式编程:
lines = open("portfolio.txt") fields = (line.split() for line in lines) total = sum(float(f[1]) * float(f[2]) for f in fields) print(total)
对应的命令式编程风格如下:
total = 0 for line in open("portfolio.txt"): fields = line.split() total += float(fields[1]) * float(fields[2]) print(total)
6.17. lambda 运算符
使用 lambda
语句可以创建表达式形式的匿名函数。语法为:
lambda args: expression
其中 args 是以逗号为分隔的参数列表,而 expression 是用到这些参数的表达式。如:
a = lambda x,y : x+y r = a(2, 3) # r = 5
使用 lambda 语句定义的代码必须是合法的表达式,不能出现 for, while 等结构。
7. 类(class)
Python 程序中使用的所有值都是对象。对象由内部数据和各种方法组成。
Python 中,使用 class
语句可定义新的对象类型。
下面是类的定义和实例化的简单例子:
>>> class Complex: ... def __init__(self, realpart, imagpart): ... self.r = realpart ... self.i = imagpart ... >>> x = Complex(3.0, -4.5) >>> x.r 3.0 >>> x.i -4.5
7.1. 继承
Python 中,类继承的语法如下所示:
class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N>
如下面是栈的简单实现(它继承自 object,object 是所有 Python 类型的根类型):
class Stack(object): # 继承object def __init__(self): # __init__ 是个特殊的方法,用来初始化一个对象实例 self.stack = [] def push(self,object): self.stack.append(object) def pop(self): return self.stack.pop() def length(self): return len(self.stack)
由于 Stack 和内置的 list 几乎完全相同,因此,我们还可以通过继承 list 然后添加额外的方法也创建 Stack。如:
class Stack(list): # 继承list。仅需要添加push方法,list本身带有pop方法 def push(self, object): self.append(object)
可以像下面这样使用上面定义的 Stack 类:
s = Stack() s.push("Dave") s.push(43) x = s.pop() # x gets 43 print(x) # output 43 del s
7.2. Abstract Base Classes
使用库 abc 可以实现抽象基类,继承它的类必须实现抽象基类中定义的抽象方法,如:
from abc import ABCMeta, abstractmethod class Bird(metaclass=ABCMeta): # metaclass=ABCMeta 表明它是一个抽象基类 """ Bird 是抽象基类,不能直接实例化,继承它的类必须实现下面的抽象方法 fly """ @abstractmethod def fly(self): pass class Parrot(Bird): # 方式一:直接继承 Bird """ Parrot 继承于 Bird,必须实现抽象方法 fly """ def fly(self): print("Parrot Flying") @Bird.register # 方式二:通过装饰器声明 Magpie 是 Bird 的子类 class Magpie: """ Magpie 继承于 Bird,必须实现抽象方法 fly """ def fly(self): print("Magpie Flying") class Crow: def fly(self): print("Crow Flying") Bird.register(Crow) # 方式三:显式地调用 Bird.register 来声明 Crow 是 Bird 的子类 bird = Parrot() bird.fly() print(isinstance(bird, Bird)) # True print(issubclass(Parrot, Bird)) # True bird = Magpie() bird.fly() print(isinstance(bird, Bird)) # True print(issubclass(Magpie, Bird)) # True bird = Crow() bird.fly() print(isinstance(bird, Bird)) # True print(issubclass(Crow, Bird)) # True
7.3. 私有成员
Python 中不使用 private 等关键字来表示类中的私有方法或私有实例变量。Python 采用命名约定来表示私有方法或私有变量,如果方法名或实例变量名以单个下划线 _
开始,则认为它是私有的。如果方法名或实例变量名以两个下划线 __
开始,则它会被解释为另一个名称来防止和子类的方法或变量冲突,具体来说就是 __bar
会变为 _ClassName__bar
。
表 7 显示了单下划线和双下划线的区别。
访问方式 | 说明 | |
---|---|---|
_foo |
obj._foo |
单下划线开始,则认为它是私有的,只是一种约定,在外部是可以访问的 |
__bar |
obj._ClassName__bar |
双下划线开始也是一种关于私有的约定,且名称前会自动加上 _ClassName 来避免可能出来在子类中的名称冲突 |
下面以私有实例变量为例子介绍一下单个下划线和两个下划线的区别:
>>> class MyClass(): ... def __init__(self): ... self._foo = "This is _foo" # 实例变量以单下划线开始 ... self.__bar = "This is __bar" # 实例变量以双下划线开始 ... >>> obj = MyClass() >>> print(obj._foo) This is _foo >>> print(obj._MyClass__bar) This is __bar >>> print(obj.__bar) # 不能直接访问 __bar,要通过 obj._MyClass__bar 访问 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'MyClass' object has no attribute '__bar'
参考:https://peps.python.org/pep-0008/#method-names-and-instance-variables
8. 模块和包
Python 允许你把函数定义或公共部分放入一个文件,然后在其他程中使用 import
将该文件作为一个模块导入。保存公共代码的文件名就是模块名。
# file : div.py def divide(a,b): q = a/b # If a and b are integers, q is an integer r = a - q*b return (q,r)
要在其它的程序中使用上面模块,可以这样:
import div a, b = div.divide(2305, 29)
import
语句创建一个新的名字空间,该空间包含模块中所有定义对象的名称。要访问这个名字空间,把模块名作为一个前缀来使用这个模块内的对象,就像上边例子中那样 div.divide()
。
8.1. 给模块换个名字(as
)
导入时给模块换个名字,可以使用关键字 as
。
import div as foo a,b = foo.divide(2305,29)
8.2. 导入模块中指定对象到当前名称空间(from
)
可以用 from
导入指定对象到当前名称空间,这样使用模块对象时就可以省略模块前缀了。
from div import divide a,b = divide(2305,29) # No longer need the div prefix
可以用星号(*
)把一个模块中除下划线开头的所有内容导入到当前的名称空间中:
from div import *
不过,如果一个模块如果定义有列表 __all__
,则 from module import *
语句只能导入 __all__
列表中存在的对象。
# module: foo.py __all__ = [ 'bar', 'spam' ] # 定义使用 `*` 可以导入的对象
8.3. 查看模块中的可用函数和变量(dir
)
用 dir
可查看模块中的可用函数和变量。如:
$ python3 -q >>> import string >>> dir(string) ['ChainMap', 'Formatter', 'Template', '_TemplateMetaclass', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_re', '_string', 'ascii_letters', 'ascii_lowercase', 'ascii_uppercase', 'capwords', 'digits', 'hexdigits', 'octdigits', 'printable', 'punctuation', 'whitespace']
8.4. 模块的__name__属性
每个模块都拥有 __name__
属性,它是一个内容为模块名字的字符串。 最顶层的模块名称是__main__。命令行或是交互模式下程序都运行在__main__模块内部。 利用__name__属性,我们可以让同一个程序在不同的场合(单独执行或被导入)具有不同的行为,像下面这样做:
# 检查是单独执行还是被导入 if __name__ == '__main__': # Yes statements else: # No (可能被作为模块导入) statements
8.5. 包
一个 Python 源文件就是一个 Python 模块。 如果 Python 源文件所在的目录中存在一个名为 __init__.py
的文件,则这个文件夹就称为包(Package),目录名就是包名。 文件 __init__.py
可以为空文件。
假设有下面目录结构:
sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ...
当执行 import sound
(这是导入包)时会加载文件 sound/__init__.py
。
当执行 import sound.effects
(这是导入子包)时会加载文件 sound/__init__.py
和 sound/effects/__init__.py
。
当执行 import sound.effects.echo
(这是导入模块)时会加载文件 sound/__init__.py
和 sound/effects/__init__.py
和 sound/effects/echo.py
。
9. Python 标准库
9.1. Built-in 函数
表 8 是 Python 3 的内置函数。
Built-in Functions | ||||
---|---|---|---|---|
abs() | dict() | help() | min() | setattr() |
all() | dir() | hex() | next() | slice() |
any() | divmod() | id() | object() | sorted() |
ascii() | enumerate() | input() | oct() | staticmethod() |
bin() | eval() | int() | open() | str() |
bool() | exec() | isinstance() | ord() | sum() |
bytearray() | filter() | issubclass() | pow() | super() |
bytes() | float() | iter() | print() | tuple() |
callable() | format() | len() | property() | type() |
chr() | frozenset() | list() | range() | vars() |
classmethod() | getattr() | locals() | repr() | zip() |
compile() | globals() | map() | reversed() | __import__() |
complex() | hasattr() | max() | round() | |
delattr() | hash() | memoryview() | set() |
9.2. 文本处理
9.2.1. 输入与输出
下面的程序打开一个文件,然后一行行地读出并显示文件内容(相当于 cat 命令):
import sys f = open("foo.txt") line = f.readline() while line: sys.stdout.write(line) line = f.readline() f.close()
9.3. argparser 模块(处理命令行参数)
optparse 模块已经过时,推荐使用 argparser 模块(它是在 Python 2.6 和 Python 3.2 中引入的)。
9.4. logging 模块
实例:
#!/usr/bin/env python # -*- coding: utf-8 -*- import logging logging.basicConfig( format = "[%(filename)s:%(lineno)s:%(funcName)15s %(levelname)8s] %(message)s", level = logging.INFO ) logger = logging.getLogger(__name__) def fun1(): # some things logger.critical("This is critical msg") logger.error("This is error msg") # some things fun1() logger.warning("This is warning msg") logger.info("This is info msg") logger.debug("This is debug msg")
9.5. struct 模块(处理二进制数据结构)
struct 用于在 Python 与二进制数据结构之间转换数据。这些数据结构通常在与使用 C 编写的函数,二进制文件格式、网络协议进行交互时使用。
下面是用于打包和解包的两个基本函数:
struct.pack(fmt, v1, v2, ...) # 根据fmt中的格式字符串,将值v1,v2等打包后返回 struct.unpack(fmt, buffer) # 把buffer中的二进制数据按fmt指定的格式进行解包后返回
9.5.1. 格式字符串
进行打包解包操作时,需要指定二进制数据的格式,它们可取表 9 中的字符。
Format | C Type | Python type | Standard size |
---|---|---|---|
x | pad byte | no value | |
c | char | bytes of length 1 | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
q | long long | integer | 8 |
Q | unsigned long long | integer | 8 |
n | ssize_t | integer | |
N | size_t | integer | |
e | float | 2 | |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | bytes | |
p | char[] | bytes | |
P | void * | integer |
下面是把打包的例子:
>>> import struct >>> struct.pack('b', 65) 'A' >>> struct.pack('h', 65) 'A\x00' >>> struct.pack('i', 65) 'A\x00\x00\x00' >>> struct.pack('ii', 65, 66) 'A\x00\x00\x00B\x00\x00\x00' >>> struct.pack('4s2s', b'abcd', b'ef') # 对于s,需要在前面指定长度 'abcdef'
下面是解包二进制数据的例子:
>>> import struct >>> struct.unpack('i', b'A\x00\x00\x00') (65,) >>> struct.unpack('ii', b'A\x00\x00\x00B\x00\x00\x00') (65, 66) >>> struct.unpack('bb', b'AB') (65, 66) >>> struct.unpack('4s2s', b'abcdef') ('abcd', 'ef')
如果是多字节的数据,我们可以在格式字符串的第一个字符(可指定为表 10 所示字符)指定大小端形式。
Character | Byte order | Size | Alignment |
---|---|---|---|
@ | native(依赖于 sys.byteorder) | native | native |
= | native | standard | none |
< | little-endian | standard | none |
> | big-endian | standard | none |
! | network (= big-endian) | standard | none |
如:
>>> struct.pack('i', 0x3) # 没有明确指定大小端,默认为@(在intel平台中为小端形式) '\x03\x00\x00\x00' >>> struct.pack('<i', 0x3) # 明确指定以小端形式编码 '\x03\x00\x00\x00' >>> struct.pack('>i', 0x3) # 明确指定以大端形式编码 '\x00\x00\x00\x03'
9.5.2. Struct 对象
如果使用同一个格式字符串进行多次打包解包操作,推荐使用 Struct 对象。它效率更高,因为需要解释一次格式字符串。如:
>>> s = struct.Struct('<ii') >>> s.pack(0x3, 0x4) '\x03\x00\x00\x00\x04\x00\x00\x00' >>> s.pack(0x5, 0x6) '\x05\x00\x00\x00\x06\x00\x00\x00'
9.5.3. struct.unpack 实例:分析 zip 文件头
下面是使用 struct.unpack
分析 zip 文件 header 的例子:
import struct zipfile_path = '/path/to/file.zip' # zip file header format (first 30 bytes): # All multi-byte values in the header are stored in little-endian byte order. # | Offset | Bytes | Description | # |--------+-------+---------------------------------------------------------------------------| # | 0 | 4 | Local file header signature = 0x04034b50 (read as a little-endian number) | # | 4 | 2 | Version needed to extract (minimum) | # | 6 | 2 | General purpose bit flag | # | 8 | 2 | Compression method | # | 10 | 2 | File last modification time | # | 12 | 2 | File last modification date | # | 14 | 4 | CRC-32 | # | 18 | 4 | Compressed size | # | 22 | 4 | Uncompressed size | # | 26 | 2 | File name length (n) | # | 28 | 2 | Extra field length (m) | # # See https://en.wikipedia.org/wiki/Zip_(file_format)#File_headers with open(zipfile_path, 'rb') as zipf: header = zipf.read(30) # read first 30 bytes in zip result = struct.unpack('<Ihhhhhiiihh', header) signature = result[0] min_ver = result[1] bit_flag = result[2] cmp_method = result[3] modif_time = result[4] modif_date = result[5] crc32 = result[6] cmp_size = result[7] uncmp_size = result[8] fname_len = result[9] extra_field_len = result[10] print(result) print("min_ver = {0}".format(min_ver)) print("modif_time = {0}".format(modif_time)) print("modif_date = {0}".format(modif_date))
9.6. enum 枚举
下面是 Python 中枚举的使用实例:
#!/usr/bin/env python3 from enum import Enum class Color(Enum): # 创建枚举的方式 1 RED = 0 GREEN = 1 BLUE = 2 # Color = Enum('Color', ['RED', 'GREEN', 'BLUE']) # 创建枚举的方式 2 color = Color.RED print(color.name) # 每个枚举都有 name 属性 print(color.value) # 每个枚举都有 value 属性 match color: # python 3.10 引入了 match 语句 case Color.RED: print("I see red!") case Color.GREEN: print("Grass is green") case Color.BLUE: print("I'm feeling the blues :(")
9.7. venv 模块(创建虚拟环境)
有时,工程 1 中使用的包 A 依赖于包 B 的 1.0 版本;而工程 2 中使用的包 C 依赖于包 B 的 2.0 版本。而包 B 的 2.0 版本和 1.0 版本不兼容,我们无法在系统中同时安装这两个版本。这时,通过创建“虚拟环境”可以解决这个问题。
Python 3.3 中增加了内置模块 venv,可以用来创建“虚拟环境”,在这个环境中安装的包会保存在当前环境关联的目录中,而不是系统目录中。
venv 的基本用法如下:
$ python3 -m venv /path/to/new/virtual/environment # 创建虚拟环境,需要指定新环境目录 $ ls /path/to/new/virtual/environment bin/ include/ lib/ lib64@ pyvenv.cfg share/ $ source /path/to/new/virtual/environment/bin/activate # 进入虚拟环境 $ pip install xxx # 进入虚拟环境后,安装的包都会放在的当前环境目录的子目录lib/pythonX.Y/中 $ deactivate # 退出虚拟环境
注:virtualenv 是另一个流行的 Python 虚拟环境创建工具,它支持 Python 2 和 Python 3(前面介绍的 venv 只支持 Python 3)。
参考:
https://virtualenv.pypa.io/en/latest/
https://docs.python.org/3/library/venv.html
10. pip 基本使用
pip 是安装 Python 模块的实用工具。
10.1. 安装软件(install)
$ pip install PackageName
10.1.1. 根据 requirements.txt 文件安装软件
$ pip freeze > requirements.txt # 把当前环境下安装的软件列表保存到requirements.txt中 $ pip install --requirement requirements.txt # 安装 requirements.txt 文件中的所有软件 $ pip install -r requirements.txt # 是上条命令的简写
下面是 requirements.txt 文件的一个例子
$ cat requirements.txt cycler==0.10.0 functools32==3.2.3.post2 matplotlib==2.0.2 numpy==1.13.0 pyparsing==2.2.0 python-dateutil==2.6.0 pytz==2017.2 six==1.10.0 subprocess32==3.2.7 vboxapi==1.0
不一定要指定精确版本,也可以使用 >=
指定“最低版本”,如:
numpy>=1.13.0
10.1.2. 升级软件
$ pip install --upgrade PackageName # 升级软件 $ pip install -U PackageName # 是上条命令的简写
10.2. 卸载软件(uninstall)
$ pip uninstall PackageName
10.3. 查看已经安装的软件列表(list)
$ pip list cycler (0.10.0) functools32 (3.2.3.post2) matplotlib (2.0.2) numpy (1.13.0) pip (9.0.1) pyparsing (2.2.0) python-dateutil (2.6.0) pytz (2017.2) setuptools (18.7.1) six (1.10.0) subprocess32 (3.2.7) vboxapi (1.0) wheel (0.26.0)
10.3.1. 查看过时的软件
$ pip list --outdated numpy (1.13.0) - Latest: 1.13.1 [wheel] setuptools (18.7.1) - Latest: 36.0.1 [wheel] wheel (0.26.0) - Latest: 0.29.0 [wheel]
10.4. 查看某个包的信息(show)
$ pip show numpy Name: numpy Version: 1.13.0 Summary: NumPy: array processing for numbers, strings, records, and objects. Home-page: http://www.numpy.org Author: NumPy Developers Author-email: numpy-discussion@python.org License: BSD Location: /usr/local/lib/python2.7/site-packages Requires:
10.5. 在线查找软件包(search)
$ pip search json |more json-rpc-3 (1.8.4) - Pure Python 3 JSON-RPC 2.0 transport realisation PyWaPa-3k (0.1.1) - Python WhAtever Parser is a python markup converter from xml, json, yaml and ini to python dictionary. It allows also conversion between markup languages. Python 3 compatible version. django-json-404-middleware (0.0.1) - Django middleware that returns 404s as JSON instead of html ......
11. Tips
11.1. 手动安装模块
Python Package Index 上有很多 python 模块。推荐使用工具 pip 来在线安装 python 模块。
下面介绍的是手动安装模块的典型过程。
从 Python Package Index 中下载想要安装的模块,假设文件名为 foo-1.0.tar.gz,解压后里面会有个 setup.py 文件,执行它可进行安装。比如:
$ gunzip -c foo-1.0.tar.gz | tar xf - # unpacks into directory foo-1.0 $ cd foo-1.0 $ python setup.py install --prefix=/Users/cig01/soft/ # 安装到系统目录中不用指定 --prefix 参数
如果想要查看帮助信息,可以执行 python setup.py --help
。如:
$ python setup.py --help Common commands: (see '--help-commands' for more) setup.py build will build the package underneath 'build/' setup.py install will install the package Global options: --verbose (-v) run verbosely (default) --quiet (-q) run quietly (turns verbosity off) --dry-run (-n) don't actually do anything --help (-h) show detailed help message --no-user-cfg ignore pydistutils.cfg in your home directory --command-packages list of packages that provide distutils commands Information display options (just display information, ignore any commands) --help-commands list all available commands --name print package name --version (-V) print package version --fullname print <package name>-<version> --author print the author's name --author-email print the author's email address --maintainer print the maintainer's name --maintainer-email print the maintainer's email address --contact print the maintainer's name if known, else the author's --contact-email print the maintainer's email address if known, else the author's --url print the URL for this package --license print the license of the package --licence alias for --license --description print the package description --long-description print the long package description --platforms print the list of platforms --classifiers print the list of classifiers --keywords print the list of keywords --provides print the list of packages/modules provided --requires print the list of packages/modules required --obsoletes print the list of packages/modules made obsolete usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: setup.py --help [cmd1 cmd2 ...] or: setup.py --help-commands or: setup.py cmd --help
参考:
Installing Python Modules (Legacy version): https://docs.python.org/2/install/
11.2. 列出本地安装的所有 python 模块
如何列出本地安装的所有 python 模块?
方法一,在 python 提示符中输入: help('modules')
,如:
>>> help('modules') Please wait a moment while I gather a list of all available modules... BaseHTTPServer audioop httplib sched Bastion base64 ihooks select CGIHTTPServer bdb imaplib sets ConfigParser binascii imghdr sgmllib Cookie binhex imp sha DocXMLRPCServer bisect importlib shelve HTMLParser bsddb imputil shlex IN bz2 inspect shutil MimeWriter cPickle io signal PyQt4 cProfile itertools sip Queue cStringIO json sipconfig SimpleHTTPServer calendar keyword sipdistutils SimpleXMLRPCServer cgi lib2to3 site SocketServer cgitb linecache smtpd StringIO chunk locale smtplib UserDict cmath logging sndhdr UserList cmd macpath socket UserString code macurl2path sqlite3 _LWPCookieJar codecs mailbox sre _MozillaCookieJar codeop mailcap sre_compile __builtin__ collections markupbase sre_constants __future__ colorsys marshal sre_parse _abcoll commands math ssl _ast compileall md5 stat _bisect compiler mhlib statvfs _bsddb contextlib mimetools string _codecs cookielib mimetypes stringold _codecs_cn copy mimify stringprep _codecs_hk copy_reg mmap strop _codecs_iso2022 crypt modulefinder struct _codecs_jp csv multifile subprocess _codecs_kr ctypes multiprocessing sunau _codecs_tw curses mutex sunaudio _collections datetime netrc symbol _csv dbhash new symtable _ctypes dbm nntplib sys _ctypes_test dbus ntpath sysconfig _curses decimal nturl2path syslog _curses_panel difflib numbers tabnanny _dbus_bindings dircache opcode tarfile _dbus_glib_bindings dis operator telnetlib _elementtree distutils optparse tempfile _functools doctest os termios _hashlib dsextras os2emxpath textwrap _heapq dumbdbm parser this _hotshot dummy_thread pdb thread _io dummy_threading pickle threading _json email pickletools time _locale encodings pipes timeit _lsprof errno pkgutil toaiff _multibytecodec exceptions platform token _multiprocessing fcntl plistlib tokenize _osx_support filecmp popen2 trace _pyio fileinput poplib traceback _random fnmatch posix tty _socket formatter posixfile types _sqlite3 fpformat posixpath unicodedata _sre fractions pprint unittest _ssl ftplib profile urllib _strptime functools pstats urllib2 _struct future_builtins pty urlparse _symtable gc pwd user _sysconfigdata gdbm py_compile uu _testcapi genericpath pyclbr uuid _threading_local getopt pydoc warnings _warnings getpass pydoc_data wave _weakref gettext pyexpat weakref _weakrefset gio pygtk webbrowser abc glib quopri whichdb aifc glob random wsgiref antigravity gobject re xdrlib anydbm grp readline xml argparse gzip repr xmllib array hashlib resource xmlrpclib ast heapq rexec xxsubtype asynchat hmac rfc822 zipfile asyncore hotshot rlcompleter zipimport atexit htmlentitydefs robotparser zlib audiodev htmllib runpy Enter any module name to get more help. Or, type "modules spam" to search for modules whose descriptions contain the word "spam".
方法二,在 shell 提示符中输入: pydoc modules
。
参考:
http://stackoverflow.com/questions/739993/how-can-i-get-a-list-of-locally-installed-python-modules
11.3. 检测某个 python 模块是否安装和模块安装位置
用命令 python -c 'import modulename' 可以检测系统中是否安装有指定模块,如果正确安装了模块 modulename,那么这个命令不会有任何输出;否则会提示错误信息,如:
$ python -c 'import pycurl' # 当前系统中没有模块pycurl,会提示类似下面的错误 Traceback (most recent call last): File "<string>", line 1, in <module> ImportError: No module named pycurl
用命令 python -c 'import modulename; print(modulename.__file__)' 可以进一步查看模块在系统中的所在路径。如:
$ python -c 'import zlib; print(zlib.__file__)' # 检测 zlib 所在路径 /usr/lib64/python2.6/lib-dynload/zlibmodule.so $ python -c 'import json.tool; print(json.tool.__file__)' # 检测 json.tool 所在路径 /usr/lib64/python2.6/json/tool.pyc
11.4. 一条命令实现 HTTP 服务
下面命令可实现一个 HTTP 服务,把当前目录设为 HTTP 的根目录,可以通过 http://localhost:8000 访问。
$ python -m SimpleHTTPServer # Python 2,默认端口8000 $ python -m SimpleHTTPServer [port] # Python 2,指定端口
如果你使用 Python 3,上面命令会报错,你可以执行:
$ python -m http.server # Python 3,默认端口8000 $ python -m http.server [port] # Python 3,指定端口
11.5. 提高 Python 代码质量
11.5.1. Python 代码风格(PEP8)
Python 有一套自己的代码风格。如有命名约定:py 脚本名,函数名,变量名等使用“全小写字母”的形式,要分隔的话中间使用“下划线”,如 lower_with_under.py;而对于类名,一般使用 CapitalizedWords 风格。
完整的代码风格可参考:PEP 8 -- Style Guide for Python Code
11.5.2. 使用 pycodestyle 检查代码是否符合 PEP8 规范
使用工具 pycodestyle (这个工具以前的名字是 pep8)可以检查代码是否符合 PEP8 规范。安装如下:
$ pip install pycodestyle
下面是 pycodestyle 的使用实例:
$ pycodestyle --show-source --show-pep8 testsuite/E40.py testsuite/E40.py:2:10: E401 multiple imports on one line import os, sys ^ Imports should usually be on separate lines. Okay: import os\nimport sys E401: import sys, os
11.5.3. 代码格式化工具
11.5.3.1. autopep8
使用工具 autopep8 (它依赖于 pycodestyle)可以转换 Python 代码为符合 PEP8 规范的代码。安装如下:
$ pip install autopep8
下面是使用 autopep8
原地修改源码的实例:
$ autopep8 --in-place --aggressive --aggressive <filename>
11.5.3.2. yapf(推荐)
yapf 是 Google 出品的一个 Python 代码格式化工具,推荐使用。
11.5.4. 静态分析工具(pylint)
使用工具 pylint 可以检测 Python 代码中的一些潜在问题,如警告未使用的变量等等。
11.6. Python 3 和 Python 2 不兼容
Writing code that runs under both Python2 and 3: https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef
Python 3 和 Python 2 有很多不兼容的地方,请参考: The key differences between Python 2.7.x and Python 3.x with examples
编写和 Python 3 和 Python 2 都兼容的代码,请参考: Cheat Sheet: Writing Python 2-3 compatible code