菜鸟教程《Python 3 教程》笔记

菜鸟教程《Python 3 教程》笔记

0 写在前面

笔记带有个人侧重点,不追求面面俱到。

1 基本数据类型

出处: 菜鸟教程 - Python3 基本数据类型

Python 3 的六个标准数据类型中:

  • 不可变数据(3 个): Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个): List(列表)、Dictionary(字典)、Set(集合)。

此外还有一些高级的数据类型,如: 字节数组类型(bytes)。

1.1 Number(数字)

注意:Python3 中,bool 是 int 的子类,True 和 False 可以和数字相加, True==1False==0会返回 True,但可以通过 is 来判断类型。

>>> issubclass(bool, int) 
True
>>> True==1
True
>>> False==0
True
>>> True+1
2
>>> False+1
1
>>> 1 is True
False
>>> 0 is False
False

1.2 String(字符串)

Python 使用反斜杠 \转义特殊字符,如果你不想让反斜杠发生转义,可以在字符串前面添加一个 r,表示原始字符串:

>>> print('Ru\noob')
Ru
oob
>>> print(r'Ru\noob')
Ru\noob

1.3 bool(布尔类型)

  • 布尔类型可以和其他数据类型进行比较,比如数字、字符串等。在比较时,Python 会将 True 视为 1,False 视为 0。
  • 布尔类型也可以被转换成其他数据类型,比如整数、浮点数和字符串。在转换时,True 会被转换成 1,False 会被转换成 0。
a = True
b = False

# 比较运算符
print(2 < 3)   # True
print(2 == 3)  # False

# 逻辑运算符
print(a and b)  # False
print(a or b)   # True
print(not a)    # False

# 类型转换
print(int(a))   # 1
print(float(b)) # 0.0
print(str(a))   # "True"

1.4 List(列表)

list1 = [x for x in range(9)]

print(list1[-1::-2])  # [9]
print(list1[-1:-4])   # []

1.5 Tuple(元组)

虽然tuple的元素不可改变,但它可以包含可变的对象,比如list列表。
构造包含 0 个或 1 个元素的元组比较特殊,所以有一些额外的语法规则:

tup1 = ()    # 空元组
tup2 = (20,) # 一个元素,需要在元素后添加逗号

1.6 Set(集合)

注意: 创建一个空集合必须用 set() 而不是 {},因为 {} 是用来创建一个空字典。

# set可以进行集合运算
a = set('abracadabra')
b = set('alacazam')

print(a)
print(a - b)     # a 和 b 的差集
print(b - a)
print(a | b)     # a 和 b 的并集
print(a & b)     # a 和 b 的交集
print(a ^ b)     # a 和 b 中不同时存在的元素

{'d', 'c', 'r', 'a', 'b'}
{'r', 'd', 'b'}
{'m', 'z', 'l'}
{'m', 'd', 'c', 'r', 'a', 'z', 'b', 'l'}
{'a', 'c'}
{'m', 'r', 'd', 'z', 'b', 'l'}

1.7 Dictionary(字典)

键(key)必须使用不可变类型。在同一个字典中,键(key)必须是唯一的

构造函数 dict() 可以直接从键值对序列中构建字典如下:

>>> dict([('Runoob', 1), ('Google', 2), ('Taobao', 3)])
{'Runoob': 1, 'Google': 2, 'Taobao': 3}
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
>>> dict(Runoob=1, Google=2, Taobao=3)
{'Runoob': 1, 'Google': 2, 'Taobao': 3}

1.8 bytes 类型

什么是 bytes 类型:

  • 在 Python3 中,bytes 类型表示的是不可变的二进制序列(byte sequence)。
  • 与字符串类型不同的是,bytes 类型中的元素是整数值(0 到 255 之间的整数),而不是 Unicode 字符。

怎么创建 bytes 类型:

  1. 使用 b 前缀;
  2. 可以使用 bytes() 函数将其他类型的对象转换为 bytes 类型。
x = bytes("hello", encoding="utf-8")

注意: bytes 类型中的元素是整数值,因此在进行比较操作时需要使用相应的整数值。

x = b"hello"
if x[0] == ord("h"):
    print("The first element is 'h'")

2 数据类型转换

出处:菜鸟教程 - Python3 数据类型转换

Python 数据类型转换可以分为2种:

  • 隐式类型转换 - 自动完成;
  • 显式类型转换 - 需要使用类型函数来转换。

2.1 隐式类型转换

对两种不同类型的数据进行运算,较低数据类型(整数)就会转换为较高数据类型(浮点数)以避免数据丢失。

2.2 显式类型转换

Column 1Column 2
int(x [,base])将x转换为一个整数
float(x)将x转换到一个浮点数
complex(real [,imag])创建一个复数
str(x)将对象 x 转换为字符串
repr(x)将对象 x 转换为表达式字符串
eval(str)用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s)将序列 s 转换为一个元组
list(s)将序列 s 转换为一个列表
set(s)转换为可变集合
dict(d)创建一个字典。d 必须是一个 (key, value)元组序列
frozenset(s)转换为不可变集合
chr(x)将一个整数转换为一个字符
ord(x)将一个字符转换为它的整数值
hex(x)将一个整数转换为一个十六进制字符串
oct(x)将一个整数转换为一个八进制字符串

2.2.1 int() 函数

语法:

class int(x, base=10)

参数:

  • x – 字符串或数字。
  • base – 进制数,默认十进制。

返回值:
返回整型数据。

实例:

>>>int()               # 不传入参数时,得到结果0
0
>>> int(3)
3
>>> int(3.6)
3
>>> int('12', 16)        # 如果是带参数base的话,12要以字符串的形式进行输入,12 为 16进制
18
>>> int('0xa', 16)  
10  
>>> int('10', 8)  
8
int(float("2.3"))

2.2.2 repr() 函数

语法:

repr(object)

参数:

  • object – 对象。

返回值:
返回一个对象的 string 格式。

实例:

>>> s = 'RUNOOB'
>>> repr(s)
"'RUNOOB'"
>>> dict = {'runoob': 'runoob.com', 'google': 'google.com'};
>>> repr(dict)
"{'google': 'google.com', 'runoob': 'runoob.com'}"
>>>

str()和repr()的区别:

出处:python3编程基础:str()、repr()的区别

区别 1:字符串再转换为字符串

>>> repr('abd')  #repr转换后是在'abd'的外层又加了一层引号
"'abd'"
>>> str('abd')   #str转换后还是原来的值
'abd'
>>> str('abd') == 'abd'
True
>>> repr('abd') == 'abd'
False
>>> len(repr('abd'))  #repr转换后的字符串和str转换后的字符串个数都是不一样的
5
>>> len(str('abd'))
3

区别 2:命令行下print和直接输出的对比

>>> class A():
...     def __repr__(self):
...         return 'repr'
...     def __str__(self):
...         return 'str'
...
>>> a = A()
>>> a    #直接输出调用的是repr方法
repr
>>> print(a)    #print调用的是str方法
str

repr的使用场景:

>>> s = 'abdcf'
>>> eval('['+','.join([repr(i) for i in s])+']')
['a', 'b', 'd', 'c', 'f']
>>> eval('['+','.join([str(i) for i in s])+']')    #str报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'b' is not defined

2.2.3 frozenset ()函数

语法:

class frozenset([iterable])

参数:

  • iterable – 可迭代的对象,比如列表、字典、元组等等。

返回值:
返回新的 frozenset 对象,如果不提供任何参数,默认会生成空集合。

为什么需要冻结的集合(即不可变的集合)呢?因为在集合的关系中,有集合的中的元素是另一个集合的情况,但是普通集合(set)本身是可变的,那么它的实例就不能放在另一个集合中(set中的元素必须是不可变类型)。
所以,frozenset提供了不可变的集合的功能,当集合不可变时,它就满足了作为集合中的元素的要求,就可以放在另一个集合中了。

3 Python 3 运算符

出处:菜鸟教程 - Python3 运算符

Python 语言支持以下类型的运算符:

  • 算术运算符
  • 比较(关系)运算符
  • 赋值运算符
  • 逻辑运算符
  • 位运算符
  • 成员运算符
  • 身份运算符

3.1 赋值运算符

Python 3.8 新增了,海象运算符 := ,可在表达式内部为变量赋值,目的是避免重复调用,提高运行时间

示例:

if (n := len(a)) > 10:
    print(f"List is too long ({n} elements, expected <= 10)")

上述代码,避免2次调用 len(),也省去使用中间变量。

扩展阅读:
Python :=海象运算符最简单的解释
What’s New In Python 3.8

3.2 位运算符

#!/usr/bin/python3
 
a = 60            # 60 = 0011 1100 
b = 13            # 13 = 0000 1101 
c = 0
# 按位与运算符
c = a & b        # 12 = 0000 1100
print ("1 - c 的值为:", c)
# 按位或运算符
c = a | b        # 61 = 0011 1101 
print ("2 - c 的值为:", c)
# 按位异或运算符
c = a ^ b        # 49 = 0011 0001
print ("3 - c 的值为:", c)
# 按位取反运算符
c = ~a           # -61 = 1100 0011
print ("4 - c 的值为:", c)
# 左移动运算符
c = a << 2       # 240 = 1111 0000
print ("5 - c 的值为:", c)
# 右移动运算符
c = a >> 2       # 15 = 0000 1111
print ("6 - c 的值为:", c)

3.3 逻辑运算符

运算符逻辑表达式描述
andx and y布尔"与" - 如果 x 为 False,x and y 返回 x 的值,否则返回 y 的计算值。
orx or y布尔"或" - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。
notnot x布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。

实例:

print(10 and 20)  # 20
print(0 and 20)   # 0
print(10 or 20)   # 10
print(0 or 20)    # 20
print(0 or True)  # True
print(not 0)      # True
print(not True)   # False

3.4 身份运算符

is== 的区别:is 用于判断两个变量引用对象是否为同一个, == 用于判断引用变量的值(子对象)是否相等。

补充: is 判断两个对象是否为同一对象,是通过 id 来判断的;当两个基本类型数据(或元组)内容相同时,id 会相同,但并不代表 a 会随 b 的改变而改变。
== 判断两个对象的内容是否相同,是通过调用 __eq__() 来判断的。

实例:

>>>a = [1, 2, 3]
>>> b = a
>>> b is a 
True
>>> b == a
True
>>> b = a[:]
>>> b is a
False
>>> b == a
True

上述代码,b = aba 的引用,指向同一个内存地址;b = a[:]ba 的浅拷贝,共用相同的子对象。可以参考:Python中直接赋值、浅拷贝和深拷贝的区别

补充内容:

a = 100
b = 100
print(a is b)  # True
print(a == b)  # True

上述代码中,ab 指向了同一个内存地址。这是 Python 的特性,变量以内容为基准。这个特性决定了在 Python 中,数字类型的值是不可变的。当 a 的值改变时,是 a 指向了新的内存地址,而不是内存地址中的值改变了。

3.5 运算符优先级

以下表格列出了从最高到最低优先级的所有运算符, 相同单元格内的运算符具有相同优先级。 运算符均指二元运算,除非特别指出。 相同单元格内的运算符从左至右分组(除了幂运算是从右至左分组):

运算符描述
(expressions…), [expressions…], {key: value…}, {expressions…}圆括号的表达式
x[index], x[index: index], x(arguments…), x.attribute读取,切片,调用,属性引用
await xawait 表达式
**乘方(指数)
+x, -x, ~x正,负,按位非 NOT
*, @, /, //, %乘,矩阵乘,除,整除,取余
+, -加和减
<<, >>移位
&按位与 AND
^按位异或 XOR
|按位或 OR
in,not in, is,is not, <, <=, >, >=, !=, ==比较运算,包括成员检测和标识号检测
not x逻辑非 NOT
and逻辑与 AND
or逻辑或 OR
if – else条件表达式
lambdalambda 表达式
:=赋值表达式

注意: and 的优先级比 or 高。

4 数字

出处:菜鸟教程 - Python3 数字(Number)

数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间。

4.1 Python 数字运算

注意:// 得到的并不一定是整数类型的数,它与分母分子的数据类型有关系。

实例:

>>> 7//2
3
>>> 7.0//2
3.0
>>> 7//2.0
3.0

4.2 数字函数

4.2.1 abs() 函数

描述:

abs() 函数返回数字的绝对值。

语法:

abs(x)

参数:

  • x – 数值表达式,可以是整数,浮点数,复数

返回值:

函数返回 x(数字)的绝对值,如果参数是一个复数,则返回它的大小

>>>v = Vecter(3, 4)
>>>abs(v)
5.0

fabs() 与 abs() 的区别:

  1. abs() 是一个内置函数,而 fabs() 在 math 模块中定义的;
  2. fabs() 函数只适用于 float 和 integer 类型,而 abs() 也适用于复数;
  3. abs() 的返回值可以是整数也可以是浮点数,视输入而定,fabs() 的返回值总是浮点数。

4.2.2 ceil() 函数

描述:

ceil() 函数返回一个大于或等于 x 的的最小整数。(向上取整)

语法:

import math
math.ceil(x)

参数:

  • x – 数值表达式。

返回值:

返回一个大于或等于 x 的的最小整数。

4.2.3 cmp() 函数(Python 3 已废弃)

描述:

如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1。

注意: Python 3 已废弃,使用 (x>y)-(x<y) 替换。

4.2.4 floor() 函数

描述:

floor() 返回数字的下舍整数,小于或等于 x。(向下取整)

语法:

import math
math.floor(x)

参数:

  • x – 数值表达式。

返回值:

返回小于或等于 x 的整数。

4.2.5 max() 函数

描述:

max() 方法返回给定参数的最大值,参数可以为序列。

注意: 入参类型不能混入,要么全是数字,要么全是序列。入参是序列的话: 单序列入参,返回序列中最大的一个数值。多序列入参, 按索引顺序,逐一对比各序列的当前索引位的 “值”,直到遇见最大值立即停止对比,并返回最大值所在的序列

>>> max(0, True)
True
>>> max([1,2,3])
3
>>> max([2,4], [3,6])
[3, 6]
>>> max([2,4], [1,5])
[2, 4]
>>> max((1,-1,0), (True,False,2,0),(1, 0, 0, 2))
(True, False, 2, 0)
>>> max((1,-1,0), (True,),(1,))
(1, -1, 0)
>>> max([1,3,2],3,4) #非法入参
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'int' and 'list'
>>> max((1,2,3), [2,4,1]) #非法入参
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '>' not supported between instances of 'list' and 'tuple'

4.2.6 modf() 函数

描述:

modf() 方法返回 x 的整数部分与小数部分,两部分的数值符号与 x 相同,整数部分以浮点型表示

语法:

import math
math.modf(x)

参数:

  • x – 数值表达式。

返回值:

返回x的整数部分与小数部分。

实例:

>>> import math
>>> math.modf(3.2)
(0.20000000000000018, 3.0)
>>> math.modf(-0.01)
(-0.01, -0.0)
>>> type(math.modf(-3.2))
<class 'tuple'>

4.2.7 pow() 函数

描述:

pow() 方法返回 xy(x的y次方) 的值。

语法:

import math
math.pow(x, y)
pow(x, y[, z])

注意: pow() 通过内置的方法直接调用,内置方法会把参数作为整型,而 math 模块则会把参数转换为 float。

参数:

  • x – 数值表达式。
  • y – 数值表达式。
  • z – 数值表达式。

返回值:

返回 xy(x的y次方) 的值。

4.2.8 round() 函数

描述:

round() 方法返回浮点数 x 的四舍五入值,准确的说保留值将保留到离上一位更近的一端(四舍六入)。精度要求高的,不建议使用该函数。

语法:

round( x [, n]  )

参数:

  • x – 数字表达式。
  • n – 表示从小数点位数,其中 x 需要四舍五入,默认值为 0。

返回值:

返回浮点数x的四舍五入值。

注意: round() 保留值将保留到离上一位更近的一端(四舍六入)。如果距离两边一样远,会保留到偶数的一边。比如 round(0.5) 和 round(-0.5) 都会保留到 0,而 round(1.5) 会保留到 2。同时,受浮点数精度影响,结果不一定复合预期。

参考:python中关于round函数的小坑

4.3 随机数函数

4.3.1 choice() 函数

描述:

choice() 方法返回一个列表,元组或字符串的随机项。

语法:

import random
random.choice(seq)

参数:

  • seq – 可以是一个列表,元组或字符串。

返回值:

返回随机项。

实例:

random.choice(range(100))
random.choice([1, 2, 3, 5, 9])
random.choice('Runoob')

4.3.2 randrange() 函数

描述:

randrange() 方法返回指定递增基数集合中的一个随机数,基数默认值为1。

语法:

import random
random.randrange ([start,] stop [,step])

参数:

  • start – 指定范围内的开始值,包含在范围内;
  • stop – 指定范围内的结束值,不包含在范围内;
  • step – 指定递增基数。

返回值:

从给定的范围返回随机项。

实例:

random.randrange(1, 100, 2)
random.randrange(100)

4.3.3 shuffle() 函数

描述:

shuffle() 方法将序列的所有元素随机排序。

语法:

import random
random.shuffle(lst)

参数:

  • lst – 列表。

返回值:

返回 None。

4.3.4 uniform() 函数

描述:

uniform() 方法将随机生成下一个实数,它在 [x,y] 范围内。

语法:

import random
random.uniform(x, y)

参数:

  • x – 随机数的最小值,包含该值;
  • y – 随机数的最大值,包含该值。

返回值:

返回一个浮点数 N,取值范围为如果 x<y 则 x <= N <= y,如果 y<x 则y <= N <= x。

5 字符串

出处:菜鸟教程 - Python3 字符串

5.1 Python 转义字符

在需要在字符中使用特殊字符时,python 用反斜杠 \ 转义字符。如下表:

转义字符描述
\(在行尾时)续行符
\反斜杠符号
单引号
"双引号
\a响铃
\b退格(Backspace)
\000
\n换行
\v纵向制表符
\t横向制表符
\r回车,将 \r 后面的内容移到字符串开头,并逐一替换开头部分的字符,直至将 \r 后面的内容完全替换完成。
\f换页
\yyy八进制数,y 代表 0~7 的字符,例如:\012 代表换行。
\xyy十六进制数,以 \x 开头,y 代表的字符,例如:\x0a 代表换行。
\other其它的字符以普通格式输出

实例:

>>> print("line1 \
... line2 \
... line3")
line1 line2 line3
>>> print("\\")
\
>>> print('\'')
'
>>> print("\"")
"
>>> print("\a")
# 执行后电脑有响声。
>>> print("Hello \b World!")
Hello World!
>>> print("\000")

>>> print("\n")


>>> print("Hello \v World!")
Hello 
       World!
>>> print("Hello\rWorld!")
World!
>>> print('google runoob taobao\r123456')
123456 runoob taobao
>>> print("Hello \f World!")
Hello 
       World!
>>> print("\110\145\154\154\157\40\127\157\162\154\144\41")
Hello World!
>>> print("\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21")
Hello World!

一段有趣的代码:

import time

for i in range(101):
    print("\r{:3}%".format(i),end=' ')
    time.sleep(0.05)

5.2 Python 字符串格式化

在 Python 中,字符串格式化使用与 C 中 sprintf 函数一样的语法。

符号描述
%c格式化字符及其ASCII码
%s格式化字符串
%d格式化整数
%u格式化无符号整型
%0格式化无符号八进制数
%x格式化无符号十六进制数
%X格式化无符号十六进制数(大写)
%f格式化浮点数字,可指定小数点后的精度
%e用科学计数法格式化浮点数
%E作用同%e,用科学计数法格式化浮点数
%g%f和%e的简写
%G%f 和 %E 的简写
%p用十六进制数格式化变量的地址

格式化操作符辅助指令:

符号功能
*定义宽度或者小数点精度
-用做左对齐
+在正数前面显示加号( + )
<sp>在正数前面显示空格
#在八进制数前面显示零(‘0’),在十六进制前面显示’0x’或者’0X’(取决于用的是’x’还是’X’)
0显示的数字前面填充’0’而不是默认的空格
%‘%%‘输出一个单一的’%’
(var)映射变量(字典参数)
m.n.m 是显示的最小总宽度,n 是小数点后的位数(如果可用的话)

5.3 f-string

f-string 是 python3.6 之后版本添加的,称之为字面量格式化字符串,是新的格式化字符串的语法。

在 Python 3.8 的版本中可以使用 = 符号来拼接运算表达式与结果:

>>> x = 1
>>> print(f'{x+1}')   # Python 3.6
2

>>> x = 1
>>> print(f'{x+1=}')   # Python 3.8
x+1=2

5.4 Unicode 字符串

在Python2中,普通字符串是以8位ASCII码进行存储的,而Unicode字符串则存储为16位unicode字符串,这样能够表示更多的字符集。使用的语法是在字符串前面加上前缀 u。

在Python3中,所有的字符串都是Unicode字符串。

5.5 Python 的字符串方法

5.5.1 capitalize()

描述:

capitalize() 将字符串的第一个字母变成大写,其他字母变小写。

语法:

str.capitalize()

5.5.2 center()、ljust()、rjust()、zfill()

描述:

center() 方法返回一个指定的宽度 width 居中的字符串,fillchar 为填充的字符,默认为空格。
ljust() 方法返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串。
rjust() 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串。如果指定的长度小于字符串的长度则返回原字符串。
zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。zfill(width) 作用同 rjust(width, "0")

语法:

str.center(width[, fillchar])

参数:

  • width – 字符串的总宽度。
  • fillchar – 填充字符。

返回值:

返回一个指定的宽度 width 居中的字符串,如果 width 小于字符串宽度直接返回字符串,否则使用 fillchar 去填充。

实例:

str = "[runoob]"
print ("str.center(40, '*') : ", str.center(40, '*'))
print ("str.center(40, '*') : ", str.ljust(40, '*'))
print ("str.center(40, '*') : ", str.rjust(40, '*'))

str.center(40, '*') :  ****************[runoob]****************
str.center(40, '*') :  [runoob]********************************
str.center(40, '*') :  ********************************[runoob]

注意:

  1. width 小于字符串长度时,返回字符串,不会截断;
  2. fillchar 只能接收单个字符;
  3. 奇数个字符时,优先补充右边;偶数个字符时,优先补充左边。
>>> str = "[www.runoob.com]"
>>> print ("str.center(4, '*') : ", str.center(4, '*'))
str.center(4, '*') :  [www.runoob.com]         // width 小于字符串宽度

>>> str = "[www.runoob.com]"
>>> print ("str.center(40, '?!') : ", str.center(40, '?!'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: The fill character must be exactly one character long

>>> print('123'.center(4, '*'))     # 奇数个字符时优先向右边补*
123*
>>> print('1234'.center(5, '*'))    # 偶数个字符时优先向左边补*
*1234
>>> print('1234'.center(7, '*'))
**1234*

5.5.3 count()

描述:

count() 方法用于统计字符串里某个字符出现的次数。可选参数为在字符串搜索的开始与结束位置。

语法:

str.count(sub, start= 0,end=len(string))

参数:

  • sub – 搜索的子字符串;
  • start – 字符串开始搜索的位置。默认为第一个字符,第一个字符索引值为0;
  • end – 字符串中结束搜索的位置。字符中第一个字符的索引为 0。默认为字符串的最后一个位置。

返回值:

该方法返回子字符串在字符串中出现的次数。

实例:

str="www.runoob.com"
sub='o'
print ("str.count('o') : ", str.count(sub))
sub='run'
print ("str.count('run', 0, 10) : ", str.count(sub,0,10))
# 输出
str.count('o') :  3
str.count('run', 0, 10) :  1

注意: 已统计过的字符不重复统计。

>>> str1,str2,str3 = 'aaaa','aaaaa','aaaaaa'
>>> print(str2.count('aa'))
2
>>> print(str2.count('aa'))
2
>>> print(str3.count('aa'))
3

5.5.4 endswith()、startswith()

描述:

endswith() 方法用于判断字符串是否以指定后缀结尾,如果以指定后缀结尾返回 True,否则返回 False。可选参数 “start” 与 “end” 为检索字符串的开始与结束位置。

语法:

str.endswith(suffix[, start[, end]])

参数:

  • suffix – 该参数可以是一个字符串或者是一个元素;
  • start – 字符串中的开始位置;
  • end – 字符中结束位置。

返回值:

如果字符串含有指定的后缀返回 True,否则返回 False。

注意: 判断范围为 [start, end) 。

5.5.5 expandtabs()

描述:

expandtabs() 方法把字符串中的 tab 符号 \t 转为空格,tab 符号 \t 默认的空格数是 8,在第 0、8、16…等处给出制表符位置,如果当前位置到开始位置或上一个制表符位置的字符数不足 8 的倍数则以空格代替。

语法:

str.expandtabs(tabsize=8)

参数:

  • tabsize – 指定转换字符串中的 tab 符号 \t 转为空格的字符数。

返回值:

该方法返回字符串中的 tab 符号 \t 转为空格后生成的新字符串。

实例:

str = "this is\tstring example....wow!!!"    # \t 前面的字符串长度为 7

print("替换 \\t 符号: " + str.expandtabs(0))  # 删除\t
print("替换 \\t 符号: " + str.expandtabs(1))  # 补充1个
print("替换 \\t 符号: " + str.expandtabs(2))  # 补充1个
print("替换 \\t 符号: " + str.expandtabs(3))  # 补充2个
print("替换 \\t 符号: " + str.expandtabs(7))  # 补充7个
# 输出
替换 \t 符号: this isstring example....wow!!!
替换 \t 符号: this is string example....wow!!!
替换 \t 符号: this is string example....wow!!!
替换 \t 符号: this is  string example....wow!!!
替换 \t 符号: this is       string example....wow!!!

5.5.6 find()、rfind()、index()和rindex()

语法:

str.find(str, beg=0, end=len(string))
str.index(str, beg=0, end=len(string))

注意: 寻值范围为 [beg, end) 。

两者的区别:

未找到子字符串时,find() 返回 -1,index() 抛出异常。

5.5.7 isalnum() 等方法

方法描述TrueFalse
isalnum()如果字符串至少有一个字符并且所有字符都是字母或数字则返 回 True,否则返回 Falsea-z,A-Z,0-9,中文标点符号
isalpha()如果字符串至少有一个字符并且所有字符都是字母或中文字则返回 True, 否则返回 Falsea-z,A-Z,中文0-9,标点符号
isdigit()如果字符串只包含数字则返回 True 否则返回 False0-9,byte数字(单字节),全角数字(双字节)汉字数字,罗马数字
isnumeric()如果字符串中只包含数字字符,则返回 True,否则返回 FalseUnicode 数字,全角数字(双字节),汉字数字罗马数字
isdecimal()检查字符串是否只包含十进制字符,如果是返回 true,否则返回 false0-9,全角数字(双字节)罗马数字,汉字数字
isspace()如果字符串中只包含空白,则返回 True,否则返回 False空格、制表符(\t)、换行(\n)、回车(\r)
isupper()如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True,否则返回 FalseA-Z + 其他a-z + 其他
islower()如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 Falsea-z + 其他A-Z + 其他
istitle()如果字符串是标题化的(见 5.5.12 title())则返回 True,否则返回 False大写字母开头,后接小写字母或中文

实例:

num = "\u0030"  #unicode 0
num.isdigit()   # True
num.isdecimal() # True
num.isnumeric() # True

num = "\u00B2"  #unicode ²
num.isdigit()   # True
num.isdecimal() # False
num.isnumeric() # True

num = "\u00B2"  #unicode ½
num.isdigit()   # False
num.isdecimal() # False
num.isnumeric() # True

num = "1" # 全角
num.isdigit()   # True
num.isdecimal() # True
num.isnumeric() # True

num = b"1" # byte
num.isdigit()   # True
num.isdecimal() # AttributeError 'bytes' object has no attribute 'isdecimal'
num.isnumeric() # AttributeError 'bytes' object has no attribute 'isnumeric'

num = "IV" # 罗马数字
num.isdigit()   # False
num.isdecimal() # False
num.isnumeric() # False

num = "四" # 汉字
num.isdigit()   # False
num.isdecimal() # False
num.isnumeric() # True

5.5.8 maketrans()、translate()

描述:

maketrans() 方法用于创建字符映射的转换表,对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标。两个字符串的长度必须相同,为一一对应的关系。

注意: Python3.4 已经没有 string.maketrans() 了,取而代之的是内建函数: bytearray.maketrans()、bytes.maketrans()、str.maketrans() 。

语法:

string.maketrans(x[, y[, z]])

参数:

  • x – 必需,字符串中要替代的字符组成的字符串;
  • y – 可选,相应的映射字符的字符串;
  • z – 可选,要删除的字符。

返回值:

返回字符串转换后生成的新字符串。

实例:

# 一个参数,该参数必须为字典
>>> d = {'a':'1','b':'2','c':'3','d':'4','e':'5','s':'6'}
>>> trantab = str.maketrans(d)
>>> st='just do it'
>>> print(st.translate(trantab))
ju6t 4o it

# 两个参数 x 和 y,x、y 必须是长度相等的字符串,并且 x 中每个字符映射到 y 中相同位置的字符
>>> x = 'abcdefs'
>>> y = '1234567'
>>> st='just do it'
>>> trantab = str.maketrans(x,y)
>>> print(st.translate(trantab))
ju7t 4o it

# 三个参数 x、y、z,第三个参数 z 必须是字符串,先删除,再替换
>>> x = 'abcdefs'
>>> y='1234567'
>>> z='ot'
>>> st='just do it'
>>> trantab = str.maketrans(x,y,z)
>>> print(st.translate(trantab))
ju7 4 i

5.5.9 rstrip()、lstrip()、strip()

描述:

rstrip() 删除 string 字符串末尾的指定字符,默认为空白符,包括空格、换行符、回车符、制表符。

语法:

str.rstrip([chars])

注意: chars 是一个包含需要删除的字符的字符串。从 str 的末尾开始删除,直到末尾的字符不在 chars 中。

参数:

  • chars – 指定删除的字符(默认为空白符)

返回值:

返回删除 string 字符串末尾的指定字符后生成的新字符串。

实例:

>>> random_string = "this is good    "
# 字符串末尾的空格会被删除
>>> print(random_string.rstrip())
this is good
# 'si oo' 不是尾随字符,因此不会删除任何内容
>>> print(random_string.rstrip('si oo'))
this is good
# 在 'sid oo' 中 'd oo' 是尾随字符,'ood' 从字符串中删除
>>> print(random_string.rstrip('sid oo')) 
this is g
# 移除逗号(,)、点号(.)、字母 s、q 或 w,这几个都是尾随字符
>>> txt = "banana,,,,,ssqqqww....."
>>> print(txt.rstrip(",.qsw"))
banana

5.5.10 split()

描述:

split() 方法通过指定分隔符对字符串进行切片,该方法将字符串分割成子字符串并返回一个由这些子字符串组成的列表。

语法:

str.split(str="", num=string.count(str))

参数:

  • str – 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等;
  • num – 分割次数,如果设置了这个参数,则最多分割成 maxsplit+1 个子字符串。默认为 -1, 即分隔所有。

返回值:

返回分割后的字符串列表。

5.5.11 splitlines()

描述:

splitlines() 按照行(‘\r’, ‘\r\n’, \n’)分隔,返回一个包含各行作为元素的列表,如果参数 keepends 为 False,不包含换行符,如果为 True,则保留换行符。

语法:

str.splitlines([keepends])

参数:

  • keepends – 在输出结果里是否去掉换行符(‘\r’, ‘\r\n’, \n’),默认为 False,不包含换行符,如果为 True,则保留换行符。

返回值:

返回一个包含各行作为元素的列表。

实例:

>>> 'ab c\n\nde fg\rkl\r\n'.splitlines()
['ab c', '', 'de fg', 'kl']
>>> 'ab c\n\nde fg\rkl\r\n'.splitlines(True)
['ab c\n', '\n', 'de fg\r', 'kl\r\n']

5.5.12 title()

描述:

title() 方法返回"标题化"的字符串,就是说所有单词的首个字母转化为大写,其余字母均为小写。

注意: 单词是指以a-zA-Z之外符号分割的字母。例如:ab12cd中ab、cd是单词。

语法:

str.title()

参数:

  • NA

返回值:

返回"标题化"的字符串,就是说所有单词的首字母都转化为大写。

实例:

>>> txt = "hello b2b2b2 and 3g3g3g"
>>> print(txt.title())
Hello B2B2B2 And 3G3G3G

6 列表

出处: 菜鸟教程 - Python3 列表

6.1 删除列表元素

>>> list = ['Google', 'Runoob', 1997, 2000]
>>> print ("原始列表 : ", list)
原始列表 :  ['Google', 'Runoob', 1997, 2000]
>>> del list[2]
>>> print ("删除第三个元素 : ", list)
删除第三个元素 :  ['Google', 'Runoob', 2000]

6.2 列表函数和方法

6.2.1 max()、min()

注意:

  1. 使用 max()min() 时,列表中的元素需要是同一种类型,否则无法比较;
  2. 当列表的元素为 list[int] 整型列表时,优先比较整型列表第一个元素,如果相同会继续比较下去;
  3. 当列表的元素为 string 字符串时,会比较每个字符串元素的第一个字符的 ASCII 的大小。

实例:

>>> list1 = [[1, 4], [2, 3], [1, 5]]
>>> max(list1)
[2, 3]
>>> list1 = [[1, 4], [1, 3], [1, 5]]
>>> max(list1)
[1, 5]

>>> list1 = ['我最', '爱学习', 'python']
>>> max(list1)
'爱学习'
# ord('我') >>> 25105
# ord('爱') >>> 29233
# ord('p') >>> 112

6.2.2 reverse()

描述:

reverse() 函数用于反向列表中元素。

语法:

list.reverse()

6.2.3 sort()

描述:

sort() 函数用于对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数。

语法:

list.sort(key=None, reverse=False)

参数:

  • key – 一个函数,入参为可迭代对象的元素,根据函数的返回值继续排序;
  • reverse – 排序规则,reverse = True 降序, reverse = False 升序(默认)。

返回值:

该方法没有返回值,但是会对列表的对象进行排序。

实例:

>>> list1 = ["apple", "watermelon", "banana"]
>>> list1.sort(key = lambda ele: len(ele))
>>> list1
['apple', 'banana', 'watermelon']

7 元组

出处: 菜鸟教程 - Python3 元组

注意: tuple1 = (50,) 带 , 是元组,否则是整型。

8 字典

出处: 菜鸟教程 - Python3 字典

8.1 字典内置函数和方法

8.1.1 fromkeys()

描述:

fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值。

语法:

dict.fromkeys(seq[, value])

参数:

  • seq – 字典键值列表;
  • value – 可选参数, 设置键序列(seq)对应的值,默认为 None。

返回值:

该方法返回一个新字典。

实例:

>>> seq = ('name', 'age', 'sex')
>>> dict.fromkeys(seq)
{'age': None, 'name': None, 'sex': None}
>>> dict.fromkeys(seq, 10)
{'age': 10, 'name': 10, 'sex': 10}

8.1.2 get()、setdefault()

描述:

get() 函数返回指定键的值。
setdefault() 方法和 get()方法 类似, 如果键不存在于字典中,将会添加键并将值设为默认值。

语法:

dict.get(key[, value]) 
dict.setdefault(key, default=None)

参数:

  • key – 字典中要查找的键;
  • value – 可选,如果指定键的值不存在时,返回该默认值;
  • default – 键不存在时,设置的默认键值。

返回值:

返回指定键的值,如果键不在字典中返回默认值,如果不指定默认值,则返回 None。
如果 key 在 字典中,返回对应的值。如果不在字典中,则插入 key 及设置的默认值 default,并返回 default ,default 默认值为 None。

8.1.3 popitem()

描述:

popitem() 方法随机返回并删除字典中的最后一对键和值。如果字典已经为空,却调用了此方法,就报出 KeyError 异常。

9 集合

出处: 菜鸟教程 - Python3 集合

集合是一组无序排列的可哈希的值。

9.1 集合的基本操作

# 集合间的运算
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  
{'a', 'r', 'b', 'c', 'd'}
>>> a - b  # 集合a中包含而集合b中不包含的元素
{'r', 'd', 'b'}
>>> a | b  # 集合a或b中包含的所有元素
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b  # 集合a和b中都包含了的元素
{'a', 'c'}
>>> a ^ b  # 不同时包含于a和b的元素
{'r', 'd', 'b', 'm', 'z', 'l'}

9.2 集合内置方法

9.2.1 add()、update()

add() 只接受可哈希的值,即不可变数据,数、字符串、元组。
update 可以接受数、字符串、元组、列表、集合。

9.2.2 pop()、remove()、discard()

pop 不具名地移除集合中的元素;
remove 移除集合中的指定元素,如果元素不存在,则会发生错误 KeyError
discard 移除集合中的指定元素,如果元素不存在也不会报错。

9.2.3 difference()、differece_update()等

>>> x = {"apple", "banana", "cherry"}
>>> y = {"google", "microsoft", "apple"}
>>> x.difference(y)
{'cherry', 'banana'}                         # 返回值 x - y
>>> x.intersection(y)
{'apple'}                                    # 返回值 x & y
>>> x.symmetric_difference(y)
{'microsoft', 'google', 'cherry', 'banana'}  # 返回值 x ^ y
>>> x.union(y)                               # 返回值 a | b
{'microsoft', 'google', 'cherry', 'banana', 'apple'}


>>> x.difference_update(y)                   # 没有返回值
>>> x
{'banana', 'cherry'}
>>> x.add('apple')
>>> x.intersection_update(y)                 # 没有返回值
>>> x
{'apple'}
>>> x.add({'banana', 'cherry'})
>>> x.symmetric_difference_update(y)         # 没有返回值
>>> x
{'microsoft', 'google', 'cherry', 'banana'}
>>> x.update({apple})                        # 没有返回值
>>> x
{'microsoft', 'google', 'cherry', 'banana', 'apple'}

9.2.4 isdisjoint()

描述:

isdisjoint() 方法用于判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False。即两者是否有交集。

9.2.5 issubset()、issuperset()

issubset() 方法用于判断集合的所有元素是否都包含在指定集合中,如果是则返回 True,否则返回 False。即:A ⊆ B。
issuperset() 方法用于判断指定集合的所有元素是否都包含在原始的集合中,如果是则返回 True,否则返回 False。即:A ⊇ B。

10 条件控制

出处: 菜鸟教程 - Python3 条件控制

10.1 match…case

Python 3.10 增加了 match...case 的条件判断。match 后的对象会依次与 case 后的内容进行匹配,如果匹配成功,则执行匹配到的表达式,否则直接跳过,_ 可以匹配一切。

语法:

match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>

10.1.1 基本模式匹配

x = 10
match x:
    case 10:
        print("x is 10")
    case 20:
        print("x is 20")
    case _:
        print("x is something else")

10.1.2 序列模式匹配

point = (2, 3)
match point:
    case (0, 0):
        print("Origin")
    case (0, y):
        print(f"Point is on the Y axis at {y}")
    case (x, 0):
        print(f"Point is on the X axis at {x}")
    case (x, y):
        print(f"Point is at ({x}, {y})")
    case _:
        print("Not a point")

10.1.3 对象模式匹配

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(0, 3)
match p:
    case Point(x=0, y=y):
        print(f"Point is on the Y axis at {y}")
    case Point(x=x, y=0):
        print(f"Point is on the X axis at {x}")
    case Point(x, y):
        print(f"Point is at ({x}, {y})")
    case _:
        print("Not a point")

10.1.4 OR 模式

x = 2
match x:
    case 1 | 2 | 3:
        print("x is 1, 2, or 3")
    case _:
        print("x is something else")

10.1.5 守卫

x = 10
match x:
    case x if x > 5:
        print("x is greater than 5")
    case _:
        print("x is 5 or less")

扩展阅读:Python 3.10里面的Match-Case语法详解

11 循环语句

出处: 菜鸟教程 - Python3 循环语句

11.1 while 循环

11.1.1 while 循环使用 else 语句

expr 条件语句为 true 则执行 statement(s) 语句块,如果为 false,则执行 additional_statement(s)。

语法:

while <expr>:
    <statement(s)>
else:
    <additional_statement(s)>

11.2 for 语句

11.2.1 for…else

当循环执行完毕(即遍历完 iterable 中的所有元素)后,会执行 else 子句中的代码,如果在循环过程中遇到了 break 语句,则会中断循环,此时不会执行 else 子句。

for <variable> in <sequence>:
    <statements>
else:
    <statements>

11.3 break 和 continue 语句及循环中的 else 子句

break 语句可以跳出 for 和 while 的循环体。如果你从 for 或 while 循环中终止,任何对应的循环 else 块将不执行。

continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句,然后继续进行下一轮循环。

12 推导式

出处: 菜鸟教程 - Python3 推导式

12.1 列表推导式

"""
格式 1:简单生成列表
[<expression> for <item> in <iterable>]
"""
>>> [i**2 for i in range(5)]
[0, 1, 4, 9, 16]

"""
格式 2:条件生成列表
[<expression> for <item> in <iterable> if <condition>]
"""
>>> [i for i in range(5) if i % 2 == 0]
[0, 2, 4]

"""
格式 3:条件生成列表
[<expression1> if <condition> else <expression2> for <item> in <iterable>]
"""
>>> list1 = [1, 2, 3, 4, 5]
>>> [1 if i > 3 else 0 for i in list1]
[0, 0, 0, 1, 1]

"""
格式 4:多条件生成列表
[<expression1> if <condition1> else <expression2> if <condition2> else <expression3> for <item> in <iterable>]
"""

"""
格式 5:多重循环
[<expression> for <item1> in <iterable1> for <item2> in <iterable2>]

[<expression> for <item1> in <iterable1> for <item2> in <item1>]
"""
>>> [i * j for i in range(5) for j in "ab"]
['', '', 'a', 'b', 'aa', 'bb', 'aaa', 'bbb', 'aaaa', 'bbbb']

>>> list1 = [[1, 2], [3, 4]]
>>> [j for item in list1 for j in item]
[1, 2, 3, 4]

"""
格式 6:嵌套列表
[[<expression> for <item2> in <iterable2>] for <item1> in <iterable1>]
"""
>>> [[i * j for j in "ab"] for i in range(3)]
[['', ''], ['a', 'b'], ['aa', 'bb']]

扩展阅读:Python 中的列表推导式

12.2 字典推导式

"""
格式 1:简单生成
{<key_expr>: <value_expr> for <item> in <iterable>}
"""
>>> listdemo = ['Google','Runoob', 'Taobao']
>>> {key: len(key) for key in listdemo}
{'Google': 6, 'Runoob': 6, 'Taobao': 6}

"""
格式 2:条件生成
{<key_expr>: <value_expr> for <item> in <iterable> if <condition>}
"""
>>> {i: i**2 for i in range(5) if i % 2 == 0}
{0: 0, 2: 4, 4: 16}

12.3 集合推导式

参考 12.1 列表推导式,将 [] 替换为 {}

12.4 元组推导式(生成器表达式)

注意: 元组推导式返回的结果是一个生成器对象。

>>> a = (x for x in range(1,10))
>>> a
<generator object <genexpr> at 0x7faf6ee20a50>  # 返回的是生成器对象

>>> tuple(a)       # 使用 tuple() 函数,可以直接将生成器对象转换成元组
(1, 2, 3, 4, 5, 6, 7, 8, 9)

13 迭代器与生成器

出处: 菜鸟教程 - Python3 迭代器与生成器

13.1 迭代器

迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退

迭代器有两个基本的方法:iter()next()

字符串,列表或元组对象都可用于创建迭代器:

>>> list = [1, 2, 3, 4]
>>> it = iter(list)    # 创建迭代器对象
>>> print (next(it))   # 输出迭代器的下一个元素
1
>>> print (next(it))
2

13.1.1 创建一个迭代器

把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()__next__()

  • __iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。

  • __next__() 方法会返回下一个迭代器对象。

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    x = self.a
    self.a += 1
    return x
 
myclass = MyNumbers()
myiter = iter(myclass)
 
print(next(myiter))  # 1
print(next(myiter))  # 2

print(type(myclass))  # <class '__main__.MyNumbers'>
print(type(myiter))   # <class '__main__.MyNumbers'>
print(myclass is myiter)  # True
print(next(myclass))  # 1
print(next(myclass))  # 2

13.1.2 StopIteration

StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration

13.2 生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

def countdown(n):
    while n > 0:
        yield n
        n -= 1
 
# 创建生成器对象
generator = countdown(5)
 
# 通过迭代生成器获取值
print(next(generator))  # 输出: 5
print(next(generator))  # 输出: 4
print(next(generator))  # 输出: 3
 
# 使用 for 循环迭代生成器
for value in generator:
    print(value)  # 输出: 2 1

13.3 yield 使用浅析

出处: 菜鸟教程 - Python yield 使用浅析

13.3.1 通过 iterable 对象来迭代

在 python 2 中,range() 为 list 对象,会生成 list,xrange() 为 iterable 对象。在 python 3 中,range() 和 xrange() 合并了。

13.3.2 使用 isgeneratorfunction 判断

可以利用 isgeneratorfunction 判断一个函数是否是一个特殊的 generator 函数。

def fab(max): 
    n, a, b = 0, 0, 1 
    while n < max: 
        yield b      # 使用 yield
        a, b = b, a + b 
        n = n + 1

from inspect import isgeneratorfunction 
print(isgeneratorfunction(fab))  # True

13.3.3 类的定义和类的实例

fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别

import types 
print(isinstance(fab, types.GeneratorType))  # False
print(isinstance(fab(5), types.GeneratorType))  # True

fab 是无法迭代的,而 fab(5) 是可迭代的

from collections import Iterable 
print(isinstance(fab, Iterable))  # False 
print(isinstance(fab(5), Iterable))  # True

13.3.4 return 的作用

在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

13.3.5 另一个 yield 的例子

另一个 yield 的例子来源于文件读取。如果直接对文件对象调用 read() 方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。

def read_file(fpath): 
    BLOCK_SIZE = 1024 
    with open(fpath, 'rb') as f: 
        while True: 
            block = f.read(BLOCK_SIZE) 
            if block: 
                yield block 
            else: 
                return

14 函数

出处: 菜鸟教程 - Python3 函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。

14.1 参数传递

在 python 中,类型属于对象,对象有不同类型的区分,变量是没有类型的。她仅仅是一个对象的引用(一个指针),可以是指向 List 类型对象,也可以是指向 String 类型对象。

14.1.1 可更改(mutable)与不可更改(immutable)对象

在 python 中,strings、tuples 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。

  • 不可变类型: 变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变 a 的值,相当于新生成了 a。
  • 可变类型: 变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

python 函数的参数传递:

  • 不可变类型: 类似 C++ 的值传递,如整数、字符串、元组。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。
  • 可变类型: 类似 C++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响。

python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

14.1.2 python 传不可变对象实例

def change(a):
    print(id(a))   # 指向的是同一个对象
    a=10
    print(id(a))   # 一个新对象
 
a=1
print(id(a))
change(a)

4379369136
4379369136
4379369424

可以看见在调用函数前后,形参和实参指向的是同一个对象(对象 id 相同),在函数内部修改形参后,形参指向的是不同的 id。

14.2 参数

以下是调用函数时可使用的正式参数类型:

  • 必需参数
  • 关键字参数
  • 默认参数
  • 不定长参数

14.2.1 必需参数

必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样,不然会出现语法错误。

14.2.2 关键字参数

使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

14.2.3 默认参数

调用函数时,如果没有传递参数,则会使用默认参数。默认参数必须放在最后,否则会报错。

14.2.4 不定长参数

语法 1:

def functionname([formal_args,] *var_args_tuple ):
   "函数_文档字符串"
   function_suite
   return [expression]

加了星号 * 的参数会以==元组(tuple)==的形式导入,存放所有未命名的变量参数。

# 可写函数说明
def printinfo( arg1, *vartuple ):
   "打印任何传入的参数"
   print ("输出: ")
   print (arg1)
   print (vartuple)
 
# 调用printinfo 函数
printinfo(10)
printinfo(70, 60, 50)

# 输出: 
10
70
(60, 50)

语法 2:

def functionname([formal_args,] **var_args_dict ):
   "函数_文档字符串"
   function_suite
   return [expression]

加了两个星号 ** 的参数会以字典的形式导入。

#!/usr/bin/python3
  
# 可写函数说明
def printinfo( arg1, **vardict ):
   "打印任何传入的参数"
   print ("输出: ")
   print (arg1)
   print (vardict)
 
# 调用printinfo 函数
printinfo(1, a=2,b=3)

# 输出: 
1
{'a': 2, 'b': 3}

语法 3:

声明函数时,参数中星号 * 可以单独出现.如果单独出现星号 *,则星号 * 后的参数必须用关键字传入。

>>> def f(a,b,*,c):
...     return a+b+c
... 
>>> f(1,2,3)   # 报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were given
>>> f(1,2,c=3) # 正常
6

14.3 匿名函数

Python 使用 lambda 来创建匿名函数。

  • lambda 的主体是一个表达式,而不是一个代码块;
  • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数;
  • 虽然 lambda 函数看起来只能写一行,却不等同于 C 或 C++ 的内联函数,内联函数的目的是调用小函数时不占用栈内存从而减少函数调用的开销,提高代码的执行速度。

语法:

lambda [arg1 [,arg2,.....argn]]:expression

可以将匿名函数封装在一个函数内,这样可以使用同样的代码来创建多个匿名函数:

def myfunc(n):
  return lambda a : a * n
 
mydoubler = myfunc(2)
mytripler = myfunc(3)
 
print(mydoubler(11))  # 22
print(mytripler(11))  # 33

lambda 匿名函数也是可以使用"关键字参数"进行参数传递,也可以设定默认值。

14.4 强制位置参数

Python3.8 新增了一个函数形参语法 / 用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。

在以下的例子中,形参 a 和 b 必须使用指定位置参数,c 或 d 可以是位置形参或关键字形参,而 e 和 f 要求为关键字形参:

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

# 正确的调用
f(10, 20, 30, d=40, e=50, f=60)
# 错误的调用
f(10, b=20, c=30, d=40, e=50, f=60)   # b 不能使用关键字参数的形式
f(10, 20, 30, 40, 50, f=60)           # e 必须使用关键字参数的形式

15 数据结构

出处: 菜鸟教程 - Python3 数据结构

15.1 将列表当作队列使用

在列表的最后添加或者弹出元素速度快,然而在列表里插入或者从头部弹出速度却不快(因为所有其他的元素都得一个一个地移动)。

15.2 遍历技巧

遍历字典:

>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

遍历列表:

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

遍历多个序列:

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

反向遍历:

>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

16 模块(具体不懂)

出处: 菜鸟教程 - Python3 模块

模块 是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。

16.1 import 语句

不管执行了多少次 import,一个模块只会被导入一次。

使用 import 语句的时候,Python 解释器会从搜索路径中依次寻找所引入的模块。搜索路径可以通过 sys 模块中的 path 变量进行查看。

>>> import sys
>>> sys.path
['', '/usr/lib/python3.4', '/usr/lib/python3.4/plat-x86_64-linux-gnu', '/usr/lib/python3.4/lib-dynload', '/usr/local/lib/python3.4/dist-packages', '/usr/lib/python3/dist-packages']
>>> 

sys.path 输出是一个列表,其中第一项是空串 ‘’,代表当前目录(若是从一个脚本中打印出来的话,可以更清楚地看出是哪个目录),亦即我们执行python解释器的目录(对于脚本的话就是运行的脚本所在的目录)。

不要使用 from package import *
推荐使用 import packagefrom package import item

16.2 深入模块

模块除了方法定义,还可以包括可执行的代码。这些代码一般用来初始化这个模块。这些代码只有在第一次被导入时才会被执行。

每个模块有各自独立的符号表,在模块内部为所有的函数当作全局符号表来使用。所以,模块的作者可以放心大胆的在模块内部使用这些全局变量,而不用担心把其他用户的全局变量搞混。从另一个方面,当你确实知道你在做什么的话,你也可以通过 modname.itemname 这样的表示法来访问模块内的函数。

16.3 __name__属性

一个模块被另一个程序第一次引入时,其主程序将运行。 如果我们想在模块被引入时,模块中的某一程序块不执行,可以用 __name__ 属性来使该程序块仅在该模块自身运行时执行。每个模块都有一个 __name__ 属性,当其值是 __main__ 时,表明该模块自身在运行,否则是被引入。

#!/usr/bin/python3
# Filename: using_name.py

if __name__ == '__main__':
   print('程序自身在运行')
else:
   print('我来自另一模块')

输出:

>>> python using_name.py
程序自身在运行
>>> import using_name
我来自另一模块

16.4 dir() 函数

内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回。

>>> a = 1
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a']
>>> import sys
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'sys']
>>> dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', 
'__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__', 
'_base_executable', '_clear_type_cache', '_current_exceptions', '_current_frames', '_deactivate_opcache', 
'_debugmallocstats', '_enablelegacywindowsfsencoding', '_framework', '_getframe', '_git', '_home', '_xoptions', 
'addaudithook', 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix', 'breakpointhook', 
'builtin_module_names', 'byteorder', 'call_tracing', 'copyright', 'displayhook', 'dllhandle', 
'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 
'float_repr_style', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth', 'get_int_max_str_digits', 
'getallocatedblocks', 'getdefaultencoding', 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile', 
'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 
'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'maxsize', 'maxunicode', 
'meta_path', 'modules', 'orig_argv', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'platlibdir', 
'prefix', 'ps1', 'ps2', 'pycache_prefix', 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 
'set_int_max_str_digits', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin',
 'stdlib_module_names', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info', 'warnoptions', 'winver']

16.5 包

是一种管理 Python 模块命名空间的形式,采用 “点模块名称”。比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。

这里给出了一种可能的包结构(在分层的文件系统中):

sound/                          顶层包
      __init__.py               初始化 sound 包
      formats/                  文件格式转换子包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  声音效果子包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  filters 子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。

目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包,主要是为了避免一些滥俗的名字(比如叫做 string)不小心的影响搜索路径中的有效模块。

# 导入方式
import sound.effects.echo
# 调用方式
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
# 导入方式
import sound.effects import echo
# 调用方式
echo.echofilter(input, output, delay=0.7, atten=4)
# 导入方式
from sound.effects.echo import echofilter
# 调用方式
echofilter(input, output, delay=0.7, atten=4)

当使用 from package import item 这种形式的时候,import 语法会首先把 item 当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,抛出一个 :exc:ImportError 异常。

反之,如果使用形如 import item.subitem.subsubitem 这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字

16.6 从一个包中导入*

如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。

__all__ = ["echo", "surround", "reverse"]

如果 __all__ 真的没有定义,那么使用 from sound.effects import * 这种语法的时候,就不会导入包 sound.effects 里的任何子模块。他只是把包 sound.effects 和它里面定义的所有内容导入进来(可能运行 __init__.py 里定义的初始化代码)。

扩展阅读: CSDN Python 入门技能树-预备知识-模块管理

17 输入和输出

出处: 菜鸟教程 - Python3 输入和输出

17.1 读取键盘输入

Python3.x 中 input() 函数接受一个标准输入数据,返回为 string 类型。

语法:

input([prompt])

17.2 读和写文件

语法:

open(filename, mode)

参数:

  • filename:要访问的文件名称的字符串值。
  • mode:决定了打开文件的模式:只读,写入,追加等,默认文件访问模式为只读®。

模式选取:

模式描述
r以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
rb以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。
r+打开一个文件用于读写。文件指针将会放在文件的开头。
rb+以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
w打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
w+打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
wb+以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。
a打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
ab以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
a+打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
ab+以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。
模式rr+ww+aa+
++++
+++++
创建++++
覆盖++
指针在开始++++
指针在结尾++

实例:

with open("/tmp/foo.txt", "r") as f:
    read_data = f.read

当处理一个文件对象时, 使用 with 关键字是非常好的方式。在结束后, 它会帮你正确的关闭文件。

17.3 文件对象的方法

17.3.1 read()、readline()、readlines()

描述:

read() 方法用于从文件读取指定的字符数(文本模式 t)或字节数(二进制模式 b),如果未给定参数 size 或 size 为负数则读取文件所有内容。
readline() 方法用于从文件读取整行,包括 “\n” 字符。如果指定了一个非负数的参数,则返回指定大小的字节数,包括 “\n” 字符。
readlines() 方法用于读取所有行(直到结束符 EOF)并返回列表,该列表可以由 Python 的 for… in … 结构进行处理。 如果碰到结束符 EOF 则返回空字符串。

语法:

fileObject.read([size])
fileObject.readline([size]); 
fileObject.readlines();

返回值:

返回从字符串中读取的字节。

实例:

# runoob.txt
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com

>>> f = open("runoob.txt", "r+")
>>> f.read(10)
'1:www.runo'
>>> f.readline()
'ob.com\n'
>>> f.readline(5)
'2:www'
>>> f.close()

>>> f = open("runoob.txt", "r+")
>>> for line in f.readlines():
...     print(line.strip())
...
1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com

17.3.2 tell()

描述:

tell() 方法返回文件的当前位置,即文件指针当前位置。

语法:

fileObject.tell()

返回值:

返回文件的当前位置。

17.3.3 seek()

描述:

seek() 方法用于移动文件读取指针到指定位置。

语法:

f.seek(offset, from_what)

参数:

  • offset:开始的偏移量,也就是代表需要移动偏移的字节数,如果是负数表示从倒数第几位开始。
  • from_what:可选,默认值为 0。给 offset 定义一个参数,表示要从哪个位置开始偏移;0 代表从文件开头开始算起,1 代表从当前位置开始算起,2 代表从文件末尾算起。

举例:

  • seek(x,0) :从起始位置即文件首行首字符开始移动 x 个字符
  • seek(x,1) :表示从当前位置往后移动x个字符
  • seek(-x,2) :表示从文件的结尾往前移动x个字符

返回值:

如果操作成功,则返回新的文件位置,如果操作失败,则函数返回 -1。

实例:

>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5)      # 移动到文件的第六个字节
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2)  # 移动到文件倒数第三个字节
13
>>> f.read(1)
b'd'

17.4 pickle 模块(没看懂)

python 的 pickle 模块实现了基本的数据序列和反序列化。通过 pickle 模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储。通过 pickle 模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。

语法:

pickle.dump(obj, file, [,protocol])

实例:

# 使用pickle模块将数据对象保存到文件
output = open('data.pkl', 'wb')
pickle.dump(data1, output)

#使用pickle模块从文件中重构python对象
pkl_file = open('data.pkl', 'rb')
data1 = pickle.load(pkl_file)

18 File(文件)方法

出处: 菜鸟教程 - Python3 File(文件) 方法

部分内容可以参考上一章节

18.1 open() 方法

open() 方法用于打开一个文件,并返回文件对象。在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError

使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。

语法:

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

参数:

  • file: 必需,文件路径(相对或者绝对路径);
  • mode: 可选,文件打开模式;
  • buffering: 设置缓冲;
  • encoding: 一般使用utf8;
  • errors: 报错级别;
  • newline: 区分换行符;
  • closefd: 传入的file参数类型;
  • opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。

18.2 file 对象

18.2.1 flush()

描述:

flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要被动的等待输出缓冲区写入。

一般情况下,文件关闭后会自动刷新缓冲区,但有时你需要在关闭前刷新它,这时就可以使用 flush() 方法。

实例:

f = open("runoob.txt", "wb")
f.flush()
f.close()

18.2.2 fileno()

描述:

fileno() 方法返回一个整型的文件描述符(file descriptor FD 整型),可用于底层操作系统的 I/O 操作。

返回值:

返回文件描述符。

实例:

>>> f = open("runoob.txt", "wb")
>>> f.fileno()
3
>>> f.close()

18.2.3 isatty()

描述:

isatty() 方法检测文件是否连接到一个终端设备,如果是返回 True,否则返回 False。

返回值:

如果连接到一个终端设备返回 True,否则返回 False。

实例:

>>> f = open("runoob.txt", "wb")
>>> f.isatty()
False
>>> f.close()

18.2.4 truncate()

描述:

truncate() 方法用于从文件的首行首字节开始截断,截断文件为 size 个字节,无 size 表示从当前位置截断至末尾;截断之后,后面的所有字节被删除,其中,Widnows 系统下的换行代表2个字节大小。

语法:

fileObject.truncate([size])

实例:

文件 runoob.txt 的内容:

1:www.runoob.com
2:www.runoob.com
3:www.runoob.com
4:www.runoob.com
5:www.runoob.com

操作:

# python 3.10.8
>>> f = open("runoob.txt", "r+")
>>> f.truncate(30)
30
>>> f.read()
'1:www.runoob.com\n2:www.runoob'
>>> f.close()

处理后,文件 runoob.txt 的内容:

1:www.runoob.com
2:www.runoob

18.2.5 write()

描述:

write() 方法用于向文件中写入指定字符串。

在文件关闭前或缓冲区刷新前,字符串内容存储在缓冲区中,这时在文件中是看不到写入的内容的。

如果文件打开模式带 b,那写入文件内容时,str (参数)要用 encode 方法转为 bytes 形式,否则报错:TypeError: a bytes-like object is required, not 'str'

语法:

fileObject.write(str)

返回值:

返回的是写入的字符长度。

实例:

>>> f = open("runoob.txt", "r+")
>>> f.write("runoob")
6
>>> f.flush()
>>> f.close()

18.2.6 writelines()

描述:

writelines() 方法用于向文件中写入一序列的字符串。这一序列字符串可以是由迭代对象产生的,如一个字符串列表。换行需要制定换行符 \n

语法:

fileObject.writelines(seq)

参数:

  • seq – 要写入文件的字符串序列。

实例:

>>> f = open("runoob.txt", "w")
>>> f.writelines(["1","2","3\n","4"])
>>> f.close()

处理后,文件 runoob.txt 的内容:

123
4

19 错误和异常

出处: 菜鸟教程 - Python3 错误和异常

Python 有两种错误很容易辨认:语法错误异常

19.1 assert(断言)

assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况,

语法格式:

assert expression [, arguments]

等效于:

if not expression:
    raise AssertionError(arguments)

实例:

>>> assert 1==2    # 条件为 false 触发异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

>>> assert 1==2, '1 不等于 2'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError: 1 不等于 2

19.2 异常处理

个人理解: 进行异常处理是为了避免直接抛出异常而导致暴露,通过接收不同的异常可以进行针对性地处理(或者做出提示)。

异常处理
图片出处:菜鸟教程 - Python3 错误和异常

19.2.1 try/except

可以使用 try/except 语句异常捕捉。

try 语句执行逻辑:

  1. 执行 try 子句;
  2. 如果没有异常发生,忽略 except 子句,try 子句执行后结束,如果在执行 try 子句的过程中发生了异常,那么 try 子句余下的部分将被忽略;
  3. 如果异常的类型和 except 之后的名称相符,那么对应的 except 子句将被执行,如果一个异常没有与任何的 except 匹配,那么这个异常将会传递给上层的 try 中

一个 try 语句可能包含多个 except 子句,分别来处理不同的特定的异常,最多只有一个分支会被执行

处理程序将只针对对应的 try 子句中的异常进行处理,而不是其他的 try 的处理程序中的异常。

一个 except 子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组,例如:

except (RuntimeError, TypeError, NameError):
    pass

可以使用 Exception 捕获除了 SystemExitKeyboardInterruptGeneratorExit 之外的所有异常。 如果还想捕获这三个异常,可以将 Exception 改成 BaseException 即可。

except Exception as err:
    print(err)

最后一个 except 子句可以忽略异常的名称,它将被当作通配符使用。可以使用这种方法打印一个错误信息,然后再次把异常抛出:

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error: {0}".format(err))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise  # 抛出异常

19.2.2 try/except…else

else 子句将在 try 子句没有发生任何异常的时候执行。使用 else 子句比把所有的语句都放在 try 子句里面要好,这样可以避免一些意想不到,而 except 又无法捕获的异常。

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print('cannot open', arg)
    else:
        print(arg, 'has', len(f.readlines()), 'lines')
        f.close()

异常处理并不仅仅处理那些直接发生在 try 子句中的异常,而且还能处理子句中调用的函数(甚至间接调用的函数)里抛出的异常。

19.2.3 try-finally 语句

finally 语句无论异常是否发生都会执行。

try:
    runoob()
except AssertionError as error:
    print(error)
else:
    try:
        with open('file.log') as file:
            read_data = file.read()
    except FileNotFoundError as fnf_error:
        print(fnf_error)
finally:
    print('这句话,无论异常是否发生都会执行。')

19.3 抛出异常

Python 使用 raise 语句抛出一个指定的异常。raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。

raise [Exception [, args [, traceback]]]

如果只想知道这是否抛出了一个异常,并不想去处理它,那么一个简单的 raise 语句就可以再次把它抛出。

>>> try:
        raise NameError('HiThere')  # 模拟一个异常。
    except NameError:
        print('An exception flew by!')
        raise
   
An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

19.4 用户自定义异常

可以通过继承 Exception 类来自定义异常类。大多数的异常的名字都以 “Error” 结尾,就跟标准的异常命名一样。

当创建一个模块有可能抛出多种不同的异常时,一种通常的做法是为这个包建立一个基础异常类,然后基于这个基础类为不同的错误情况创建不同的子类

class Error(Exception):
    """Base class for exceptions in this module."""
    pass

class InputError(Error):
    """Exception raised for errors in the input.

    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.

    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

19.5 清理行为

19.5.1 定义清理行为

不管 try 子句里面有没有发生异常,finally 子句都会执行。如果一个异常在 try 子句里(或者在 except 和 else 子句里)被抛出,而又没有任何的 except 把它截住,那么这个异常会在 finally 子句执行后被抛出。

19.5.2 预定义的清理行为

关键词 with 语句可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法。就算在处理过程中出问题了,文件总是会关闭。

19.5.3 with 关键字

Python 中的 with 语句用于异常处理,封装了 try…except…finally 编码范式,提高了易用性。with 语句使代码更清晰、更具可读性, 它简化了文件流等公共资源的管理。在处理文件对象时使用 with 关键字是一种很好的做法。

实例:

with open('./test_runoob.txt', 'w') as file:
    file.write('hello world !')

等效于:

file = open('./test_runoob.txt', 'w')
try:
    file.write('hello world')
finally:
    file.close()

with 语句实现原理建立在上下文管理器之上。

20 面向对象

出处: 菜鸟教程 - Python3 面向对象

参考内容:

  1. GtiHub - jackfrued: Python-100-Days/09.面向对象进阶
  2. 知乎 - 泽霖: python类的实例方法、静态方法和类方法区别

20.1 面向对象技术简介

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 方法: 类中定义的函数。
  • 类变量: 类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 局部变量: 定义在方法中的变量,只作用于当前实例的类。
  • 实例变量: 在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。
  • 数据成员: 类变量或者实例变量用于处理类及其实例对象的相关的数据。
  • 实例化: 创建一个类的实例,类的具体对象。
  • 对象: 通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
  • 继承: 即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
  • 方法重写: 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。

20.2 创建类

20.2.1 类定义

使用 class 定义类。

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>

最好使用 class ClassName(object) 的形式定义类。

类的属性和方法:

class ClassName(object):
    a = 0

    def fuc():
        print("Hello")

20.2.2 实例化

通过下述方法可以创建类实例。

obj = Myclass()

20.2.3 初始化

类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用。

class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart
x = Complex(3.0, -4.5)
print(x.r, x.i)   # 输出结果:3.0 -4.5

self代表类的实例,而非类

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self

class Test:
    def prt(self):
        print(self)
        print(self.__class__)
 
t = Test()
t.prt()

执行结果:
<__main__.Test instance at 0x100771878>
__main__.Test

从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.__class__ 则指向类。

self 不是 python 关键字,把它换成 runoob 也是可以正常执行的:

class Test:
    def prt(runoob):
        print(runoob)
        print(runoob.__class__)
 
t = Test()
t.prt()

20.2.4 类变量、实例变量

class test(object):
    a = 'cls'  # 类变量
    def __init__(self, num):
        some.b = num  # 实例变量

print(test.a)
obj = test(10)
print(obj.b)
print(obj.a)

20.2.5 类方法、实例方法、静态方法

一、创建

类方法第一个参数为 cls,表示这个类本身,包含当前类的相关信息。

实例方法第一个参数为 self,表示实例本身,包含实例对象的信息。

静态方法可以不用传入参数,相当于把一个外部函数放在类中。

class test(object):
    a = "类变量"

    def __init__(self):
        self.b = "实例变量"

    @classmethod
    def cls_func(cls):
        print("这是一个类方法")

    def obj_func(self):
        print("这是一个实例方法")

    @staticmethod
    def func():
        print("这是一个静态方法")

二、调用
类内调用:

class Test(object):
    a = "类变量"

    def __init__(self):
        self.b = "实例变量"

    @classmethod
    def cls_func(cls):
        print("这是一个类方法")

    def obj_func(self):
        print("这是一个实例方法")

    @staticmethod
    def func():
        print("这是一个静态方法")

    # 类内调用
    @classmethod
    def cls_func1(cls):
        cls.cls_func()
        cls.func()
        obj = cls()
        obj.obj_func()

    def obj_func1(self):
        self.cls_func()
        self.obj_func()
        self.func()

    @staticmethod
    def func1():
        Test.cls_func()
        obj = Test()
        obj.obj_func()
        Test.func()

对象调用:

if __name__ == "__main__":
    test.cls_func()
    test.func()

    obj = test()
    obj.cls_func()
    obj.obj_func()
    obj.func()

三、使用场景

类方法一般是整个类都会使用的操作,不受实例化影响。

实例方法需要实例化,用于涉及特定实例化对象的操作。

静态方法用于不涉及类信息的操作。

四、区别

子类的实例继承了父类的static_method静态方法,调用该方法,还是调用的父类的方法和类属性。

子类的实例继承了父类的class_method类方法,调用该方法,调用的是子类的方法和子类的类属性。

20.3 访问可见性

在 Python 中,属性和方法的访问权限只有2种:公开私有。以2个下划线开头进行声明,可以将属性和方法定义为私有。声明为私有后,只能在类的内部调用 ,不能在类的外部调用。

class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    # AttributeError: 'Test' object has no attribute '__bar'
    test.__bar()
    # AttributeError: 'Test' object has no attribute '__foo'
    print(test.__foo)


if __name__ == "__main__":
    main()

但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是"We are all consenting adults here"。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。

class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    test._Test__bar()
    print(test._Test__foo)


if __name__ == "__main__":
    main()

在实际开发中,并不建议将属性设置为私有的,因为这会导致子类无法访问。所以大多数Python程序员会遵循一种命名惯例就是让属性名单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重。这种做法并不是语法上的规则,单下划线开头的属性和方法外界仍然是可以访问的,所以更多的时候它是一种暗示或隐喻。

20.3.1 @property装饰器

在不将属性直接暴露给外界的情况下,如果想访问属性可以通过属性的 getter(访问器)和 setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用 @property 包装器来包装 gettersetter 方法,使得对属性的访问既安全又方便。

class Person(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

    # 访问器 - getter方法
    @property
    def name(self):
        return self._name

    # 访问器 - getter方法
    @property
    def age(self):
        return self._age

    # 修改器 - setter方法
    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        if self._age <= 16:
            print('%s正在玩飞行棋.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大锤', 12)
    person.play()
    person.age = 22
    person.play()
    # person.name = '白元芳'  # AttributeError: can't set attribute


if __name__ == '__main__':
    main()

20.4 动态性

Python 是一门动态语言。通常,动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定

import types

class Pearson(object):

    def __init__(self, name, age):
        self._name = name
        self._age = age

def is_adult(self):
    if self._age >= 18:
        return True
    else:
        return False

def main():
    boy = Pearson("小明", 16)
    boy._gender = "男"
    boy.is_adult = types.MethodType(is_adult, boy)
    print(boy._gender)
    print(boy.is_adult())
    del boy.is_adult
    delattr(boy, "_gender")

if __name__ == "__main__":
    main()

注意: deldelattr 功能有限,都是针对实例对象而言的,对于类方法,类属性则删除不了。

更多内容可以参考:CSDN - 涤生大数据:Python语言的动态性:运行时动态绑定,删除属性和方法

20.4.1 __slots__魔法

如果需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义 __slots__ 变量来进行限定。需要注意的是 __slots__ 的限定只对当前类的对象生效,对子类并不起任何作用

class Person(object):

    # 限定Person对象只能绑定_name, _age和_gender属性
    __slots__ = ('_name', '_age', '_gender')

    def __init__(self, name, age):
        self._name = name
        self._age = age

20.5 继承

子类(派生类 DerivedClassName)会继承父类(基类 BaseClassName)的属性和方法:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>

基类定义在另一个模块中时,可以使用:

class DerivedClassName(modname.BaseClassName):

子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力。

20.5.1 多继承

多继承的类定义形如下:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类类中未找到时,python会从左到右查找父类中是否包含方法。

class A(object):
    @classmethod
    def who(cls):
        print("A")

class B(object):
    @classmethod
    def who(cls):
        print("B")

class C(A, B):
    pass

def main():
    C.who()
    print(C.__mro__)  # 查看类的方法解析顺序

if __name__ == "__main__":
    main()

更多内容可以参考:朋疏哲N:Python多继承实现以及问题应对策略

20.5.2 super() 函数

super() 函数是用于调用父类(超类)的一个方法。super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

语法:

super(type[, object-or-type])

参数:

  • type – 类。
  • object-or-type – 类,一般是 self。

实例:

class FooParent(object):
    def __init__(self):
        self.parent = 'I\'m the parent.'
        print ('Parent')
    
    def bar(self, message):
        print ("%s from Parent" % message)
 
class FooChild(FooParent):
    def __init__(self):
        super().__init__()    
        print ('Child')
        
    def bar(self, message):
        super().bar(message)
        print ('Child bar fuction')
        print (self.parent)
 
if __name__ == '__main__':
    fooChild = FooChild()
    fooChild.bar('HelloWorld')

菱形继承:

     ---> B ---
A --|          |--> D
     ---> C ---

Python3 中继承遵循广度优先原则:

class A:
    def __init__(self):
        print("Enter A")
        print(self)
        print("Leave A")

class B(A):
    def __init__(self):
        print("Enter B")
        print(self)
        super(B, self).__init__()
        print("Leave B")

class C(A):
    def __init__(self):
        print("Enter C")
        print(self)
        super(C, self).__init__()
        print("Leave C")

class D(B, C):
    def __init__(self):
        print("Enter D")
        print(self)
        super(D, self).__init__()
        print("Leave D")

d = D()

运行结果:

Enter D
<__main__.D object at 0x7fdb02618490>
Enter B
<__main__.D object at 0x7fdb02618490>
Enter C
<__main__.D object at 0x7fdb02618490>
Enter A
<__main__.D object at 0x7fdb02618490>
Leave A
Leave C
Leave B
Leave D

20.6 多态

子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。

class Parent:        # 定义父类
   def myMethod(self):
      print ('调用父类方法')
 
class Child(Parent): # 定义子类
   def myMethod(self):
      print ('调用子类方法')
 
c = Child()          # 子类实例
c.myMethod()         # 子类调用重写方法
super(Child, c).myMethod()  # 用子类对象调用父类已被覆盖的方法

20.7 类的专有方法

  • __init__:构造函数,在生成对象时调用;
  • __del__:析构函数,释放对象时使用;
  • __repr__:打印,转换;
  • __setitem__:按照索引赋值;
  • __getitem__:按照索引获取值;
  • __len__:获得长度;
  • __cmp__:比较运算;
  • __call__:函数调用;
  • __add__:加运算;
  • __sub__:减运算;
  • __mul__:乘运算;
  • __truediv__:除运算;
  • __mod__:求余运算;
  • __pow__:乘方。

参考阅读:
知乎 - 黄同学:Python基础(十九):面向对象“类”之魔法方法
DataScience:Day12.魔法方法&方法重写

20.7.1 运算符重载

#!/usr/bin/python3

class Vector:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def __str__(self):
        return 'Vector (%d, %d)' % (self.a, self.b)

    def __repr__(self):
        return 'Vector (%d, %d)' % (self.a, self.b)

    def __add__(self,other):
        if other.__class__ is Vector:
            return Vector(self.a + other.a, self.b + other.b)
        elif other.__class__ is int:
            return Vector(self.a+other,self.b)

    def __radd__(self,other):
        """反向算术运算符的重载
        __add__运算符重载可以保证V+int的情况下不会报错,但是反过来int+V就会报错,
        通过反向运算符重载可以解决此问题
        """

        if other.__class__ is int or other.__class__ is float:
            return Vector(self.a+other,self.b)
        else:
            raise ValueError("值错误")

    def __iadd__(self,other):
        """复合赋值算数运算符的重载
        主要用于列表,例如L1+=L2,默认情况下调用__add__,会生成一个新的列表,
        当数据过大的时候会影响效率,而此函数可以重载+=,使L2直接增加到L1后面
        """

        if other.__class__ is Vector:
            return Vector(self.a + other.a, self.b + other.b)
        elif other.__class__ is int:
            return Vector(self.a+other,self.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)
print (v1 + 5)
print (6 + v2)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎猫骑巨兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值