Python 学习日记

文章目录

  • 2021/1/11
    • 1.4 关于Python语言
    • 2.2 关键字
    • 2.3 标识符
    • 2.4 数据类型
      • 2.4.1 整数 int
      • 2.4.2 浮点数 float
      • 2.4.3 复数 complex
      • 2.4.4 字符串 str
  • 2021/1/12
      • 2.4.5 布尔值 bool
      • 2.4.6 空值 None
    • 2.5 运算
      • 2.5.1 算术运算
      • 2.5.2 逻辑运算
      • 2.5.3 字符串的运算
      • 2.5.4 比较运算
      • 2.5.5 运算符 in 和 is
  • 2021/1/13
    • 2.6 类型转换
    • 2.7 赋值语句
    • 3.2 初识map函数
  • 2021/1/14
  • 2021/1/15
    • 3.3 使用 eval 函数
    • 3.4 字符串格式化
    • 3.5 Python 的语句块
    • 3.6 注释和续行
    • 4.1 分支结构
      • 4.1.1 简单的 if 语句,如:
      • 4.1.2 带分支的条件语句
  • 2021/1/16
    • 4.2 循环结构
      • 4.2.1 使用 range 函数
      • 4.2.2 for 循环
  • 2021/1/17
      • 4.2.3 break 语句
      • 4.2.4 while 循环
  • 2021/1/18
  • 2021/1/19
      • 4.2.5 continue 语句
  • 2021/1/20
    • 4.3 循环结构的典型应用
      • 4.3.1 汇总型循环
  • 2021/1/22
      • 4.3.2 发现型循环
      • 4.3.3 将一维数据矩阵化
    • 4.4 断言和捕获程序错误
  • 2021/1/22
    • 4.5 程序的调试
      • 4.5.1 语法错误
      • 4.5.1 运行时错误
      • 4.5.3 语义错误
      • 4.5.4 使用集成开发环境调试程序的几种方法
    • 5.1 用户定义的函数
  • 2021/1/23
  • 2021/1/28
    • 5.2 未具名的函数:匿名函数
  • 2021/1/29
    • 5.3 函数的递归调用:递归函数
  • 2021/2/1
    • 5.4 了解模块
  • 2021/2/3
    • 5.5 数学模块
    • 5.6 随机数模块 random
    • 5.7 时间模块 time
    • 5.8 系统和操作系统模块 sys、os
  • 2021/2/5
    • 5.9 自定义模块
    • 6.3 对字符串元素的索引
    • 6.4 对字符串元素的切片
    • 6.5 对字符串进行遍历
    • 6.6 字符串方法
  • 2021/2/13
      • 6.6.1 联合 join 和分割 split 方法
  • 2021/2/14
      • 6.6.2 查找 find 方法和字符串解析
    • 7.1 认识列表
    • 7.2 作为可变对象的列表
    • 7.3 列表的增删改操作
      • 7.3.1 列表的运算和成员检查
      • 7.3.2 列表的增操作
      • 7.3.3 列表的删操作
      • 7.3.4 列表的改操作
  • 2021/2/19
    • 7.4 列表的方法
    • 7.5 遍历列表
    • 7.6 列表的复制:深拷贝和浅拷贝
  • 2021/2/22
    • 7.7 列表推导式
  • 2021/3/7
    • 7.8 认识元组
    • 7.9 生成器表达式
  • 2021/3/16
    • 8.1 认识字典
      • 8.1.1 字典的创建
      • 8.1.2 字典的访问
      • 8.1.3 字典的编辑
  • 2021/3/8
    • 10.1 使用 enumerate 函数枚举对象
    • 10.2 使用 product 函数扁平化循环
    • 10.3 使用 any/all 函数替代循环
  • 2021/3/13
    • 10.4 使用 exec 函数
    • 10.5 字典和集合推导式
    • 10.6 可迭代对象与迭代器
  • 2021/3/15
    • 10.7 生成器表达式和函数式生成器
    • 10.8 迭代工具模块 itertools
    • 12.1 多维数组 ndarray 的创建
      • 12.1.1 使用 array 函数
    • 12.2 索引和切片
      • 12.2.2 多维数组的索引和切片

2021/1/11

《简明Python教程》

1.4 关于Python语言

Python作为解释型语言,运行速度偏慢,尤其是和编译型的C语言对比。但是Python拥有灵活的数据结构和丰富的扩展模块,因此编程过程更加方便快捷。

2.2 关键字

可以在help函数派生的环境下查询关键字。第一步,在shell中输入:

help()

然后在help>子环境中输入:

help>keywords

2.3 标识符

首字符必须是字母或下划线
区分大小写

2.4 数据类型

2.4.1 整数 int

二进制(binary)数的前缀 0b/0B
八进制(boctal)数的前缀 0o
十六进制(hexadecimal)数的前缀 0x

对于较大整数,Python支持用下划线分隔数的表示方法,如:

a = 12_345_000 表示 12345000

2.4.2 浮点数 float

浮点数可以使用字母e参与表示,如:

1.23e9 表示 1.23 乘以 10 的 9 次方

比较两个浮点数是否相等的方法,如:

设置一个较小值 epsilon,例如 epsilon = 1e-10,进而判断两个浮点数差值是否小于 epsilon ,如果是,则认定二者相等,反之二者不相等。
注意:不要试图用“ == ”测试两个浮点数是否相等。

2.4.3 复数 complex

c = 3 + 5j #表示一个复数
c = complex(3, 5) == 3 + 5j #True
c.real = 3.0                #点语法
c.imag = 5.0                #点语法 

2.4.4 字符串 str

使用成对的单引号或双引号定界字符串没有本质的区别,但是设计不止一种表示方式的好处在于,能够在某种情况下赋予用户更大的灵活性。

"It's a python"
'It\'s a python' #用到转义字符

常用的转义字符,如:

"\b" #Backspace 退格键
"\n" #Linefeed 换行
"\r" #将光标退回到本行的开头位置

关于三引号“```”的说明,如:

# 正规注释一般由“#”引导一行文本构成
s1 = ```
I LIKE PYTHON
SO DOES HE ```
# 三引号定界的一段文本实际上创建了一个字符串常量,不少程序员习惯用三引号定界的文本来注释程序。
#并且,三引号定界的字符串如果放在函数定义的函数体之前,这段文本则会成为该函数的帮助文档。

2021/1/12

2.4.5 布尔值 bool

not 非运算
and 与运算
or 或运算

2.4.6 空值 None

None 是类型 NoneType 的唯一值。
None 并不等同于 0 或空字符串

2.5 运算

2.5.1 算术运算

两个整数相除,结果是浮点数。
可以理解为:当运算在两个不同类型之间进行时,参与运算的数自动地向更宽泛的类型转换,类型宽泛性次序为:布尔型<整数<浮点数<复数

当一个表达式中出现多个操作符时,求值顺序依赖其优先级。优先级顺序从高到低,如:

括号
乘方
乘法和除法
加法和减法

7 = 3 * 2 +1 式中,“7”是被除数,“3”是除数,“2”是商,“1”是余数。
** 表示指数运算,// 表示整除求商,% 表示求余,如:

2 ** 3 # ** 表示指数运算,2 ** 3 == 8
7 // 3 # // 表示整除求商,7 // 3 == 2
7 % 3  # % 表示求余,7 % 3 == 1
divmod(7, 3) # divmod(7, 3) == (2, 1)

2.5.2 逻辑运算

布尔值除进行算术运算外,还可以进行逻辑运算,逻辑运算符包括 not,and 和 or,优先级如:

not 一元操作,执行“非”运算,得到和操作数相反的逻辑值
and 二元操作,执行“与”运算,只有当两个操作数均为True时,结果为True
or 二元操作,执行“或”运算,只有当两个操作数都为False时,结果为False

其他类型的变量参与逻辑运算时,会发生自动的类型转换

2.5.3 字符串的运算

“+”表示连接两个字符串,“*”表示将字符串延展若干倍,如:

s1 = "Python"
s2 = "I love" + s1
>> s2
'I love Python'
s3 = s1 * 3
>> s3
'PythonPythonPython'

2.5.4 比较运算

当整数和浮点数进行比较时,整数会被隐式地转换为浮点数
字符串的比较是从首位开始,逐一比较ASCII值

Python支持链式的比较运算,如:

>> 3 < 5 < 9 == 9 <= 10
True

2.5.5 运算符 in 和 is

判断元素是否存在与容器中可用关系运算符 in(not in),如:

>> "p" in "python"
True
>> "Py" not in "python"
True

判断两个变量是否为同一对象的运算符 is(is not),即判断两个变量是否位于同一内存地址,如:

a = 2 + 3
b = 5 * 1
>> a is b
True

>> id(a) == id (b) # id() 返回对象的地址
True

2021/1/13

2.6 类型转换

print("I have " + 3 + "mobiles") # TypeError

print("I have " + str(3) + "mobiles") # Success

查看帮助,如:

help(int)

2.7 赋值语句

Python 中赋值操作本质上是建立对象的引用,或理解为给对象贴标签。b = a 即b与a指向同一个对象,a 和 b “互为别名”(地址相同),是同一个对象的两个标签。如:

a = [0,1,2]
b = a
a[1] = 5
print(b)
>>[0,5,2]

Python 也允许 a,b 所指向的内存地址不同,但值可以相同。即浅拷贝,如:

a = [0, 1, 2]
b = a[:]
# 因为 a,b 地址不相同,因此改变 a 或 b 的值时对另一个没有影响

Python 交换赋值,如:

a = 2
b = 3
a, b = b, a
print(a, b)
>> 3 2

Python *在赋值中的应用,如:

a, *b, c, d = [0, 1, 2, 3, 4, 5]
print(a, b, c, d) #a,c,d根据位置分别获得值0,4,5,而 b 获得其余值
>> (0, [1, 2, 3], 4, 5)

3.2 初识map函数

map(func, *iterables) 根据提供的函数对指定序列作映射

function – 函数
iterable – 一个或多个序列

如:

def square(x)
	return x ** 2
map(square, [1,2,3,4,5])
>>[1,4,9,16,25]

str.split(self, /, sep=None, maxsplit=-1) 指定分隔符对字符串进行切片

self – 实例
sep – 分隔符,如" ", “#” 等
maxsplit – 分隔次数,默认-1,即分隔所有

prompt = "请输入两个个整数,用空格分开:\n"
inp = input(prompt)
nums = map(int, inp.split())
print("和为:", sum(nums))
>> 70    30
和为:100

在上例中,我们称split为方法(method),而int、map为函数(function)
方法是定义在类中的函数,必须使用点语法进行调用,调用者必须是类的对象

2021/1/14

《原子核物理》成绩计算程序

f = open("C:/Users/ChaoWang/Desktop/Score.txt", "r", encoding='utf-16')
line = f.readline()
result = []

while line:
    line = line.split(sep="\t")
    line = [x.strip() for x in line]
    temp = 0

    for i in range(2, 10):
        if line[i] == "A+":
            temp = temp + 100
        elif line[i] == "A":
            temp = temp + 95
        elif line[i] == "A-":
            temp = temp + 90
        elif line[i] == "B+":
            temp = temp + 85
        elif line[i] == "B":
            temp = temp + 80
        elif line[i] == "B-":
            temp = temp + 75
    line.append(temp / (len(line) - 2))
    print(f'{line[1]}的平时成绩是:{line[-1]}')
    line = f.readline()  # 读取一行
print(result)
f.close()

2021/1/15

3.3 使用 eval 函数

使用 eval 从字符串中直接提取数值,但必须是合法的元组对象(用逗号隔开),如:

prompt = "请输入两个整数,用逗号分开:\n"
inp = input(prompt)
nums = eval(inp)
print(nums)
print("和为:", sum(nums))

例3-4 从磁盘上读取数据、计算并写回,如:

import math
f = open(r"c:\a.txt")
num = f.read()
f.close()
result = math.sqrt(int(num))
f = open(r"c:\b.txt", 'w')
f.write(str(result))
f.close()

3.4 字符串格式化

我们可以用逗号隔开的两个对象拼接为一个整体的字符串,但是当对象数量较多时,操作会显得繁琐。为此,可以采用 Python 提供的字符串格式化方案,如:

n1 = 2
n2 = 1
n3 = 0
# 方案1,代码续前
print("I have %d dogs, %d cats, and %d fish." % (n1, n2, n3))
# 使用 % 进行字符串格式化(如:%d,%s等)
# 注意:前面待替换数目和后面变量数目要严格一致

# 方案2,使用f前缀
print(f'I have {n1} dogs, {n2} cats, and {n3} fish')

3.5 Python 的语句块

  1. Python 中用缩进和对齐来区别语句块。具有相同缩进量的一系列语句属于一个语句块。书写代码时,务必重视对代码缩进深度进行检查
  2. Tab 键配置为4个空格

3.6 注释和续行

可以使用引号定界的字符串代替注释,对于多行注释可以使用三引号
在程序调试的过程中,常用注释来临时关闭某一行代码。与其将其径直删除,把它设为注释并且保留才是常规的做法

代码分行的两种方法:

  1. 在代码截断处,上一行末尾插入反斜杠 \
  2. 考虑将截断处放在一套 (), [], {} 这样的定界符之内 —— 推荐使用

4.1 分支结构

4.1.1 简单的 if 语句,如:

score = 95
if score >= 90:
    print("excellent")

Python 的条件语法规则有几点值得注意

  1. if 后的判断条件 score >= 90 并不需要用 () 括起来
  2. if 语句后需要加冒号
  3. 处于相同缩进的若干语句构成语句块。当语句块结束后要退出缩进
  4. 当语句块并不长时,可以使用“if 条件表达式: 语句块”的紧凑语法

4.1.2 带分支的条件语句

if…else… 语句的逻辑是:判断条件表达式的值,如果为 True,则执行语句块1的内容,否则执行语句块2的内容,如:

score = 60
if score >= 60:
	print("excellent")
else:
	print("failed")

对于多分支的情况,Python 以 elif 来实现,如:

score = 55
if score >= 90:
    print("excellent")
elif score >= 60:
    print("pass")
else:
    print("failed")

此外,Python 中还支持一种…if…else…的条件表达形式,如:

Passed = “Yes” if score >= 60 else "No"
# 当条件为 True 时,右侧表达式的值为 Yes,反之为 No

2021/1/16

4.2 循环结构

4.2.1 使用 range 函数

内置函数range是for循环之基,大量的循环基于range返回值展开,因此先了解range函数是很有必要的。range函数根据参数返回特定的range对象,该对象蕴含两个整数之间的序列,如:

print(range(5))
>> range(5)
print(list(range(5)))
>> [0, 1, 2, 3, 4]
print(range(3, 9))
>> range(3,9)
print(len(range(3, 9)))
>> 6
print(list(range(3, 9, 2))) #  range(start, stop[, step])
>> [3, 5, 7]
print(list(range(10, 1, -3)))
>> [10, 7, 4]

range 函数的特点,如:

  1. range 函数返回的的不是具体序列的值,而是惰性生成的range对象,可以使用 list 函数将 range 对象强制展开
  2. range 函数的形式之一是 range(stop),唯一参数为终点 stop ,函数返回从 0 到 stop 但不包含 stop 的序列,即 [0, stop)
  3. range 函数的形式之二是 range(start, stop[,step]),函数返回从 start 到 stop 但不包含 stop 的序列,即 [0, stop)
  4. range() 对象的参数都是整数,因此得到的序列也都是整数,如果需要构造含浮点数的序列可以用如下两个方案:

例 4-1 使用列表推导式(list comprehension)构造序列,相当于循环创建列表的简化,如:

size = int((5 - 3.14)/0.2)+1 # size == 10
rst = [3.14 + i * 0.2 for i in range(size)]
print(rst)
# 最后一个数为 3.14 + 9 * 0.2 = 4.94
>> [3.14, 3.3400000000000003, 3.54, 3.74, 3.9400000000000004, 4.140000000000001, 4.34, 4.54, 4.74, 4.94]

例 4-2 使用 numpy.arrage 函数构造序列,如:

import numpy as np
rst = np.arange(3.14, 5, 0.2)
print(rst)
>> [3.14 3.34 3.54 3.74 3.94 4.14 4.34 4.54 4.74 4.94]
  1. 当步长为 1 时,range 对象蕴含的元素数目等于 stop - start
  2. range() 对象是可迭代的(iterable)对象,这意味着 range 对象可以用 for 循环遍历访问(迭代)。但并不是迭代器(iterator),即访问一次 range 对象之后,其中的元素并不会因为访问而消耗掉,而元素的一次访问性是迭代器的特性

4.2.2 for 循环

for 循环的逻辑是设置一个使循环持续进行的条件,如果该条件 满足,则持续执行循环体,for 循环的语法如下:

for 循环变量 in 序列:
    语句块1
[else:
    语句块2]
# 推荐使用不带 else 语句的 for 循环

例 4-3 求 1 ~ 100 的和_v1

total = 0 
for x in range(1, 101): # x 是 1 ~ 100 的循环变量
    total = total + x
print(total)

推荐使用不带 else 的 for 循环和 while 循环,如:

s = "abcdefghijk"
find = False
for i in range(len(s)):
    if s[i] == "l":
        print("找到了,位置在:", i)
        find = True
        break
if not find:
    print("没找到")

例 4-5 打印输出九九乘法表。外层循环变量 i 处理行,内层循环变量 j 处理列。因为第 i 行有 i 个口诀,因此 j 的范围是[1, i+1),如:

for i in range(1, 10):
    for j in range(1, i+1):
        s = f'{i} * {j} = {i*j} '
        print("%-6s" % s, end = " ")
    print("\n")

2021/1/17

例 4-7 水仙花数是指该数各位数的立方和等于其本身。求三维水仙花数(思路1:遍历 100 ~ 999 的数),如:

for i in range(100, 1000):
    # 分别取得百位、十位和个位 a, b, c
    a, temp = divmod(i, 100) # a = 商,temp = 余数
    b, c = divmod(temp, 10)
    if a ** 3 + b ** 3 + c ** 3 == i:
        print(i, end=" ")

例 4-7 水仙花数是指该数各位数的立方和等于其本身。求三维水仙花数(思路2:遍历 百、十、个位),如:

for a in range(1, 10):
    for b in range(0, 10):
        for c in range(0, 10):
            if a * 100 + b * 10 + c == a ** 3 + b ** 3 + c ** 3:
                print(a * 100 + b * 10 + c, end=",")

4.2.3 break 语句

break 语句用在循环体之内,用途是推出本层循环。break 语句常和条件判断组合使用,表示在特定的条件下退出当前循环。

get_solution = False
for chick in range(1, 35 + 1):
    for rabbit in range(1, 35 + 1):
        if chick + rabbit == 35 and chick * 2 + rabbit * 4 == 94:
            print(chick, rabbit)
            get_solution = True
            break
    if get_solution:
        break

当循环不止两层时,这样逐层推出的方式十分繁琐。此时可以利用定义函数的方法,利用函数中的return语句从内层循环直接退出所有循环,如:

def calcCR(head, foot):
    for chick in range(1, head + 1):
        for rabbit in range(1, head + 1):
            if chick + rabbit == 35 and chick * 2 + rabbit * 4 == 94:
                return chick, rabbit
print(calcCR(35,94))
>> (23, 12)

4.2.4 while 循环

while 循环执行的逻辑是:每次进入循环前检测条件表达式,如果为 True ,执行循环体,如果为 False ,执行 else 子句。while 循环的语法如下:

while 条件表达式:
    语句块 1
[else:
    语句块 2
]
# 建议不使用带有 else 子句的非主流用法

例 4-11 求 1 ~ 100 的和(方法3)

total = 0
x = 1 # 循环计数器初始值
while x <= 100:
    total = total + x
    x += 1
print(total)

while 循环适用于循环体执行次数不明确的情形。典型地,循环体会修改某些关键变量,使得条件表达式的值从起初的 True 变为后来的 False,循环退出。
另一种典型应用是:使用 while True 引导循环,在这种写法下必须使用退出循环语句,避免其成为死循环,如:
例 4-12 求 1 ~ 100 的和(方法4)。使用带 break 的无限循环

total = 0
x = 1
while 1:  # 无限循环
    total = total + x
    if x >= 2:
        break
    x += 1
print(total)

2021/1/18

今天去了兴庆

2021/1/19

4.2.5 continue 语句

continue 语句用在循环体之内,用途是提前结束本轮循环进入下一轮循环。常与条件判断语句组合使用。
例4-14 模拟 shell 环境。用’==>'提示用户输入,用户输入字符串之后,回应“你输入了” + 用户所输入内容;当用户输入的字符串以 # 开头时,不作回应;当用户输入’quit’后立即退出

while 1:
    inp = input("==>")
    if inp[0] == '#':
        continue      # 退出本次循环
    if inp == 'quit':
        break         # 退出循环
    print("你输入了" + inp)
print("Byebye")

4-15 猜数字游戏。游戏规则说明:外层循环不断产生4位整数供用户猜想,用户随机猜出一个结果后,程序反馈正确数字的个数(位置不一定正确)和准确数字的个数(数字和位置都正确),用户根据程序反馈继续猜测。用户输入“next”表示放弃本次猜测本次出现的数字,输入“exit”表示结束程序。

自己编写的程序:

import random
while 1:
    inp = input("请输入猜测的四位整数")

    if inp == "next": # list对象和字符串不能直接比较
        continue
    elif inp == "exit":
        break
    else:
        Correct_number = 0
        Accurate_number = 0
        Guessing_integers = random.randint(1000, 9999)
        Guessing_integers = list(str(Guessing_integers))
        inp = list(str(inp))
        for number1 in range(len(Guessing_integers)):
            for number2 in range(len(inp)):
                if Guessing_integers[number1] == inp[number2]:
                    Correct_number = Correct_number + 1
                    if number1 == number2:
                        Accurate_number = Accurate_number + 1

    print(Guessing_integers, "正确数字的个数为:", Correct_number, "准确数字的个数为:", Accurate_number)

2021/1/20

4-15 猜数字游戏。游戏规则说明:外层循环不断产生4位整数供用户猜想,用户随机猜出一个结果后,程序反馈正确数字的个数(位置不一定正确)和准确数字的个数(数字和位置都正确),用户根据程序反馈继续猜测。用户输入“next”表示放弃本次猜测本次出现的数字,输入“exit”表示结束程序。

书上编写的程序(定义函数来实现且使用了 in 操作符):

import random


def compare(s1, s2):
    correct_number, accurate_number = 0, 0
    for i in range(len(s2)):
        if s2[i] in s1:
            correct_number += 1
        if s2[i] == s1[i]:
            accurate_number += 1
    return correct_number, accurate_number


while 1:
    the_num = random.randint(0, 9999)
    the_str = str(the_num).zfill(4)  # 方法返回指定长度的字符串,原字符串右对齐,前面填充0
    print("4 位整数已就绪,请猜一猜:\n")
    time = 0
    while 1:
        inp = input(">")
        time += 1
        if inp == "next":
            break
        if inp == "exit":
            exit()         # 结束程序
        if inp == "peep":  # 显示随机生成的数字
            print(the_str)
            continue
        if len(inp) == 4 and inp.isnumeric():  # 检测字符串是否只由数字组成
            m, n = compare(inp, the_str)
            print("Time(%d): %d correct %d accurate" % (time, m, n))
            if n == 4:
                print("Great!")
                break
        else:
            continue

exit() 结束程序
isnumeric()方法 检测字符串是否只由数字组成

4.3 循环结构的典型应用

4.3.1 汇总型循环

汇总型循环适用于这样一些场景:统计一份学生成绩单中及格者的人数,或统计一段文本中各种类型的字符的数目等。对于这样的需求,一般是完全遍历整个对象,并且设置临时变量来存储所需要的中间结果,直至遍历完成后获得最终结果。
例 4-16 统计一份成绩单中及格者的人数和平均分

score = [70, 54, 50, 90, 89, 78, 67, 45, 31, 43]
count, total = 0, 0
for s in score:
    if s > 60:
        count += 1
        total += s
assert count > 0  # assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。
average = total / count
print(average)

assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常

2021/1/22

4.3.2 发现型循环

发现型循环适用于这样的场景:查找一份学生清单中的最高分。对于这样的需求,编程思路是:首先记录第一个学生的分数为最高分,然后遍历整个清单,当存在比之前记录的最高分更高的分数时,用新的高分替换旧的高分,直至遍历完成。

4.3.3 将一维数据矩阵化

例 4-19 给某班 48 名考生随机排座位。假设考生学号为 1~48,考场为 8 行 6 列,要求给所有考生按照格式 1:3:4(表示学号为1的考生位于第3行第4列)输出座位代码。

import random

num = 48
nRow = 8
label = list(range(1, num + 1))  # 1~48 号考生的标签
random.shuffle(label)    # 将序列的所有元素随机排序。

for i in range(1, num + 1):
    row_no = label[i-1] % nRow + 1   # 根据label计算行号,+1 表示 1 起点
    col_no = label[i-1] // nRow + 1  # 根据label计算列号,+1 表示 1 起点
    s = f'{i}:{row_no}:{col_no}'
    print(s)

4.4 断言和捕获程序错误

在程序的编写过程中,由于一系列原因可能导致运行结果不符合预期。可以通过在关键点插入辅助性语句进行程序的调试。
思路 1:使用 print 函数输出关键变量的状态。在大批量执行的循环语句中,如果要频繁输出关键信息,会令测试者目不暇接。为此,可考虑使用间歇性的 print 输出呈现信息,如:

x = 3
y = 5
for i in range(1000):
    # 省略了代码
    if i == 100:
        print(x, y)
    # 省略了代码

上述做法的缺点是,在调试阶段插入的 print() 语句在发布阶段必须删除

思路 2:插入 assert 断言语句。该语句在 expression 为 True 时,不作任何响应,在 expression 为 False 时抛出异常,如:

assert expression
assert 1 == 2

>> Traceback (most recent call last):
  File "D:/PythonProject/main.py", line 303, in <module>
    assert 1 == 2
AssertionError

Process finished with exit code 1

一个好的程序,不可以因为用户的输入不符合预期就直接报错进而退出

异常捕捉可以用 try/expect 语句

2021/1/22

4.5 程序的调试

4.5.1 语法错误

解释器在语法检查阶段会发现语法错误并报告错误所在行

4.5.1 运行时错误

在代码已经通过了语法检查之后,程序在运行阶段报错。有如下几种手段改进完善程序:

使错误尽可能复现
通过对用户输入进行检查来加强代码的鲁棒性

4.5.3 语义错误

程序根本不报错,但是运行结果不符合预期,存在 Bug。对于 Python 而言,一种典型的语义错误是由于没有正确使用代码缩进。

4.5.4 使用集成开发环境调试程序的几种方法

设置断点,并且全速运行至断
单步执行、进入和跳出函数、全速运行

5.1 用户定义的函数

函数可以减少程序的重复,缩减篇幅。函数定义,如:

def 函数名(形参列表): #可以没有形参
	函数体
	return 表达式1 # 如果函数有返回的结果(没有返回结果可以不用或者仅写一个return,即返回None)
	return 表达式2

当某行代码的缩进退回到和 def 对齐时表示函数结束
函数体不能为空,未设计完成时可以使用占位语句 pass 代替
函数的定义必须先于调用

在内存中创建函数对象之后,可以通过 id、type 观察函数对象的地址、类型,如:

def calc(a, b):
    return a ** 2 - b ** 2


print(id(calc))    # 输出对象地址
print(type(calc))  # 输出对象类型

>> 1994280620528
>> <class 'function'>

也可以将该对象赋值给某变量,赋值之后,该变量指向函数,即可以通过新的变量名来调用函数,如:

def calc(a, b):
    return a ** 2 - b ** 2


va = calc
print(va(1, 2))

>> -3

2021/1/23

今天调整一下生物钟,晚上十点半之后写博客每次都很迟睡。明天看完第五章。
在自定义函数中撰写“帮助”文档

2021/1/28

一调整就是五天没更新

通过在函数体和函数头之间使用 三引号 插入字符串的方式,自定义函数中撰写帮助文档

def myFun():
    """这是函数的帮助文档"""
    pass

在 Pycharm 编辑器中,选中函数名,点击 view -> Quick Documentation 即可查看函数的帮助文档

例 5-1 实现银行卡号的 Luhn 检验,检验过程如下:

  1. 从卡号的最后一位数字开始,逆向将奇数位相加
  2. 从卡号的最后一位数字开始,逆向将偶数位数字先 乘以2(如果乘积>=10,则减 9) 再求和
  3. 将上述两步的结果求和,若能被 10 整除,则校验通过
def CheckCard(n):
    """对银行卡卡号进行 Luhn 检验,合法返回 True,否则返回 False"""
    counter1, counter2 = 0, 0  # 与奇数位相关的计数,与偶数位相关的计数
    list_n = list(str(n))
    n_len = len(list_n)

    if n_len % 2 == 0:    # 银行卡号的位数为偶数
        i = -1
        while 1:
            if i < -n_len:
                break
            else:
                counter1 += int(list_n[i])  # 奇数位
                if int(list_n[i-1])*2 < 10:  # 偶数位
                    counter2 += int(list_n[i - 1])*2
                else:
                    counter2 += int(list_n[i-1])*2 - 9
                i = i - 2
    else:
        i = -1
        while 1:
            if i < -n_len:
                break
            else:
                counter1 += int(list_n[i])  # 奇数位
                if i == -n_len:
                    pass
                else:
                    if int(list_n[i-1])*2 < 10:  # 偶数位
                        counter2 += int(list_n[i-1])*2
                    else:
                        counter2 += int(list_n[i-1])*2 - 9
                i = i - 2

    print("银行卡号有效") if (counter1 + counter2) % 10 == 0 else print("银行卡号无效")

5.2 未具名的函数:匿名函数

当程序规模变得庞大时,定义大量的函数名字在一定程度上是一个负担,并且思路还会被繁琐的函数定义打断.可以用 lambda 定义匿名函数(即用即抛的函数,比较简短),语法如下:

lambda 形参1, 形参2, ...: 表达式

匿名函数常与 map 搭配使用,用于将不复杂的特殊运算映射到某序列之上

print(list(map(lambda x: x**2 + 1, [1, 2, 3])))
>> [2, 5, 10]

例 5-3 使用匿名函数给 names 列表,按照 姓 给字典顺序排序

names = ["Chao Wang", "Wenquan Wei", "Zebing Li"]
names.sort(key = lambda name: name.split()[-1].lower())
print(names)

>> ['Zebing Li', 'Chao Wang', 'Wenquan Wei']

列表中的 sort 方法对列表元素进行排序,将 key 指定为匿名函数,在函数中通过 split 方法取得姓名分割后的列表,通过 [-1]取最后的元素,通过 lower() 确保排序为不区分大小写的字典排序

2021/1/29

5.3 函数的递归调用:递归函数

所谓递归调用就是在函数内部调用自身

对于递归有没有什么好的理解方法? - 帅地的回答 - 知乎
https://www.zhihu.com/question/31412436/answer/683820765

例 5-4 求自然数 n 的阶乘

def factorial(n):
    if n <= 1:
        return 1  # 递归的出口
    else:
        return n*factorial(n-1)

    
print(factorial(10))

当我们试图调用factorial(1000)时,Pycharm编辑器会报错,如下:
RecursionError: maximum recursion depth exceeded in comparison
即超出了最大递归深度

2021/2/1

例 5-5 求斐波那契数列的第n项。斐波那契数列形如1,1,2,3,5,8…这样的无限序列,所有项均为前面两项之和

def Fibonacci(n):
	if n == 1 or n == 2
		return 1
	else:
		return Fibonacci(n-1) + Fibonacci(n-2)

上列代码当参数越来越大时会逐渐变慢,因为递归过程会一直从 n 溯源到 1,再返回 n。

为解决上述问题,可以使用 functools 模块下的 lru_cache 函数,“lru”表示 Least-recently-used(最近最少使用),是一种缓存技术,用其修饰函数可以显著提升访问性能。

from functools import lru_cache
@ lru_cache(maxsize = 100)

def Fibonacci(n):
	if n == 1 or n == 2
		return 1
	else:
		return Fibonacci(n-1) + Fibonacci(n-2)

5.4 了解模块

要使用模块所提供的内容,须要先导入模块,导入完成后,对其函数的调用或变量的使用应使用“模块名.函数名()”这样的点语法
导入方式 1:

import math, random
# 可以将不同的模块名称用逗号隔开,一并导入

print(math.sqrt(16))
print(math.pi)

由于模块的名称在代码中会频繁被使用,人们还常在模块导入时以“as + 别名”语法,得到更简短的名字空间,便于使用,如:

import math as ms
import numpy as np

这种使用 as 别名不是新增一个名字空间,而是创建一个以别名为名字的空间,原先的名字并不存在对应的空间。也就是说执行 import math as ms 之后,只可以使用 ms.sqrt,而math.sqrt 这样的调用并不合法。

导入方式 2:

from math import sqrt
print(sqrt(16))
>> 4.0
print(math.sqrt(16))
>> NameError: name 'math' is not defined

2021/2/3

5.5 数学模块

import math
dir(math)  # 函数可以查看包含的内容
help(math.log)  # 函数可以查看帮助信息

5.6 随机数模块 random

random模块比较重要的函数有:

random() 函数,返回 0 到 1 之间均匀分布的随机数
uniform(a, b) 函数,返回介于 a, b 两个数之间的均匀分布的随机数
randint(a, b) 函数,返回介于 a, b 两个数之间,并且包含两端的随机整数

import random
random.seed(2.10)
print(random.random())

5.7 时间模块 time

sleep()函数,执行以秒为单位的延时。这个函数可以用来构造某些需要延时执行或定期执行的任务
time()函数,用来获取当前时间的时间戳,即1970年1月1日8时0分0秒至当前时间的秒数。常在部分代码前后分别获取时间戳的额方法,来计算代码运行所消耗的时间。

例 5-11 获取代码的运行时间

import time
t0 = time.time()
for i in range(100_0000):pass
t1 = time.time()
print(t1 - t0)

代码中,for 循环内实际上什么都没有做,但是它测试了循环条件100万次,所以消耗了CPU资源,占用了时间

5.8 系统和操作系统模块 sys、os

例 5-12 sys 和 os 模块的简单应用

import sys
import os
print(sys.version)
print(sys.path)  #系统路径
print(os.name)  # 操作系统名称

2021/2/5

5.9 自定义模块

  1. 首先创建 python 文件,如
    module1.py
  2. 在同样的路径下输入如下语句
import module1py
# 调用模块

6.3 对字符串元素的索引

  1. 字符串中的每一个字符都可以通过两个索引值来访问,这两个索引值的绝对值之和等于字符串的长度
  2. 不能修改字符串中的元素,如:
s1 = "student"
s1[2] = 'U'
>> TypeError

6.4 对字符串元素的切片

通过切片方式可以访问字符串的多个元素,语法格式如下:

s[start:end:step]
# start 表示切片的起始位置,end 表示切片的截至位置,step 表示切片的步长
# 当 step 为负值时,切片从右向左进行,如果start省略,表示从最右侧开始;如果 end 省略,表示到最左侧结束。
s1 = "12345"
print(s1[::2])
print(s1[::-1])
print(s1[2:0:-1])  # 起始元素是第2个元素,即3。终止元素是第0个元素即1
>> 135
>> 54321
>> 32

6.5 对字符串进行遍历

假设要完成这样的任务:将字符串s1的每个元素打印输出,中间用逗号隔开。

方案1:

s1 = "student"
for i in range(len(s1)):
    print(s1[i], end=',')

方案2:

s1 = "student"
for s in s1:
    print(s, end=',')

因为字符串是可迭代对象,如遍历操作无需使用索引,可采用直接遍历的方式。

6.6 字符串方法

>> dir(str)

字符串方法的使用方式,如:

s1.upper()
# s1 是字符串类型的对象
# upper()是方法

2021/2/13

6.6.1 联合 join 和分割 split 方法

join 方法可以通过连接符将多个字符串拼接为一个整体,而 split 方法完成相反的操作,将一个字符串拆解为多个单体。

join 方法,如:

firstname = 'Tom'
lastname = 'Lucy'
s1 = "-"
s2 = ""
name = s1.join([firstname, lastname])
print(name)

>> Tom-Lucy

split 方法,如:

name = "Good-Boy, NiceGirl"
x = name.split(sep=',')
print(x)

>> ['Good-Boy', ' NiceGirl']

例 6-2 从长字符串中解析出电子邮箱地址

sentence = '''If you have any question
, please contact me via [email protected]
'''
sentenceSplit = sentence.split(sep=' ')
for temp in sentenceSplit:
    if temp.find("@") > 0:
        break
        
email = temp
print(email)

>> 410435553@qq.com

2021/2/14

6.6.2 查找 find 方法和字符串解析

上一节例 6-2 获取邮箱地址的程序虽然能正常工作,但是如果仅仅为了获得一个含有@字符的串,就将全文进行 split 切割,十分浪费资源,特别是邮箱字符串之后还存在着大段文字的时候。
新的思路:首先定位@的位置,然后确定其附近两个空格的位置,以此获取所需要的邮箱地址信息。
例 6-3 从长字符串中解析出电子邮箱地址

import string
sentence = '''If you have any question
, please contact me via [email protected], Thanks.
'''

emailSign = sentence.find("@")
emailEnd = sentence.find(" ", emailSign)  # 第2锚点
temp = sentence[emailSign::-1]
emailBegin = len(sentence) - (temp.find(" ") + len(sentence) - emailSign)
email = sentence[emailBegin:emailEnd]
email = email.strip(string.punctuation)

print(email)

7.1 认识列表

列表和元组是 Python 中重要的组合型数据类型。其中列表是可变类型,它可以存储各种类型的元素,并实现增删改。而元组是不可变类型,可以用来存储无需修改的对象。
列表作为一种有序序列,有诸多特性和字符串类似,如:

  1. 列表中的元素可以用整数索引,起点为 0
  2. 列表的切片访问方式和字符串相同,基本特征是左闭右开
  3. 列表适用“+”、“+=”、“”、“=”
  4. 列表是可迭代对象,可以使用 for 循环遍历列表对象
  5. 列表可以比较,方式和字符串比较类似,逐元素对比

创建列表,主要有以下两种方式:
一是描述法,直接用定界符 [] 把若干元素封装起来,如:

list1 = []
list2 = [1, 2, 5]

二是构造函数法,即使用 list 函数将其他类型对象转换为列表,如:

list3 = list()
list4 = list("apple")
list5 = list(range(8))

print(list3, list4, list5)

>> [] ['a', 'p', 'p', 'l', 'e'] [0, 1, 2, 3, 4, 5, 6, 7] 

7.2 作为可变对象的列表

对象的可变性是 Python 数据的重要特性,前面所讲的简单类型如整数、浮点数、布尔型都是不可变(immutable)对象,而列表是可变(mutable)对象。
注:同样是序列类型的字符串是不可变的,意味着字符串一旦创建完毕,就不可修改。

修改列表中的元素,如:

list1 = [1, 2, 5]
print(list1)
list1[0] = 9
print(list1)

>> [1, 2, 5]
>> [9, 2, 5]

修改字符串中的元素,如:

string1 = "apple"
string1[0] = "z"

>> TypeError: 'str' object does not support item assignment

7.3 列表的增删改操作

7.3.1 列表的运算和成员检查

列表支持 +、+=、=操作,分别表示连接和延展。与字符串类似。

列表支持 in、not in 这样的成员检查,如:

list1 = [1, 2, 3]
print(4 in list1, 4 not in list1)

>> False True

7.3.2 列表的增操作

列表的 append 方法,可在列表的末尾添加1个元素。
insert 方法,在列表的指定位置添加1个元素。
extend 方法,把另一个列表合并到当前列表的尾部。

append 方法,如:

list1 = [1, 2, 3]
list1.append(4)
print(list1)

>> [1, 2, 3, 4]

insert 方法,如:

list1 = [1, 2, 3]
list1.insert(1, 4)
print(list1)

>> [1, 4, 2, 3]

extend 方法,如:

list1 = [1, 2, 3]
list2 = [4, 5, 6]
list1.extend(list2)
print(list1)

>> [1, 2, 3, 4, 5, 6]

7.3.3 列表的删操作

列表的 remove 方法,可删除列表中指定的值,当存在多个值时只删除第一个,当值不存在时报错。
pop 方法可删除指定位置的元素,参数为index。未列明参数时,默认删除最后一个元素。
clear 方法可清除列表的所有对象,使列表变为空的列表,无参数。
此外,还可以使用 del 语句来删除指定的元素或整个列表。

A = list("123456")
A.remove("2")
print(A)
C = A.pop(0)
print(A)
del A[0:2]
print(A)

>> ['1', '3', '4', '5', '6']
>> ['3', '4', '5', '6']
>> ['5', '6']

7.3.4 列表的改操作

要修改列表,只要通过索引的方式定位元素,然后重新赋值即可。

A = list("123456")
A[0] = 777
print(A)

>> [777, '2', '3', '4', '5', '6']

B = list("123456")
B[-1:-3:-1] = 5, 6
print(B)

>> ['1', '2', '3', '4', 6, 5]

C = list("123456")
C[-1:-3:-1] = 5, 6, 7
print(C)

>> ValueError: attempt to assign sequence of size 3 to extended slice of size 2

2021/2/19

例 7-2 使用冒泡排序法对 [14, 77, 84, 10, 3, 99] 进行从小到大的排序。

def bubbleSort(listX):
    length = len(listX)
    for i in range(0, length-1):
        for j in range(0, length-1-i):
            if listX[j] > listX[j+1]:
                listX[j], listX[j+1] = listX[j+1], listX[j]


list1 = [14, 77, 84, 10, 3, 99]
bubbleSort(list1)
print(list1)

>> [3, 10, 14, 77, 84, 99]

函数执行效果和预期相同,但是本代码存在资源浪费,因为经过不长的几轮循环后,排序事实上已经完成,但CPU仍在空跑。可以加入判断排序是否已经完成的标志位,一旦为True,则及时退出函数,如:

# 整轮对调循环都没有发生一次对调,则认为排序已经完成
def bubbleSort(listX):
    length = len(listX)
    for i in range(0, length-1):
        isSwap = False
        for j in range(0, length-1-i):
            if listX[j] > listX[j+1]:
                listX[j], listX[j+1] = listX[j+1], listX[j]
                isSwap = True
        if not isSwap:
            return


list1 = [14, 77, 84, 10, 3, 99]
bubbleSort(list1)
print(list1)

>> [3, 10, 14, 77, 84, 99]

7.4 列表的方法

使用 count 方法可以获取指定元素出现的次数,如:

list1 = [1, 2, 5, 2]
print(list1.count(1))
print(list1.count(2))

>> 1
>> 2

列表的 index 方法可以获取指定元素首次出现的位置,如:

list1 = [1, 2, 5, 2]
print(list1.index(1))
print(list1.index(2))

>> 0
>> 1

使用 sort 方法对列表进行排序,操作完成之后,列表本身被修改。若要保持原列表不变,同时得到排序之后的新列表,可以使用 sorted 函数。

list1 = [1, 2, 5, 2]
list1.sort()
print(list1)

>>[1, 2, 2, 5]

list1.sort(reverse = True)  # 降序
print(list1)

>> [5, 2, 2, 1]

list2 = sorted(list1)
print(list1)
print(list2)

>> [5, 2, 2, 1]
>> [1, 2, 2, 5]

7.5 遍历列表

在编程中,常需要遍历列表。方式1:根据索引访问,方式2:不使用索引访问,如:

list1 = [1, 2, 5, 2]
# 方式1:
for i in range(len(list1)):
    print(list1[i], end='')

# 方式2:
for x in list1:
    print(x, end='')

7.6 列表的复制:深拷贝和浅拷贝

对于列表 list1 而言,可以使用 [:]、s.copy、copy.copy(list1)、copy.deepcopy(list1)实现列表的复制,现探究这些操作的区别。

和原数据是否指向同一对象 第一层为基本数据类型 原数据中包含子对象(如二维列表)
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变不会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变不会使原数据一同改变 改变不会使原数据一同改变
  1. 赋值操作(改变 list1、list2 中的一个,另一个随之改变)
list1 = [1, 2, 3]
list2 = list1
  1. 实施浅层拷贝的有:切片方式 [:] 、copy、copy.copy
  • 当原数据中不包含子对象时,改变 list1、list2 中的一个,另一个不会改变
list1 = [1, 2, 3]
list2 = list1[:]
print(list1, list2)
list1[2] = 5
print(list1, list2)

>> [1, 2, 3] [1, 2, 3]
>> [1, 2, 5] [1, 2, 3]
  • 当原数据中包含子对象时,改变 list1、list2 中的一个,另一个随之改变
list1 = [1, 2, [3, 4]]
list2 = list1[:]
print(list1, list2)
list1[2][0] = 5
print(list1, list2)

>> [1, 2, [3, 4]] [1, 2, [3, 4]]
>> [1, 2, [5, 4]] [1, 2, [5, 4]]

注: 列表和字符串的 [:] 不完全相同,列表是浅拷贝,字符串是引用

  1. copy.deepcopy 深拷贝
  • 改变 list1、list2 中的一个,另一个不会改变
import copy

list1 = [1, 2, [3, 4]]
list2 = copy.deepcopy(list1)
print(list1, list2)
list1[2][0] = 5
print(list1, list2)

>> [1, 2, [3, 4]] [1, 2, [3, 4]]
>> [1, 2, [5, 4]] [1, 2, [3, 4]]

2021/2/22

7.7 列表推导式

列表推导式是代替复杂循环的一种简洁语法形式。各语句之间是嵌套关系,左边第二个语句是最外层,依次往右进一层,左边第一条语句是最后一层(遵从从外而内的写法)。

list1 = []
for i in range(1, 11):
    list1.append(i**2)
print(list1)

>> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

上例的 for 循环可改写为:

list1 = [x**2 for x in range(1,11)]
print(list1)

>> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

例 7-3 生成公差为 0.65 的等差序列,从 3 开始,共 7 个元素

list1 = [3+x*0.65 for x in range(7)]
print(list1)

>> [3.0, 3.65, 4.3, 4.95, 5.6, 6.25, 6.9]

例 7-4 带条件过滤的列表推导式

list1 = [1, 2, 3, 4, 5, 6, 7, 8]
list2 = [x for x in list1 if x % 2 != 0]
print(list2)

>> [1, 3, 5, 7]
list1 = [x * x for x in range(1, 11) if x % 2 == 0]
print(list1)

>> [4, 16, 36, 64, 100]

例 7-5 使用列表推导式构造笛卡尔积

list1 = [m+n for m in 'ABC' for n in 'XYZ']
print(list1)

>> ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

例 7-6 搭配方法调用的列表推导式

list1 = ['Hello', 'World']
list2 = [s.lower() for s in list1]
print(list2)

>> ['hello', 'world']

例 7-7 将某列表中的所有元素 0 ,均改为 zero ,其余不变

import random
list1 = [random.randint(-3, 3) for x in range(20)]
print(list1)
list1 = ['zero' if x == 0 else x for x in list1]
print(list1)

>> [0, 2, -2, 2, 3, -3, 1, 1, -1, -2, 0, 3, 0, -1, -3, 0, 2, 3, -1, 2]
>> ['zero', 2, -2, 2, 3, -3, 1, 1, -1, -2, 'zero', 3, 'zero', -1, -3, 'zero', 2, 3, -1, 2]

# 另一种方案,目前看不懂什么意思
import random
list1 = [random.randint(-3, 3) for x in range(20)]
print(list1)
list1 = map((lambda x: 'zero' if x == 0 else x), list1)
print(list1)

>> [-3, -1, 1, 3, -3, -3, -1, 0, 2, -1, -3, -2, -2, -3, 1, 3, -3, 1, 3, -3]
>> <map object at 0x000001C26C216700>

例 7-8 矩阵的转置,如:

matrix = [[1,2,3], [4,5,6]]
matrix = [[matrix[r][c] for r in range(len(matrix))] for c in range(len(matrix[0]))]
# matrix[0] == [1,2,3]
print(matrix)

>> [[1, 4], [2, 5], [3, 6]]

这段代码通过双重 for 循环构造了嵌套的列表推导式,最终实现了转置。但是,当代码变得复杂的时候,我们要评估使用这种“高端”写法的必要性,这种写法的优点显而易见——简洁,缺点也显而易见——增加程序设计时间且降低程序可读性。

例 7-9 结构扁平化(将列表内部元素无差别的展开)

# list1 中含有元组、列表、字典(均为sublist1)
list1 = [(1,2), [3,4,5], (6,7), {8,9}]
list2 = [x for sublist1 in list1 for x in sublist1]
print(list2)

>> [1, 2, 3, 4, 5, 6, 7, 8, 9]

构造嵌套列表推导式的方法非常常用,但要注意两个 for 循环的顺序,应遵从从外而内的写法。本例中,外就是 for sublist1 in list1,内就是 for x in sublist1

2021/3/7

7.8 认识元组

元组是Python内置的又一重要数据结构,与列表不同,元组是不可变的(一旦创建,其内部元素便不能修改)。从形式上看,元组使用()将元素括起,元素之间用逗号分隔,元组中的元素可以是单一的类型也可以是混杂的类型。如果在程序中确定某序列无须改变,则建议采用元组以保障程序高效可靠。

A = (1,)  #只有一个元素时必须加逗号,否则表示整数1
B = (1, 2)
print(A, B)

>> (1,) (1, 2)

可以使用 tuple 函数将序列转化为元组

A = tuple()  #创建空的元组
B = tuple("Anaconda")  #('A', 'n', 'a', 'c', 'o', 'n', 'd', 'a')
C = tuple(range(2, 8))
print(A, B, C)

>> () ('A', 'n', 'a', 'c', 'o', 'n', 'd', 'a') (2, 3, 4, 5, 6, 7)

例 7-10 测试元组和列表的访问性能

import timeit, sys 
N = 100_0000
#它接受一个参数为每个测试中调用被计时语句的次数,默认为一百万次;返回所耗费的秒数。
t1 = timeit.timeit('A = [1, 2, "OK", False]', number=N)  
t2 = timeit.timeit('B = (1, 2, "OK", False)', number=N)
print(t1, t2)
>> 0.1182347 0.02775139999999998

s1 = sys.getsizeof([1, 2, "OK", False])
s2 = sys.getsizeof((1, 2, "OK", False))
print(s1, s2)
>> 88 72

值得一提的是,当元组的元素为列表时,这个列表中的元素可以被修改,从而以一种特别的方式修改元组

A = (1, 3, [1, 5], 4)
A[2][0] = 3
print(A)

>> (1, 3, [3, 5], 4)

此外,元组变量可以被整体重新赋值,这是显然的。除了元组的内容不可改变之外,元组的操作和列表有如下相似之处:

  1. 用 + 表示元组的连接,* 表示元组的扩展
  2. 用 del 语句删除一个元组(del 的作用是撕掉标签)
  3. 元组也是可迭代对象,可以用 for 循环遍历
  4. 元组的 count 方法、index 方法和列表类似

7.9 生成器表达式

前面所学的由 [] 定界的列表推导式实现了把冗长的循环语句简化,但其存在耗占内存过多的问题,为此Python提供另一种生成器(generator)表达式解决方案,既实现了形式上的简洁,又具有生成器不耗占内存的优势。

[x**2 for x in range(1, 11)] 外面的 [] 改为 () 即可

A = (x**2 for x in range(1, 11))
print(list(A))
print(list(A))  #第二次访问时为空,这是因为生成器具有重要的特性——一旦被访问即被消耗

>> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>> [] 

2021/3/16

8.1 认识字典

字典是另一种可变容器模型,且可存储任意类型对象。字典的访问速度比列表更快,但其所占据的空间比列表更大。字典的每个键值 key=>value 对用冒号 “:” 分割,每个对之间用逗号“,” 分割,整个字典包括在花括号 {} 中 ,格式如下所示:

dict1 = {key1:value1, key2:value2, key3:value3, ...}
dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}

print("dict['Name']: ", dict['Name'])
>> dict['Name']:  Runoob
print("dict['Age']: ", dict['Age'])
>> dict['Age']:  7
  1. 可以使用数字、字符串或元组作为键(key),但字典中的键必须唯一,且是不可变对象。
  2. 字典中的值可以是任意数据类型
  3. 字典中的项不存在顺序,依次取得的顺序未必和创建时一致

8.1.1 字典的创建

字典的创建可以采用 {} 定界描述的方法,也可以采用 dict 函数构造的方法(dict 可以把形如字典形式的列表或元组转换为字典)。如:

# {} 定界描述的方法
team = {"赵":22, "钱":34, "孙":28}
# dict 函数构造的方法
team = dict(=22,=34,=28)

8.1.2 字典的访问

字典的 keys 方法返回字典的键列表
字典的 values 方法返回字典的值列表
字典的 items 方法返回字典的 键值对 列表

dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}

print("dict['Name']: ", dict['Name'])

8.1.3 字典的编辑

dict = {'Name': 'Runoob', 'Age': 7, 'Class': 'First'}
dict["Name"] = "Bamboo"
print(dict["Name"])
>> Bamboo

字典的 dt1.update(dt2) 方法把字典 dt2 的数据(键值对)更新到调用者字典中
字典的 dt1.pop(“Name”) 方法删除指定的键值对
字典的 dt1.clear() 方法清除字典的数据

例 8-2 统计一段字符中各个字符出现的个数,为达成这一需求,固然也可以使用链式判断的方法,或者列表存储的方法,但是过程都比较繁琐,因为事前并不确定字符串中可能存在的哪些字符。

方式1:对字符进行逐个判断,若不在字典中则设置为1,若已在字典中则将值加1

sentence = "where there is a will, there is a way"
d = {}
for c in sentence:
    if c not in d:  # 按”键“判断其是否存在
        d[c] = 1
    else:
        d[c] += 1
print(d.items())
>> dict_items([('w', 3), ('h', 3), ('e', 6), ('r', 3), (' ', 8), ('t', 2), ('i', 3), ('s', 2), ('a', 3), ('l', 2), (',', 1), ('y', 1)])

方式2:利用了 get 方法的特性,不存在时返回缺省值 0,存在时返回实际值

sentence = "where there is a will, there is a way"
d = {}
for c in sentence:
    d[c] = d.get(c, 0) + 1
print(d.items())
>> dict_items([('w', 3), ('h', 3), ('e', 6), ('r', 3), (' ', 8), ('t', 2), ('i', 3), ('s', 2), ('a', 3), ('l', 2), (',', 1), ('y', 1)])

2021/3/8

10.1 使用 enumerate 函数枚举对象

在编写循环结构时,如果仅展示序列对象的元素,可以使用 for…in…循环结构,如果需要同时展示元素的序号以及元素的内容,可使用如下代码:

A = [1, 2, "345"]
for i, name in enumerate(A):
    print(i+1, name)

>> 1 1
>> 2 2
>> 3 345

10.2 使用 product 函数扁平化循环

多重循环时,可以使用 product 函数构造笛卡尔积,从而减少嵌套的层数,即将多重循环扁平化。
product(list1,list2)依次取出list1中每1个元素,与list2中的每1个元素,组成元组,将所有元组组合成一个列表返回。

from itertools import product
A = list(product([1,2,5], [8,3]))
print(A)

>> [(1, 8), (1, 3), (2, 8), (2, 3), (5, 8), (5, 3)]

例 10-1 鸡兔同笼问题

# 原方法:
def calcCR(head, foot):
    for chick in range(1, head + 1):
        for rabbit in range(1, head + 1):
            if chick + rabbit == 35 and chick * 2 + rabbit * 4 == 94:
                return chick, rabbit

# 改写后的方法:
from itertools import product
def calcCR_new(head, foot):
    for chick,rabbit in product(range(1,head+1), range(1,head+1)):
        if chick+rabbit == head and chick*2 + rabbit*4 == foot:
            return chick,rabbit
print(calcCR_new(10, 30))

>> (5, 5)

10.3 使用 any/all 函数替代循环

编程实践中,常须对序列的成员构成情况进行判断。这样的场景下,善用 any 和 all 函数可以编写出简洁、可靠的代码。
例 10-2 使用 any 函数判断成员情况

A = [7, 25, 36, 18, 31]
print(any(10 <= i <= 20 for i in A))

>> True

例 10-3 使用 all 函数判断成员情况
例子有误

2021/3/13

10.4 使用 exec 函数

前面所学的 eval 函数的功能是求形如字符串的表达式的值,exec 函数的功能是执行一段程序代码。相比于 eval 而言,exec 可以执行更复杂的 Python 代码。

A = eval("3+3")
print(A)
>> 6

exec('print("Hello world")')
exec("print(3+3)")
exec("""for i in range(5):
        print("iter time: %d" %i)
        """)
>> Hello world
6
iter time: 0
iter time: 1
iter time: 2
iter time: 3
iter time: 4

10.5 字典和集合推导式

P160

10.6 可迭代对象与迭代器

迭代器可以看作是一个特殊的对象,每次调用该对象时会返回自身的下一个元素,从实现上来看,一个迭代器对象必须是定义了__iter__()方法和next()方法的对象。字符串,列表或元组对象等可迭代对象都可用于创建迭代器。

list1 = [1, 2, 3, 4]  # 列表
iter1 = iter(list1)
print(next(iter1))
>> 1
print(next(iter1))
>> 2

遍历迭代器可以使用常规的 for 语句也可以使用 next() 函数:

# 使用常规 for 语句进行遍历
list1 = [1, 2, 3, 4]
iter1 = iter(list1)

for x in iter1:
    print(x, end=" ")
>> 1 2 3 4 

# 使用 next() 函数进行遍历
import sys

list1 = [1, 2, 3, 4]
iter1 = iter(list1)

while True:
    try:
        print(next(iter1))
    except StopIteration:
        sys.exit()
>>
1
2
3
4

创建一个迭代器的方法:
把一个类作为迭代器使用需要在类中实现两个方法:__iter__()__next__()
Python 的构造函数为 init(), 它会在对象初始化的时候执行
__iter__()方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。
__next__() 方法(Python 2 里是 next())会返回下一个迭代器对象。
创建一个返回数字的迭代器,初始值为 1,逐步递增 1:

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))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))

>> 1
2
3
4
5

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


myclass = MyNumbers()
myiter = iter(myclass)

for x in myiter:
    print(x)

2021/3/15

10.7 生成器表达式和函数式生成器

列表推导式使用 [] 定界,元素立即生成,因而占据内存。生成器表达式使用()定界,在系统中保存算法,而非元素,元素惰性生成,仅占据少量内存。
生成器表达式是一种迭代器,可使用for循环遍历,也可以使用next函数寻访,但是寻访之后元素不可复现。
当有待构造的序列数目较大时,应考虑使用生成器表达式。

from collections.abc import Iterable, Iterator
import sys

list1 = [x*x for x in range(10)]
print(isinstance(list1, Iterable))  # 列表是可迭代对象
print(isinstance(list1, Iterator))  # 列表不是迭代器
generator1 = (x*x for x in range(10))
print(isinstance(generator1, Iterable))  # 生成器是可迭代对象
print(isinstance(generator1, Iterator))  # 列表不是迭代器
print(sys.getsizeof(list1), sys.getsizeof(generator1))  # 184 112

构造惰性生成的 generator 符合Python风格,但是普通的 generator 功能有限,难以实现复杂的逻辑。如需构造斐波那契序列那样有着复杂逻辑的序列时,可以使用函数生成器。
函数生成器这样构造:
将普通定义的函数中输出元素的关键语句用yield改写,yield意味着收获,其取得的一系列元素就是蕴含在生成器对象中的一个又一个元素。

  1. 普通求解斐波那契序列的函数:
# 普通求解斐波那契序列的函数
def fib(num):
    n = 1
    a, b = 0, 1
    while n <= num:
        print(b)
        a, b = b, a+b
        n = n + 1
    return "done"

print(fib(5))

>> 
1
1
2
3
5
done
  1. 将上述 print 函数改为 yield 语句,整个代码就构造了函数式生成器
def fib(num):
    n = 1
    a, b = 0, 1
    while n <= num:
        yield b  # yield 语句
        a, b = b, a+b
        n = n + 1
    return "done"

print(fib(5))  # 
print(list(fib(5)))  #[1, 1, 2, 3, 5]
>>
<generator object fib at 0x000001FD06B7C9E0>
[1, 1, 2, 3, 5]

函数生成器是一个对象,这个对象常常被 for 循环遍历访问,或被 list 函数展开。这两者的本质都是调用 __next__()方法访问对象的一个又一个元素。函数生成器被寻访的过程中,遇到 yield 就输出一个元素并中断,指导下次被 __next__()方法访问时继续执行,在这一过程中 return 语句被架空,并不会执行。最后,即所有的数据都被取光时,报 StopIteration 错,此时函数生成器中的 return 语句执行。

例 10-7 使用函数生成器构造无限序列。对于 蕴含无数个元素的序列,断然是无法用[]()来描述表示的,但可以用函数生成器构造他们,这进一步说明函数生成器存储的不是实体,而是算法。

def getn():
    n = 1
    while True:
        yield n
        n += 1
generator1 = getn()
print(next(generator1))
print(next(generator1))
print(next(generator1))
>>
1
2
3

例 10-8 设计模仿 map 函数的 myMap 和 myNewMap 函数

  1. 使用系统 map 函数
ori = range(10)
# 方式1,使用系统 map
result1 = map(lambda x: x*x, ori)
print(list(result1))
  1. 使用自己创建的 myMap函数(不是惰性计算,因而内存占据量大,内存占用:184字节)
def myMap(func, num):
    rst = []
    for x in range(num):
        rst.append(func(x))
    return rst


rst2 = myMap(lambda x: x*x, 10)
print(list(rst2))
>> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  1. 在 myMap 函数的基础上进行改进,得到 myNewMap 函数(内存占用:112字节)
def myNewMap(func, li):
    for x in li:
        yield func(x)


ori = range(10)
rst3 = myNewMap(lambda x: x*x, ori)
print(list(rst3))

>> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

10.8 迭代工具模块 itertools

itertools 模块中,配置了很多有用的工具。如:
itertools.combinations():获得对象的组合,即不考虑元素顺序
itertools.permutations():获得对象的排列,即考虑元素顺序
itertools.groupby():对连续的元素进行分组

12.1 多维数组 ndarray 的创建

12.1.1 使用 array 函数

12.2 索引和切片

12.2.2 多维数组的索引和切片

不同维度的切片中间用 逗号 隔开

import numpy as np
A = np.arange(9).reshape(3,3)
print(A)
>> 
[[0 1 2]
 [3 4 5]
 [6 7 8]]
print(A[0:1, :])  # 输出[0,1) 行,所有列,等同于输出第0行。
>> [[0 1 2]]
print(A[0:1, 0:2])  # 输出[0,1) 行,[0,2)列,等同于输出第0行的第0、1列。
>> [[0 1]]

对于二维数组而言,当两个维度的切片方式都为整数时,得到标量;当两个维度中间有一个整数时,得到的是一维数组;当两个维度中都含有:时,得到二维数组。

你可能感兴趣的:(python,开发语言,后端)