Python 备忘录
基本信息
官方手册
https://docs.python.org/zh-cn/3/
语言特征
长语句可以使用 \
折行,字符串中也可以使用 \
折行。
一行可以写多条语句,语句之间用分号分隔。
字符串使用 Unicode 编码,标识符可以使用汉字。
代码区分大小写。
有垃圾收集器。
可以使用 help()
获取帮助,比如 help(1.0)
或 help(float)
。
示例程序
#!python3
# 导入 os 模块中的 system 函数,更名为 run
from os import system as run
# 定义函数
def hello():
# 调用导入的 os.system 函数
run('echo Hello, World!')
# 一个 .py 文件即可以作为脚本被执行,也可以作为模块被导入
if __name__ == '__main__':
# 当作为脚本被执行时,会执行这里的代码
hello()
else:
# 当作为模块被导入时,会执行这里的代码
# 此时 __name__ 等于文件名(不包括 .py 后缀)
print('File name: ' + __name__)
# 全局范围的语句在两种情况下都会被执行
print('Hello Python!')
语法元素
注释
行注释以 #
开头,没有块注释。单独的字符串语句可以用作注释。
# 这是注释
'这是注释'
'这是注释'; print('Hello, World!'); '这是注释' # 这是注释
'这是注释'
'''
这是多行注释
这是多行注释
这是多行注释
'''
标识符
- 以字母(包括汉字)或下划线开头,后跟字母、数字、下划线。
- 长度没有限制。
特殊标识符:
_* # from module import * 时,不会被导入
__*__ # 系统定义的名称
__* # 类的私有成员
字面量
# 整数,长度没有限制
32
0xFFFF_FFFF
0b1111_1111
0o7777_7777
# 布尔值
True
False
# 浮点数,只有双精度
3.2
3.
.2
0_3.1_4
3.14e2
3.24e-2
0e0
0xFF.FFp3
# 复数
3.2j
3.2+0.5e2j
# 字符串,单引号和双引号没有区别
'单行字符串'
"单行字符串"
'''多行字符串'''
"""多行字符串"""
r'原始字符串,不转义'
# 两个字符串之间的空白会被忽略
'拼接' '字符串' # '拼接字符串'
# 字节串
b'字节串'
br'原始字节串'
# 元组,只有一个元素时,必须有逗号,否则括号会被理解为优先级
(1, 'a', True)
(1,)
()
# 列表
[1, 'a', True]
[1]
[]
# 集合,空集合必须用 set() 来创建,因为 {} 要用来创建空字典
{1, 'a', True}
set()
# 字典
{'a':1, 2:'Hello', True:3.14}
{}
转义字符
转义字符之外的 \
会被保留(比如 \c
中的 \
将被保留)。
转义字符 | 含义 |
| 忽略反斜杠与换行符 |
| 反斜杠( |
| 单引号( |
| 双引号( |
| 响铃(BEL) |
| 退格符(BS) |
| 换页符(FF) |
| 换行符(LF) |
| 回车符(CR) |
| 水平制表符(TAB) |
| 垂直制表符(VT) |
| 八进制 ASCII 码 ooo 表示的字符 |
| 十六进制 ASCII 码 hh 表示的字符 |
| Unicode 数据库中名为 name 的字符 |
| 十六进制 Unicode 码 xxxx 表示的字符 |
| 十六进制 Unicode 码 xxxxxxxx 表示的字符 |
数据类型
在 Python 中,一切皆对象,“数据类型”是以“类”的形式呈现的(类也是对象),“数据”是以“类的实例”的形式呈现的。常见的类型有如下几种:
# 数值类
int bool float complex
# 序列类
str bytes bytearray tuple list range
# 其它类
set frozenset dict memoryview
在这些类型中,有些是只读的,有些是可写的。要想修改只读的内容,只能新建一个同类型的对象来存放修改后的数据:
# 只读
int bool float complex str bytes tuple frozenset range
# 可写
bytearray list set dict
只读类型可以通过 hash()
函数计算 Hash 值,而可写类型无法计算 Hash 值:
hash((1,2,3)) # 529344067295497451
hash([1,2,3]) # TypeError: unhashable type: 'list'
变量
变量只是对“对象”的引用,变量没有类型,可以引用任何类型的对象。
a = 1
a = b = c = 1
a, b, c = 32, 3.2, True
每个对象都有“编号、类型、值”,每个对象的编号都是唯一的。在 CPython(C 语言实现的 Python)中,编号就是对象的内存地址。id()
函数能返回一个对象的编号:
id(1) # 140336514943280
id(int) # 140336537689696
id(id) # 140336514728000
# 为了节省内存,Python 在创建只读对象时,会引用已存在的只读对象,而不会创建新的。
a, b = [], [] # 声明两个空列表(列表是可写的)
id(a) == id(b) # False,它们是两个不同的对象
a, b = (), () # 声明两个空元组(元组是只读的)
id(a) == id(b) # True,它们是同一个对象
type()
函数能返回一个对象的类型:
type(1) # <class 'int'>
type(int) # <class 'type'>
type(type) # <class 'type'>
type(abs) # <class 'builtin_function_or_method'>
type(builtin_function_or_method)
# NameError: name 'builtin_function_or_method' is not defined
def f(): pass # 定义一个空函数
type(f) # <class 'function'>
type(function)
# NameError: name 'function' is not defined
print()
函数可以查看对象值的字符串表示形式:
print(1) # 1
print(int) # <class 'int'>
print(id) # <built-in function id>
print(type) # <class 'type'>
print(print) # <built-in function print>
运算符
+ - * ** / // %
<< >> & | ^ ~
= :=
+= -= *= **= /= //= %=
<<= >>= &= |= ^=
> >= < <= == !=
. () [] ...
and or not
is is not
in not in
@
**
是乘方,/
是浮点数除法,结果为浮点数,//
是整除,如果有一个操作数是浮点类型,则结果也是浮点类型,:=
是语句内赋值,比如 if a:=1 > 0: ...
,不能写成 if a=1 > 0: ...
.
是成员引用,()
是函数调用,[]
是下标引用。
...
是 Ellipsis
类的简写,它的作用是不固定的,当不同的函数读取到 ...
参数时,会根据需要做不同的解释,目前知道的用法如下:
- 在定义函数时,表示函数没有实现代码,和
pass
的作用类似。比如def f(): ...
。 - 在
Numpy
中可以用来简写参数,比如a[:, :, None]
可以简写为a[…, None]
。
is
用于判断两个变量是否引用同一个对象,==
用于判断变量所引用的对象的值是否相等。
@
是函数装饰符,用法如下:
#!python3
# 包装其它函数的函数
def funA(fn):
print('funA')
def f():
fn()
return f
@funA # 即:funB = funA(funB) # 会执行一次 funA
def funB(): # 被包装的函数
print('funB')
# 输出结果
# funA
funB() # 相当于执行一次 f
# 输出结果
# funB
@funA # 即:funC = funA(funA(funC)) # 会执行两次 funA
@funA
def funC(): # 被包装的函数
print('funC')
# 输出结果
# funA
# funA
funC() # 相当于执行一次 f
# 输出结果
# funC
结构语句
循环语句中可以使用 continue
和 break
。
if 条件:
语句
elif 条件:
语句
else:
语句
if 条件: 语句
while 条件:
语句
else:
语句 # 未运行 break 时执行
while 条件: 语句
# 如果可迭代对象返回的值是元组类型(比如枚举),则可以使用
# for 元素1, 元素2, ... in 可迭代对象
# 的方式对返回结果進行解包。
for 值 in 可迭代对象:
语句
else:
语句 # 未运行 break 时执行
for 值 in 可迭代对象: 语句
# with 用于简化 try、except、finally 和资源的分配释放。
# with 通过 __enter__() 方法初始化,通过 __exit__() 方法终结化。
# 使用 with 的对象必须有 __enter__() 和 __exit__() 方法,
# 比如 open() 函数返回的对象,比如 memoryview 对象。
with 对象 [ as 名称 ]:
语句
with 对象 [ as 名称 ]: 语句
数值类型
int
构造器 int()
可以将其它类型的对象转换为整数。
int('FF', 16) # 255
int(3.2) # 3
int() # 0
int
对象的方法:
# 返回数值的位长度
int.bit_length()
bin(64) # '0b1000000'
(64).bit_length() # 7
# 将整数转换为字节串
# length 指定转换后的字节串长度
# byteorder 指定字节序,可以为 'little' 或 'big'
int.to_bytes(length, byteorder, *, signed=False)
(33).to_bytes(4, 'big') # b'\x00\x00\x00!'
(-33).to_bytes(4, 'big', signed=True) # b'\xff\xff\xff\xdf'
# 将字节串转换为整数
class int.from_bytes(bytes, byteorder, *, signed=False)
int.from_bytes(b'abcd', 'big') # 1633837924
int.from_bytes([255,0,0], 'big', signed=True) # -65536
bool
bool
类是 int
类的子类。可以使用 int
类的所有属性和方法。
构造器 bool()
可以将其它类型的对象转换为布尔值。
bool() # False
bool(0) # False
bool(0.0) # False
bool(0j) # False
bool('') # False
bool([]) # False
bool({}) # False
bool(set()) # False
bool(range(0)) # False
bool('False') # True
默认情况下,对象均被视为真值,除非该对象定义了 __bool__()
方法且返回 False
,或定义了 __len__()
方法且返回零。
布尔结果总是返回 0
或 False
作为假值,返回 1
或 True
作为真值,除非另行说明。(重要例外:布尔运算 or
和 and
总是返回其中一个操作数。)
5 > 3 # True
5 < 3 # False
True + 1 # 2
5 and 3 # 3
0 and 3 # 0
float
构造器 float()
可以将其它类型的对象转换为浮点数。
float(32) # 32.0
float('32') # 32.0
float() # 0.0
float
对象的方法:
# 判断小数部分是否为 0
float.is_integer()
(3.0).is_integer() # True
# 十進制格式转换为十六進制格式
float.hex()
(3.2).hex() # '0x1.999999999999ap+1'
# 十六進制格式转换为十進制格式
float.fromhex(string)
# p3 表示乘以 10 的 3 次方
float.fromhex('0xFF.FFFFp3') # 2047.9998779296875
# 返回浮点数的分数格式,分母为正数
float.as_integer_ratio()
(0.25).as_integer_ratio() # (1, 4)
(0.2).as_integer_ratio() # (3602879701896397, 18014398509481984)
complex
构造器 complex()
可以将其它类型的对象转换为复数。
complex(3) # (3+0j)
complex(3, 2) # (3+2j)
complex('3+2j') # (3+2j)
complex() # 0j
complex
对象的属性和方法:
# 返回复数的实部/虚部
complex.real
complex.imag
(3+2j).real # 3.0
(3+2j).imag # 2.0
# 返回复数的共轭复数
complex.conjugate()
(3+2j).conjugate() # (3-2j)
序列类型
除了数值类型,最常用的就是序列类型。“序列”是可以按顺序访问的元素的集合。所有序列类型都支持下列操作:
x in s # 元素 x 是否在序列 s 中
x not in s # 元素x 是否不在序列 s 中
s + t # 连接两个序列
s * n 或 n * s # 将序列 s 重复 n 次
s[i] # 通过下标引用序列的元素
s[i:j:k] # 获取序列的切片
len(s) # 获取序列长度
min(s) # 序列中的最小元素
max(s) # 序列中的最大元素
s.index(x[, i[, j]]) # 获取 x 在序列中第一次出现的位置,找不到则引发异常
s.count(x) # 统计 x 在序列中出现的次数
切片用法示例(以字符串为例):
# 截取字符串 [起始:结束:步长](包含起始索引,不包含结束索引)
'Hello World!'[-1] # !
'Hello World!'[0:-1] # Hello World
'Hello World!'[:] # Hello World!
'Hello World!'[-6:] # World!
'Hello World!'[::2] # HloWrd # 以指定的步长提取字符
'Hello World!'[::-1] # !dlroW olleH # 反向提取字符
'Hello World!'[::-2] # !lo le
虽然 in
和 not in
操作在通常情况下仅被用于简单的成员检测,但某些专门化的序列(例如 str
, bytes
和 bytearray
) 也使用它们进行子序列检测:
[1,2] in [1,2,3] # False
'ab' in 'abc' # True
请注意,在重复序列时,序列中的项并不会被拷贝,而是被多次引用。例如:
a = [[]] * 3 # [[], [], []]
a[0].append(3) # [[3], [3], [3]]
可以用以下方式创建不同元素的列表:
a = [[] for i in range(3)]
a[0].append(3)
a[1].append(5)
a[2].append(7)
a # [[3], [5], [7]]
切片操作会复制对象,而不是引用对象:
a = [1,2,3]
b = a
id(b) == id(a) # True,引用
b = a[:]
id(b) == id(a) # False,复制
序列比较(子序列中的元素也会按顺序比较)
(1, 2, (1, 2)) < (1, 2, (1, 3), 5) # True
(1, 2, (2, 2)) < (1, 2, (1, 3), 5) # False
拼接“不可变序列”总是会生成新的对象。这意味着多次“拼接序列”会有很大的开销。想要降低开销,可以使用以下方案:
- 如果拼接
str
对象,可以构建一个str
列表并在最后使用str.join([...])
,或是写入一个io.StringIO
实例,并在结束时获取它的值。 - 如果拼接
bytes
对象,你可以类似地使用bytes.join()
或io.BytesIO
,也可以使用bytearray
对象进行原地拼接。bytearray
对象是可变的,并且具有高效的重分配机制。 - 如果拼接
tuple
对象,请改用可写的list
类。 - 对于其它类型,请查看相应的文档
所有“可变序列”类型都支持下列操作:
s[i] = x # 将 s 的第 i 项替换为 x
s[i:j] = t # 将 s[i:j] 的元素替换为 t 的元素(长度可以不同)
del s[i:j] # 删除 s[i:j] 的元素,等同于 s[i:j] = []
s[i:j:k] = t # 将 s[i:j:k] 的元素替换为 t 的元素(如果 k!=1 则长度必须相同)
del s[i:j:k] # 删除 s[i:j:k] 的元素
s *= n # 将 s 的内容重复引用 n 次(不是拷贝,是引用)
s.extend(t) 或 s += t # 将 t 的元素追加到 s 的末尾 (基本等同于 s[len(s):len(s)] = t)
s.append(x) # 将 x 追加到序列的末尾 (等同于 s[len(s):len(s)] = [x])
s.clear() # 清空序列 (等同于 del s[:])
s.copy() # 创建 s 的浅拷贝 (等同于 s[:])
s.insert(i, x) # 将 x 插入 s 中索引为 i 的位置 (等同于 s[i:i] = [x])
s.pop([i]) # 获取索引为 i 的元素(i 默认为 -1),并将其从 s 中移除
s.remove(x) # 删除 s 中第一个元素值为 x 的项目(找不到则引发异常)
s.reverse() # 反转元素序列,结果写入 s 中,无返回值
tuple
tuple() # ()
() # ()
1, # (1,)
(1,) # (1,)
1, 'a', True # (1, 'a', True)
(1, 'a', True) # (1, 'a', True)
# 可以将任何可迭代对象转换为元组
tuple('abc') # ('a', 'b', 'c')
tuple([1,2,3]) # (1, 2, 3)
collections.namedtuple()
可以通过名称访问元组中的元素。
list
list() # []
[] # []
[1] # [1]
[1, 'a', True] # [1, 'a', True]
# 可以将任何可迭代对象转换为列表
list('abc') # ['a', 'b', 'c']
list((1,2,3)) # [1, 2, 3]
# 星号可以展开序列
[*'abc'] # ['a', 'b', 'c']
[*(1,2,3)] # [1, 2, 3]
# 可以使用“列表推导式”创建列表
[x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
# [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
[str(round(3.1415926, i)) for i in range(1, 6)]
# ['3.1', '3.14', '3.142', '3.1416', '3.14159']
matrix = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ]
[[row[i] for row in matrix] for i in range(4)]
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
列表排序
# 对列表進行排序,结果写入列表,无返回值。只使用 '<' 来进行各项间比较。
# 如果有任何比较操作失败,整个排序操作将失败(而列表可能会处于被部分修改的状态)。
# key 是带有一个参数的函数,用于在比较时对各个元素進行转换 (例如 key=str.lower)。
# 如果 reverse 为 True,则将结果颠倒。
list.sort(*, key=None, reverse=False)
a=[*'Hello World!']
a.sort()
# [' ', '!', 'H', 'W', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r']
a.sort(key=str.lower)
# [' ', '!', 'd', 'e', 'H', 'l', 'l', 'l', 'o', 'o', 'r', 'W']
range
range(5) # range(0, 5)
range(2, 5) # range(2, 5)
range(5, 2, -1) # range(5, 2, -1)
list(range(5)) # [0, 1, 2, 3, 4]
range()
的参数可以是 int
或任何实现了 __index__
方法的对象。
range
对象实现了一般序列的所有操作,但拼接和重复除外。
range
对象总是占用固定大小的内存,无论其表示的范围有多大(因为它只保存了 start
, stop
和 step
的值)
str
str(123)+'abc' # 123abc
有三种方式实现格式化字符串:
1、'%(name)'
该方法使用“字典中的元素”来格式化字符串。
不推荐使用该方法,因为它可能会导致许多错误(例如无法正确显示元组和字典)。
# 使用“字典中的元素”格式化字符串
'%(a)d %(b)d' % { 'a':3200, 'b':32 } # 3200 32
# 字符串中的 %% 会被转义为单个 %
'%% %%' % {} # % %
2、f'{name}'
该方法使用“已定义的变量”来格式化字符串。
# 使用“已定义的变量”格式化字符串
a, b = 3200, 32
f'{a} {b}' # 3200 32
# 字符串中的 {{ 和 }} 会被转义为单个的 { 和 }
f'{{ }}' # { }
# 可以使用表达式
a = 1
f'{a+1}' # 2
f'{len("abc")}' # 3
# 如果内容以 = 结尾,则会输出表达式本身及其结果
a, b = 1, 2
f'{a + b = }' # a + b = 3
3、'{}'.format()
该方法使用“format() 中指定的值”来格式化字符串。
# 使用“指定值”格式化字符串
'{} {}'.format(3200, 32) # 3200 32
'{1} {0}'.format(3200, 32) # 32 3200
'{b} {a}'.format(a=3200, b=32) # 32 3200
# 字符串中的 {{ 和 }} 会被转义为单个的 { 和 }
'{{ }}'.format() # { }
# 使用表达式
'{} {}'.format(1+1, len('abc')) # 2 3
# 可以通过下标获取元组 () 或列表 [] 的元素
data = ( 'Hello', 'World', '!' )
'{0[0]} {0[1]}{0[2]}'.format(data) # Hello World!
# 元组 () 或列表 [] 可以用 * 展开
data = ( 'Hello', 'World', '!' )
'{} {}{}'.format(*data) # Hello World!
# 字典 {} 可以用 ** 展开
data = { 'a':'Hello', 'b':'World', 'c':'!' }
'{a} {b}{c}'.format(**data) # Hello World!
# 直接使用字典
'{a} {b}{c}'.format_map(data) # Hello World!
指定格式
# 十進制 d、二進制 b、八進制 o、十六進制 x
'{0:d} {0:b} {0:o} {0:x}'.format(60) # 60 111100 74 3c
# 指定前缀
'{0:#d} {0:#b} {0:#o} {0:#x}'.format(60) # 60 0b111100 0o74 0x3c
# 大写格式(只有十六進制 X 可以大写)
'{0:#d} {0:#b} {0:#o} {0:#X}'.format(60) # 60 0b111100 0o74 0X3C
# 显示千位分隔符
'{:,d}'.format(320000000) # 320,000,000
'{:_d}'.format(320000000) # 320_000_000
'{:#_x}'.format(320000000) # 0x1312_d000 # 非十進制会按 4 位分隔
# 使用系统当前的“区域设置”来插入分隔符(我的系统中没有设置分隔符)
'{:n}'.format(320000000) # 320000000
# 整数,指定符号 -(表示只有负数使用符号位,这是默认行为,可省略)
'|{:-d}|{:-d}|'.format(3200, -32) # |3200|-32|
# 整数,指定符号 +
'|{:+d}|{:+d}|'.format(3200, -32) # |+3200|-32|
# 整数,保留符号位(空格)
'|{: d}|{: d}|'.format(3200, -32) # | 3200|-32|
# 整数,指定宽度(任意整数,以 0 开头表示使用前导 0)
'|{:8d}|{:08d}|'.format(3200, -32) # | 3200|-0000032|
# 整数,指定对齐 < ^ >
'|{:^8d}|{:<+8d}|'.format(3200, -32) # | 3200 |-32 |
# 整数,指定符号位置 =
'|{:=+8d}|{:=+8d}|'.format(3200, -32) # |+ 3200|- 32|
# 整数,指定填充(任意字符,需要与对齐符号一起使用)
'|{0:a<+8d}|{0:b>+8d}|'.format(3200) # |+3200aaa|bbb+3200|
# 字符串 s(可省略)
'|{:8s}|{:8}|'.format('Hello', 'World') # |Hello |World |
# 字符串,预格式化。!r 表示 repr()
'Hello {!r}!'.format('World') # Hello 'World'!
'Hello {}!'.format(repr('World')) # Hello 'World'!
# 字符串,预格式化。!a 表示 ascii()
'{!a}'.format('Hello 世界') # 'Hello \u4e16\u754c'
ascii('Hello 世界') # 'Hello \u4e16\u754c'
# 字符 c,指定编码,功能与 chr() 相同。可以通过 ord() 获取字符编码
'{:c}'.format(22909) # 好
chr(22909) # 好
ord('好') # 22909
# 小数格式 f、科学计数法 e
'{0:f} {0:e}'.format(3.2) # 3.200000 3.200000e+00
# 大写格式(对于 F 没有效果)
'{0:F} {0:E}'.format(3.2) # 3.200000 3.200000E+00
# 浮点数,指定宽度、符号、填充
'|{:+016f}|{:016e}|'.format(3200, -32) # |+00003200.000000|-0003.200000e+01|
# 浮点数,指定精度
'|{:.2f}|{:.2e}|'.format(3200, -32) # |3200.00|-3.20e+01|
# 浮点数,指定精度或有效数字
'|{0:.3f}|{0:.3}|'.format(3.1415926) # |3.142|3.14|
# 浮点数,自动选择格式 g 或 G
# 如果数值超出精度范围,或结果指数低于 -4 或高于 5,则用科学计数法。
'{:.16g}'.format(3200000000000000.0) # 3200000000000000
'{:.16g}'.format(32000000000000000.0) # 3.2e+16
'{:g}'.format(0.00032) # 0.00032
'{:g}'.format(0.000032) # 3.2e-05
'{:g}'.format(320000) # 320000
'{:G}'.format(3200000) # 3.2E+06
# 是否显示小数点
'{:g}'.format(32) # 32
'{:#g}'.format(32) # 32.0000
# 通过变量控制格式
'|{:{a}.{b}f}|'.format(3.1415926, a=8, b=2) # | 3.14|
# 指定日期格式
from datetime import datetime
today = datetime(year=2021, month=2, day=15)
'{:%Y-%m-%d}'.format(today) # 2021-02-15
f'{today = :%Y-%m-%d}' # today = 2021-02-15
# 模版字符串
from string import Template
s = Template('$a ${b}')
s.substitute(a='Hello', b='World!') # 'Hello World!'
data = dict(a='Hello')
Template('$a $$').substitute(data) # 'Hello $'
Template('$a $b').substitute(data) # 不存在 $b,引发异常
Template('$a $b').safe_substitute(data) # 'Hello $b',不引发异常
字符串函数
# 全部大写/全部小写/反转大小写
str.upper()
str.lower()
str.swapcase()
# 消除大小写(适用于 Unicode 字符)
str.casefold()
'Hello ABCß'.casefold() # 'hello abcss'
'Hello ABCß'.lower() # 'hello abcß'
# 字符串/单词首字母大写
str.capitalize()
str.title()
'hello world'.capitalize() # 'Hello world'
'hello world'.title() # 'Hello World'
# 居左/居中/居右填充字符串(填充字符可省略,默认为空格)
str.ljust(width[, fillchar])
str.center(width[, fillchar])
str.rjust(width[, fillchar])
# 在字符串前面填充 0,正负号会放在 0 前面
str.zfill(width)
'-123'.zfill(8) # '-0000123'
'+abc'.zfill(8) # '+0000abc'
# 正向/反向查找子串,返回索引,失败返回 -1
str.find(sub[, start[, end]])
str.rfind(sub[, start[, end]])
'or' in 'World' # True
# 正向/反向查找子串,返回索引,失败引发 ValueError 异常。
str.index(sub[, start[, end]])
str.rindex(sub[, start[, end]])
# 判断字符串是否以指定的子串开头/结尾
str.startswith(prefix[, start[, end]])
str.endswith(suffix[, start[, end]])
# 统计子串个数(起始索引和结束索引可省略)
str.count(sub[, start[, end]])
# 替换字符串,可指定替换次数
str.replace(old, new[, count])
# 制表符转空格
str.expandtabs(tabsize=8)
# 修剪字符串左边/右边/左右的指定字符,可以指定多个字符(组成字符串),默认为空格
str.lstrip([chars])
str.rstrip([chars])
str.strip([chars])
# 删除字符串的指定前缀/后缀
str.removeprefix(prefix)
str.removesuffix(suffix)
# 在 sep 首次/最后出现的位置拆分字符串
# 返回一个 3 元组,其中包含分隔符之前的部分、分隔符本身、分隔符之后的部分
# 如果分隔符未找到,则返回的 3 元组中包含分隔符本身和两个空字符串。
str.partition(sep)
str.rpartition(sep)
# 将字符串以指定的分隔符从左/从右拆分成单词列表。
# sep 可以是字符串,默认以空格为分隔符。
# maxsplit 可以指定拆分次数,默认不限次数。
str.split(sep=None, maxsplit=-1)
str.rsplit(sep=None, maxsplit=-1)
# 按行拆分字符串,如果 keepends 为 True,则保留行边界字符
# 行边界字符包括:\r \n \r\n \v \f
# \x1c 文件分隔符 \x1d 组分隔符 \x1e 记录分隔符
# \x85 下一行(C1 控制码) \u2028 行分隔符 \u2029 段分隔符
str.splitlines([keepends])
# 最后一个边界字符不会产生新的空元素
'Hello\n'.splitlines() # ['Hello']
'Hello\n\n'.splitlines() # ['Hello', '']
# 返回一个由 iterable 中的字符串拼接而成的字符串,以 str 为分隔符
str.join(iterable)
'--'.join(['a', 'b', 'c', 'd']) # 'a--b--c--d'
# 判断字符串中的字符是否全为大写/小写
str.isupper()
'你好ABCABC'.isupper() # True
str.islower()
'你好abcabc'.islower() # True
# 判断字符串是否为标题字符串
# 大写字符之后只能带非大写字符,而小写字符必须以大写字符开头
str.istitle()
'你好Abc Def'.istitle() # True
'Abc你好 Def'.istitle() # True
'你好Abc def'.istitle() # False
'你好Abc DeF'.istitle() # False
# 判断字符串是否由“字母”或“数字”组成(字母包括汉字)
str.isalnum()
'你好123'.isalnum() # True
# 判断字符串是否由“字母”组成(字母包括汉字)
str.isalpha()
# 判断字符串是否由“十进制字符”组成
# 十进制字符是 Unicode 通用类别 'Nd' 中的一个字符
str.isdecimal()
'123'.isdecimal() # True
# 判断字符串是否由“数字”组成
# 数字包括十进制字符和需要特殊处理的数字,如兼容性上标数字。
str.isdigit()
'123'.isdigit() # True
# 判断字符串是否由“数值”组成
# 数值包括数字,以及所有在 Unicode 中设置了数值特性的字符
str.isnumeric()
'123'.isnumeric() # True
# 判断字符串是否由“ASCII 字母”组成
str.isascii()
# 判断字符串是否由“空白”组成
# 空白字符是指在 Unicode 字符库中主要类别为 Zs 或其双向类为 WS、B 或 S 的字符。
str.isspace()
# 判断字符是否为可打印字符
# 不可打印字符是在 Unicode 字符数据库中被定义为 'Other' 或 'Separator' 的字符,
# 例外情况是 ASCII 空格字符 (0x20) 被视作可打印字符。
str.isprintable()
# 判断字符串是否为有效的标识符
str.isidentifier()
# 以给定的转换表转换字符串。转换表必须是一个使用 __getitem__() 来实现索引操作的对象,
# 通常为“映射类型”或“序列类型”。当以 Unicode 码点为索引时,转换表对象可以做以下
# 任何一种操作:
# 返回 Unicode 序号或字符串,将字符映射为一个或多个字符;
# 返回 None,将字符从结果中删除;
# 引发 LookupError 异常,将字符映射为其自身。
# 你可以使用 str.maketrans() 来创建一个转换映射表。
# 另请参阅 codecs 模块以了解定制字符映射的更灵活方式。
str.translate(table)
# 此静态方法返回一个可供 str.translate() 使用的转换对照表。
# 如果只有一个参数,则它必须是一个字典,以将“Unicode 码点、字符”映射到“Unicode 码点、(任意长度的)字符串或 None”。字符键将被转换为码点。
# 如果有两个参数,则它们必须是两个长度相等的字符串,并且在结果字典中,x 中每个字符将被映射到 y 中相同位置的字符。
# 如果有第三个参数,它必须是一个字符串,其中的字符将在结果中被映射到 None。
static str.maketrans(x[, y[, z]])
# 用指定的编码将“字符串”转换为“字节串”
# errors 用来设置不同的错误处理方案。'strict' 表示编码错误会引发 UnicodeError。
# 其他可用的值为 'ignore'、'replace'、'xmlcharrefreplace'、'backslashreplace',
# 以及任何通过 codecs.register_error() 注册的值。
# 编码查询:# https://docs.python.org/zh-cn/3/library/codecs.html#standard-encodings
str.encode(encoding='utf-8', errors='strict')
字节串
bytes 对象是由单个字节构成的“不可变序列”。
bytearray 对象是由单个字节构成的“可变序列”。
class bytes([source[, encoding[, errors]]])
class bytearray([source[, encoding[, errors]]])
bytes(5) # b'\x00\x00\x00\x00\x00'
bytearray(5) # bytearray(b'\x00\x00\x00\x00\x00')
bytes(range(5)) # b'\x00\x01\x02\x03\x04'
bytearray(range(5)) # bytearray(b'\x00\x01\x02\x03\x04')
bytes(33 for i in range(5)) # b'!!!!!'
bytearray(33 for i in range(5)) # bytearray(b'!!!!!')
bytes('你好', 'utf8') # b'\xe4\xbd\xa0\xe5\xa5\xbd'
bytearray('你好', 'utf8') # bytearray(b'\xe4\xbd\xa0\xe5\xa5\xbd')
字节串的方法:
# 将十六進制编码转换为字节串,忽略 ASCII 码中的空白符
classmethod bytes.fromhex(string)
classmethod bytearray.fromhex(string)
bytes.fromhex('48 656c6c 6f') # b'Hello'
bytearray.fromhex('48 656c6c 6f') # bytearray(b'Hello')
# 将字节串转换为十六進制编码
bytes.hex([sep[, bytes_per_sep]])
bytearray.hex([sep[, bytes_per_sep]])
b'Hello'.hex() # '48656c6c6f'
b'Hello'.hex('-') # '48-65-6c-6c-6f'
b'Hello'.hex('-', 2) # '48-656c-6c6f'
bytearray(b'Hello').hex('-', 3) # '4865-6c6c6f'
“字符串”中的方法也适用于“字节串”,不过参数必须是“字节串”:
upper lower swapcase capitalize title
ljust center rjust zfill
find rfind index rindex startswith endswith count
replace expandtabs lstrip rstrip strip removeprefix removesuffix
partition rpartition split rsplit splitlines join
isupper islower istitle isalnum isalpha isdigit isascii isspace
translate maketrans decode
字节串支持 %(name)
方式的格式化操作:
b'Hello %(a)s' % {b'a':b'World!'} # b'Hello World!'
memoryview
memoryview
对象允许 Python 代码访问一个对象的内部数据而无需进行拷贝(只要该对象支持“缓冲区协议”)。支持缓冲区协议的内置对象包括 bytes
和 bytearray
。
#!python3
a = bytearray(b'abcdabcd') # 将字符串转换为可写的字节串
v = memoryview(a) # 获取字节串的内存视图
# 操作视图
print(list(v)) # [97, 98, 99, 100, 97, 98, 99, 100]
print(v.itemsize) # 1 # 元素大小(字节)
print(len(v)) # 8 # 元素个数
# 转换内存视图的布局
v1 = v.cast('I', [2]) # 更改元素类型为无符号整数(4 字节),共 2 个元素
# 操作转换后的视图
print(list(v1)) # [1684234817, 1684234849]
print(v1.itemsize) # 4 # 元素大小(字节)
print(len(v1)) # 2 # 元素个数
v1[0] = 0x44434241 # 修改第一个整数的内容
# 释放视图
v1.release() # 销毁视图对象,不再操作
v.release() # 销毁视图对象,不再操作
# 结果会反映到原始数据中
print(a) # b'ABCDabcd'
memoryview
支持 with
语句,可以省略 release()
过程:
#!python3
a = bytearray(b'abcdabcd')
with memoryview(a) as v:
print(list(v)) # [97, 98, 99, 100, 97, 98, 99, 100]
print(v.itemsize) # 1
print(len(v)) # 8
with v.cast('I', [2]) as v:
print(list(v)) # [1684234817, 1684234849]
print(v.itemsize) # 4
print(len(v)) # 2
v[0] = 0x44434241 # 修改第一个整数的内容
print(a) # b'ABCDabcd'
可用的内存视图格式(64 位系统):
https://docs.python.org/zh-cn/3/library/struct.html#format-characters
格式 | C 类型 | Python 类型 | 标准大小 |
| 填充字节 | 无 | |
|
| 长度为 1 的字节串 | 1 |
|
| int | 1 |
|
| int | 1 |
|
| bool | 1 |
|
| int | 2 |
|
| int | 2 |
|
| int | 4 |
|
| int | 4 |
|
| int | 8 |
|
| int | 8 |
|
| int | 8 |
|
| int | 8 |
|
| int | 8 |
|
| int | 8 |
| 半精度浮点数 | float | 2 |
|
| float | 4 |
|
| float | 8 |
|
| 字节串 | |
|
| 字节串 | |
|
| int | 8 |
memoryview
对象的方法:
v.tobytes(order=None) # 返回缓冲区数据的字节串格式
v.hex([sep[, bytes_per_sep]]) # 返回缓冲区数据的十六進制编码字符串
v.tolist() # 返回缓冲区数据的列表格式
v.toreadonly() # 返回一个只读的内存视图
v.obj # 底层对象
v.nbytes # 数组在连续表示时占用的字节数,不一定等于 len(v)
v.readonly # 是否只读
v.format # 元素类型
v.itemsize # 元素大小
v.ndim # 维数
v.shape # 形状
v.strides # 每个维度中每个元素的大小
# 转换视图的形状
# format 是新的元素类型
# shape 的默认值为 [总字节长度//新的元素大小],可以指定多维视图 [m, n]
v.cast(format[, shape])
# 释放内存视图对象(可以使用 with 语句省略 release() 语句)
# 在此方法被调用后,任何对视图的进一步操作将引发 ValueError(release() 本身除外)
# 许多对象在被视图所获取时都会采取特殊动作(例如,bytearray 将会暂时禁止调整大小)
# 因此,调用 release() 可以尽早去除这些限制(并释放任何多余的资源)
v.release()
集合
集合是由“不重复”的“可 Hash”对象组成的“无序”容器。基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算。
由于计算机对于浮点数存储的只是近似值,因此将其用作集合元素是不明智的。
set
可写,forzenset
只读,它们之间可以相互比较。
set() # set()
frozenset() # frozenset()
set("aabb") # {'a', 'b'} # 参数可以是任何可迭代对象
frozenset("aabb") # frozenset({'a', 'b'})
{1,2,3} # {1, 2, 3}
{1, 1.0, True} # {1}
集合操作:
len({1, 2, 3}) # 3
1 in {1, 2, 3} # True # 成员是否存在
1 not in {1, 2, 3} # False # 成员是否不存在
{1, 2} <= {1, 2} # True # 是否为子集
{1, 2} < {1, 2, 3} # True # 是否为真子集
{1, 2} < frozenset({1, 2, 3}) # True
{1, 2} & {2, 3} # {2} # 交集
{1, 2} | {2, 3} # {1, 2, 3} # 并集
{1, 2, 3} - {1, 2} # {3} # 差集
{1, 2} ^ {2, 3} # {1, 3} # 对称差集
# 集合支持迭代
for i in {1, 2, 3}: print(i)
集合的方法:
# 可用于 set 和 frozenset 的方法
s.isdisjoint(t) # 如果 s 中没有与 t 共有的元素则返回 True
s.issubset(t) # 返回 s <= t # 参数为可迭代对象
s.issuperset(t) # 返回 s >= t # 参数为可迭代对象
s.union(*t) # 返回 s | t | ... # 参数为可迭代对象
s.intersection(*t) # 返回 s & t & ... # 参数为可迭代对象
s.difference(*t) # 返回 s - t - ... # 参数为可迭代对象
s.symmetric_difference(t) # 返回 s ^ t # 参数为可迭代对象
s.copy() # 返回浅拷贝
# 只可用于 set 的方法
s.update(*t) # s |= t | ... # 参数为可迭代对象
s.intersection_update(*t) # s &= t & ... # 参数为可迭代对象
s.difference_update(*t) # s -= t | ... # 参数为可迭代对象
s.symmetric_difference_update(t) # s ^= t # 参数为可迭代对象
s.add(x) # 添加单个元素
s.remove(x) # 删除单个元素,不存在则引发 KeyError
s.discard(x) # 删除单个元素,不存在则忽略
s.pop() # 随机弹出元素,空集合则引发 KeyError
s.clear() # 清空集合
映射
映射会将“可 Hash”对象映射到“任意对象”。目前只有一种标准映射类型 dict
。
dict
字典的键必须是可 Hash 对象,与集合的要求相同。
由于计算机对于浮点数存储的只是近似值,因此将其用作字典的键是不明智的。
字典是“有序的”,会保留插入时的顺序,更新某个元素不会改变其顺序。
dict(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3}
dict((['a',1], ['b',2], ['c',3])) # {'a': 1, 'b': 2, 'c': 3}
dict(zip('abc', [1, 2, 3])) # {'a': 1, 'b': 2, 'c': 3}
dict({'a':1, 'b':2}, c=3, d=4) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
dict(zip('ab', [1,2]), c=3, d=4) # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
dict.fromkeys('abc', 0) # {'a': 0, 'b': 0, 'c': 0}
{ 1:'Hello', True:'World', 'a':3, } # {1: 'World', 'a': 3} # True == 1
{x: x ** 2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
字典操作
len({'a':1, 'b':2, 'c':3}) # 3
d[k] # 获取指定的值,不存在则引发 KeyError
d[k] = v # 设置指定的值,不存在则添加
del d[k] # 删除指定元素,不存在则引发 KeyError
k in d # 判断键是否存在
k not in d # 判断键是否不存在
d | t # 合并字典 d 和字典 t 的内容,生成新的字典
d |= t # 使用字典 t 的内容更新字典 d
d == t # 判断两个字典的内容是否相同(不考虑顺序)
d != t # 判断两个字典的内容是否不同(不考虑顺序)
字典的方法:
d.clear() # 清空字典
d.copy() # 返回浅拷贝
d.get(k[, v]) # 获取指定的值,不存在则返回 v,无 v 则引发 KeyError
d.items() # 返回由字典的 (键, 值) 对组成的新视图。
d.keys() # 返回由字典键组成的新视图。
d.values() # 返回由字典值组成的新视图。
d.pop(k[, v]) # 弹出指定的值,不存在则返回 v,无 v 则引发 KeyError
d.popitem() # 弹出最后添加的 (键, 值) 对,空字典将引发 KeyError
d.setdefault(k[, v]) # 获取指定的值,不存在则添加,v 默认为 none
d.update([t]) # 使用 t 更新 d 的内容。t 为字典或可迭代对象
dictview
由 dict.keys()
, dict.values()
和 dict.items()
所返回的对象是视图对象。该对象提供字典条目的一个动态视图,这意味着当字典改变时,视图也会相应改变。
“键”视图类似于集合,因为其条目不重复且可 Hash。如果所有值都是可 Hash 的,那么“条目”视图也会类似于集合。(“值”视图则不会被视为类似于集合,因为其条目通常都是有重复的。)
字典视图可以被迭代以产生相应的数据,并支持成员检测:
len(dictview) # 返回字典中的条目数
iter(dictview) # 返回字典中键、值、项的迭代器
x in dictview # 判断指定的键、值、项是否存在
reversed(dictview) # 返回一个逆序获取字典键、值、项的迭代器
函数
函数定义
# “默认值参数”必须在“非默认值参数”之后
def func(arg1, arg2='default'):
# 当函数内的第一条语句是字符串时,该字符串就是文档字符串。
'''函数文档字符串'''
return # 如果没有 return 语句,则函数返回 None
文档字符串中,第一行('''
所在行)之后的第一个非空行,其行首的空格会被删除,之后各行也会删除相同数量的空格,以保持与该行的相对位置不变。
默认值参数
参数的默认值只计算一次,如果默认值为可变对象,则其值会被保留。
def f(a, L=[]):
L.append(a)
return L
f(1) # [1]
f(2) # [1, 2]
f(3) # [1, 2, 3]
如果不想保留默认值的内容,应使用如下方式编写函数:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
关键字参数
调用函数时,可以使用 key=value
形式的“关键字参数”,但必须跟在“位置参数”的后面:
def f(a, b): return a+b
f(1, b=2) # 正确
f(a=1, 2) # 错误
可变参数
定义函数时,可以使用 *
指定可变的“位置参数”,可以使用 **
指定可变的“关键字参数”。*
必须位于普通参数之后,而 **
必须位于 *
之后。
def f(a, *b, **c): return a, b, c
f(1,2,3,4,5,c=1,d=2,e=3,f=4,g=5)
# (1, (2, 3, 4, 5), {'c': 1, 'd': 2, 'e': 3, 'f': 4, 'g': 5})
限定参数格式
定义函数时,可以使用单独的 /
和 *
限定参数的格式,/
之前的参数必须是“位置参数”,之后可以是“位置参数”,也可以是“关键字参数”,*
之后的参数必须是“关键字参数”。未使用 /
和 *
时,可以是“位置参数”,也可以是“关键字参数”:
def f(a, b, /, c, d, *, e, f): return a, b, c, d, e, f
# a, b 不能指定名称
# c, d 可以指定名称,也可以不指定名称(关键字参数必须在位置参数之后)
# e, f 必须指定名称
f(1, 2, 3, d=4, e=5, f=6) # (1, 2, 3, 4, 5, 6)
匿名函数
lambda
关键字用于创建小巧的匿名函数。在语法上,匿名函数只能是单个表达式。在语义上,它只是常规函数定义的语法糖。
f = lambda a, b: a+b
f(1, 2) # 3
闭包
闭包就是返回函数中的函数,被返回的函数会用到父函数中的局部变量,因此该局部变量的生命周期与被返回的函数一样长。其它代码无法访问该局部变量,只有闭包函数可以访问:
def f(): # 父函数
data = [0] # 闭包数据
def lam(): # 闭包函数
data[0] += 1 # 操作闭包数据
print(data[0]) # 打印闭包数据
return lam # 返回闭包函数(data 不会被释放)
a = f() # 获取闭包函数
for i in range(5): a() # 通过闭包函数操作“局部变量”
# 1 2 3 4 5
参数注释
函数参数可以添加注释,以便阅读者更容易理解参数的含义。注释可以是任意数据:
# 冒号后面是注释,可以是任意数据,= 后面是默认值,-> 后面是返回值的注释
def f(a:'参数 a 的注释', b:int=0, c:str='')->'返回值的注释':
return ''
作用域
Python 中作用域的层级:
内建作用域(builtins)
全局作用域(模块)
局部作用域(函数或类)
局部作用域(子函数或类)
局部作用域(子函数或类)
......
“内建作用域”就是系统内建函数存在的地方,无法修改。
“全局作用域”就是源代码的最顶层的范围,也就是模块范围。
“局部作用域”就是“函数”或“类”自身的范围。
自身范围内的对象可以直接访问,自身范围外的对象需要通过 nonlocal
访问,全局作用域中的对象需要通过 global
访问:
def f():
def a():
n = 1 # 局部,不修改外部同名变量
def b():
nonlocal n # 外部,在外部逐层查找(没有则出错)
n = 2
def c():
global n # 全局,在模块范围查找(没有则创建)
n = 3
n = 0 # f 中的 n
a() # f 中的 n 未被修改
print(n) # 0
b() # f 中的 n 被修改为 2
print(n) # 2
c() # f 中的 n 未被修改,但创建了一个全局变量 n = 3
print(n) # 2
f()
print(n) # 3(全局变量 n)
类
类定义
类定义必须被执行才会生效,否则该类不存在(还可以在 if 语句中创建类)。
class 类名:
'''类文档字符串'''
属性 = 值
def 方法():
return
类实例
类的特殊之处在于它可以被实例化(通过在类名后面添加一个括号的方式進行实例化):
class C: pass
print(C) # <class '__main__.C'>
c = C() # 创建类实例
print(c) # <__main__.C object at 0x7eff6c026b80>
属性
类本身也是对象,可以通过“类名”引用其中的属性和方法。
实例如果没有定义自己的属性和方法,会使用类的属性和方法。
#!python3
class C:
a = 1 # 类属性
# 实例被创建后,不会有自己的属性 a,用的是类的属性 a
c = C()
print(C.a) # 1
print(c.a) # 1
C.a = 2 # 修改类属性
print(C.a) # 2
print(c.a) # 2
# 实例随时可以添加自己的属性,这里添加了属性 a,不再使用类的属性 a
c.a = 3
print(c.a) # 3
# 类的属性 a 没有被改变
print(C.a) # 2
方法
当通过“实例”调用“类方法”时,会将“实例自身”作为第一个参数传递给“类方法”:
#!python3
class C:
def f(self): # 类方法(self 可以换成其它任意名称)
print(self)
c = C()
# 调用类方法
c.f() # <__main__.C object at 0x7fabee76b580>
# 相当于下面的写法
C.f(c) # <__main__.C object at 0x7fabee76b580>
# 获取实例方法的调用者
cf=c.f
print(cf.__self__) # <__main__.C object at 0x7fabee76b580>
其实“方法”本身也是“属性”,是一种可执行的属性。和普通属性一样,实例也可以定义自己的方法来覆盖类的方法,但自己定义的只是一个普通函数,不会将“实例自身”作为第一个参数传入:
#!python3
class C:
def f(self): # 类方法
print(self)
c = C()
# 添加自己的方法来覆盖类方法
c.f = lambda : print('Hello World!')
c.f() # Hello World!
# 类方法依然可用
C.f(c) # <__main__.C object at 0x7f8b687a8d00>
类初始化
类定义中也可以有其它语句,类中的所有语句都会在“执行类定义”时被执行:
#!python3
class C:
print('Hello World!') # 类中的语句
# 将输出 Hello World!
实例初始化
可以在类中定义一个 __init__()
方法来初始化新实例:
#!python3
class C:
a = 1
# 在创建新实例时,会自动调用该方法
def __init__(self, x):
# 添加实例自己的属性 a 并初始化,避免使用类的属性 a
self.a = x
c1 = C(2)
c2 = C(3)
print(C.a) # 1
print(c1.a) # 2
print(c2.a) # 3
继承
Python 支持多重继承,父类成员的查找会按照括号中指定的顺序進行。
class 类名(父类, 父类, 父类):
...
关系判断
可以通过 isinstance()
函数来判断对象和类之间的关系。
可以通过 issubclass()
函数来判断子类和父类之间的关系。
isinstance(1, int) # True
isinstance(True, int) # True
isinstance(1, bool) # False
issubclass(int, int) # True
issubclass(bool, int) # True
issubclass(int, bool) # False
私有变量
Python 中没有真正的私有成员,但是,大多数 Python 代码都遵循这样一个约定:以“至少一个下划线”开头的名称应当被视为 API 的非公有部分(无论它是函数、方法或是数据成员)。在通过 from module import *
的方式导入时,不会导入这些成员。
任何以“至少两个下划线”开头,并且以“最多一个下划线”结尾的“类成员”将被视为更正式的“私有成员”,它们会被自动添加_classname
前缀,其中 classname
是“去除了前缀下划线”的当前类名称。这种改写不考虑标识符的位置,只要它在类定义中就会进行:
#!python3
class Base:
__data = 1
class Sub(Base):
__data = 2 # 不会与父类的同名成员冲突,因为会被重命名
a = Sub()
print(a.__data) # 不存在
print(a._Base__data) # 1 # 这种访问方式一般在调试时使用
print(a._Sub__data) # 2 # 这种访问方式一般在调试时使用
请注意传递给 exec()
或 eval()
的代码不会将“发起调用的类”视作当前类;这类似于 global
语句的效果,因此这种效果仅限于同时经过字节码编译的代码。 同样的限制也适用于 getattr()
, setattr()
和 delattr()
,以及对于 __dict__
的直接引用。
#!python3
class Base:
__data = 1
print(__data) # 1
eval('print(_Base__data)') # 1
class Sub(Base):
__data = 2
print(__data) # 2
exec('print(_Sub__data)') # 2
print(Base.__dict__['_Base__data']) # 1
print(getattr(Base, '_Base__data')) # 1
迭代器
可迭代对象可以用在 for 语句中。在幕后,for
语句会在容器对象上调用 iter()
。该函数返回一个定义了 __next__()
方法的迭代器对象,此方法会逐一访问容器中的元素。当元素用尽时,__next__()
将引发 StopIteration
异常来通知 for
循环终止。
#!python3
for c in 'abc':
print(c) # a b c
可以手动调用 iter()
来创建迭代器,然后通过 next()
遍历迭代器元素。
#!python3
a = iter('123') # <str_iterator object at 0x7fe764bacfd0>
print(a)
print(next(a)) # 1
print(next(a)) # 2
print(next(a)) # 3
print(next(a)) # StopIteration
如果一个类具有 __iter__()
方法,并且该方法返回一个带有 __next__()
方法的对象,那么这个类就可以作为迭代器使用。
#!python3
class Reverse:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
a = Reverse((1,2,3,4,5))
print(a) # <__main__.Reverse object at 0x7f655ce3cfa0>
for i in a: print(i) # 5 4 3 2 1
生成器
生成器是一个用于创建迭代器的简单而强大的工具,它的写法类似于标准的函数,但它使用 yield
语句返回数据,调用含有 yield
语句的函数时,不会执行函数中的代码,而是将函数中的代码包装成一个生成器。每次在生成器上调用 next()
时,它会从上次离开的位置恢复执行(它会记住上次执行语句时的所有数据值)。
#!python3
def Reverse(data):
for i in range(len(data)-1, -1, -1):
yield data[i] # yield 语句专用于将函数包装成生成器
# 调用 Reverse() 会返回一个迭代器对象,而不是执行函数中的代码。
# 当第一次调用 next(a) 时,才会开始执行函数中的代码。执行到
# yield 语句时,会挂起函数,并返回结果,下次再执行 next(a) 时
# 会恢复函数,继续执行,直到再次遇到 yield 语句。
a = Reverse((1,2,3,4,5))
print(a) # <generator object Reverse at 0x7f892999eb30>
for i in a: print(i) # 5 4 3 2 1
生成器表达式
“生成器表达式”的语法与“列表推导式”的语法相似,但外层使用圆括号而非方括号。这种表达式被设计用于“生成器将被立即使用”的情况。生成器表达式相比等效的列表推导式更为节省内存。
print(sum(i for i in range(5))) # 10