python基础入门教程

我终生的等候,换不来你刹那的凝眸

Posted by yishuifengxiao on 2021-05-20

一 基础入门

1.1 变量和标识符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Python中使用变量,不需要声明,直接为变量赋值即可
a = 10

# 不能使用没有进行过赋值的变量
# 如果使用没有赋值过的变量,会报错 NameError: name 'b' is not defined
# print(b)

# Python是一个动态类型的语言,可以为变量赋任意类型的值,也可以任意修改变量的值
a = 'hello'

# print(a)

# 标识符
# 在Python中所有可以自主命名的内容都属于标识符
# 比如:变量名、函数名、类名
# 标识符必须遵循标识符的规范
# 1.标识符中可以含有字母、数字、_,但是不能使用数字开头
# 例子:a_1 _a1 _1a
# 2.标识符不能是Python中的关键字和保留字
# 也不建议使用Python中的函数名作为标识符,因为这样会导致函数被覆盖
# 3.命名规范:
# 在Python中注意遵循两种命名规范:
# 下划线命名法
# 所有字母小写,单词之间使用_分割
# max_length min_length hello_world xxx_yyy_zzz
# 帕斯卡命名法(大驼峰命名法)
# 首字母大写,每个单词开头字母大写,其余字母小写
# MaxLength MinLength HelloWorld XxxYyyZzz
#
# 如果使用不符合标准的标识符,将会报错 SyntaxError: invalid syntax

_b123 = 20
# print(_b123)

# print = 123
# print(print)

1.2 数值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 在Python数值分成了三种:整数、浮点数(小数)、复数
# 在Python中所有的整数都是int类型
a = 10
b = 20
# Python中的整数的大小没有限制,可以是一个无限大的整数
# c = 999999999999999999999999999999999999999999999 ** 100

# 如果数字的长度过大,可以使用下划线作为分隔符
c = 123_456_789

# d = 0123 10进制的数字不能以0开头
# 其他进制的整数,只要是数字打印时一定是以十进制的形式显示的
# 二进制 0b开头
c = 0b10 # 二进制的10
# 八进制 0o开头
c = 0o10
# 十六进制 0x开头
c = 0x10

# 也可以通过运算符来对数字进行运算,并且可以保证整数运算的精确
c = -100
c = c + 3

# 浮点数(小数),在Python中所有的小数都是float类型
c = 1.23
c = 4.56

# 对浮点数进行运算时,可能会得到一个不精确的结果
c = 0.1 + 0.2 # 0.30000000000000004

print(c)

1.3 字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 字符串(str)
# 字符串用来表示一段文本信息,字符串是程序中使用的最多的数据类型
# 在Python中字符串需要使用引号引起来

s = 'hello'
# s = abc # 字符串必须使用引号引起来,不使用不是字符串
# 引号可以是双引号,也可以是单引号,但是注意不要混着用
s = 'hello'
s = "hello"
# s = 'hello" 引号不能混合使用 SyntaxError: EOL while scanning string literal

# 相同的引号之间不能嵌套
# s = "子曰:"学而时习之,乐呵乐呵!""
s = '子曰:"学而时习之,乐呵乐呵!"'

长字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 长字符串
# 单引号和双引号不能跨行使用
s = '锄禾日当午,\
汗滴禾下土,\
谁知盘中餐,\
粒粒皆辛苦'

# 使用三重引号来表示一个长字符串 ''' """
# 三重引号可以换行,并且会保留字符串中的格式

s = '''锄禾日当午,
汗滴禾下土,
谁知盘中餐,
粒粒皆辛苦'''

转义字符

1
2
3
4
5
6
7
8
9
10
11
12
13
# 转义字符
# 可以使用 \ 作为转义字符,通过转义字符,可以在字符串中使用一些特殊的内容
# 例子:
# \' 表示'
# \" 表示"
# \t 表示制表符
# \n 表示换行符
# \\ 表示反斜杠
# \uxxxx 表示Unicode编码
s = "子曰:\"学而时习之,\\\\n乐呵乐呵!\""

s = '\u2250'
print(s)

1.3.1 格式化字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 格式化字符串
a = 'hello'

# 字符串之间也可以进行加法运算
# 如果将两个字符串进行相加,则会自动将两个字符串拼接为一个
a = 'abc' + 'haha' + '哈哈'
# a = 123
# 字符串只能不能和其他的类型进行加法运算,如果做了会出现异常 TypeError: must be str, not int
# print("a = "+a) # 这种写法在Python中不常见
a = 123
# print('a =',a)

# 在创建字符串时,可以在字符串中指定占位符
# %s 在字符串中表示任意字符
# %f 浮点数占位符
# %d 整数占位符
b = 'Hello %s'%'孙悟空'
b = 'hello %s 你好 %s'%('tom','孙悟空')
b = 'hello %3.5s'%'abcdefg' # %3.5s字符串的长度限制在3-5之间
b = 'hello %s'%123.456
b = 'hello %.2f'%123.456
b = 'hello %d'%123.95
b = '呵呵'

# print('a = %s'%a)

# 格式化字符串,可以通过在字符串前添加一个f来创建一个格式化字符串
# 在格式化字符串中可以直接嵌入变量
c = f'hello {a} {b}'

print(f'a = {a}')

# 练习 创建一个变量保存你的名字,然后通过四种格式化字符串的方式
# 在命令行中显示,欢迎 xxx 光临!

1.3.2 复制字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建一个变量来保存你的名字
name = '孙悟空'

# 使用四种方式来输出,欢迎 xxx 光临
# 拼串
print('欢迎 '+name+' 光临!')
# 多个参数
print('欢迎',name,'光临!')
# 占位符
print('欢迎 %s 光临!'%name)
# 格式化字符串
print(f'欢迎 {name} 光临!')

# 字符串的复制(将字符串和数字相乘)
a = 'abc'
# * 在语言中表示乘法
# 如果将字符串和数字相乘,则解释器会将字符串重复指定的次数并返回
a = a * 20

print(a)

1.4 布尔值与空值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 布尔值(bool)
# 布尔值主要用来做逻辑判断
# 布尔值一共有两个 True 和 False
# True表示真 False表示假
a = True
a = False
# print('a =',a)

# 布尔值实际上也属于整型,True就相当于1,False就相当于0
# print(1 + False)

# None(空值)
# None专门用来表示不存在
b = None
print(b)

1.5 类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 通过类型检查,可以检查只能值(变量)的类型

a = 123 # 数值
b = '123' # 字符串

# print('a =',a)
# print('b =',b)、

# type()用来检查值的类型
# 该函数会将检查的结果作为返回值返回,可以通过变量来接收函数的返回值
c = type('123')
c = type(a)
# print(type(b))
print(type(1)) # <class 'int'>
print(type(1.5)) # <class 'float'>
print(type(True)) # <class 'bool'>
print(type('hello')) # <class 'str'>
print(type(None)) # <class 'NoneType'>

1.6 类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 类型转换四个函数 int() float() str() bool()
# int() 可以用来将其他的对象转换为整型
# 规则:
# 布尔值:True -> 1 False -> 0
# 浮点数:直接取整,省略小数点后的内容
# 字符串:合法的整数字符串,直接转换为对应的数字
# 如果不是一个合法的整数字符串,则报错 ValueError: invalid literal for int() with base 10: '11.5'
# 对于其他不可转换为整型的对象,直接抛出异常 ValueError
# float() 和 int()基本一致,不同的是它会将对象转换为浮点数
# str() 可以将对象转换为字符串
# True -> 'True'
# False -> 'False'
# 123 -> '123'
# 。。。
# bool() 可以将对象转换为布尔值,任何对象都可以转换为布尔值
# 规则:对于所有表示空性的对象都会转换为False,其余的转换为True
# 哪些表示的空性:0 、 None 、 '' 。。。

a = True

# 调用int()来将a转换为整型
# int()函数不会对原来的变量产生影响,他是对象转换为指定的类型并将其作为返回值返回
# 如果希望修改原来的变量,则需要对变量进行重新赋值
a = int(a)

a = False
a = int(a)

a = '123'
a = int(a)

a = 11.6
a = int(a)

a = '11.5'
# a = int(a)

a = None
# a = int(a)

a = 1
a = float(a)

a = False
a = float(a)

a = 123
a = str(a)

a = None
a = bool(a)

print('a =',a)
print('a的类型是',type(a))
# b = 456
# print('hello'+str(b))

1.7 运算符

1.7.1 条件运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 条件运算符(三元运算符)
# 语法: 语句1 if 条件表达式 else 语句2
# 执行流程:
# 条件运算符在执行时,会先对条件表达式进行求值判断
# 如果判断结果为True,则执行语句1,并返回执行结果
# 如果判断结果为False,则执行语句2,并返回执行结果

# print('你好') if False else print('Hello')

a = 30
b = 50

# print('a的值比较大!') if a > b else print('b的值比较大!')
# 获取a和b之间的较大值
max = a if a > b else b

print(max)

1.7.2 运算符的优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
a = 40
b = 50
c = 30

# 通过条件运算符获取三个值中的最大值
# max = a if a > b else b
# max = max if max > c else c

max = a if (a > b and a > c) else (b if b > c else c) # 不推荐这么使用
# max = a if (b < a > c) else (b if b > c else c)

# print(max)

# 运算符的优先级
# 和数学中一样,在Python运算也有优先级,比如先乘除 后加减
# 运算符的优先级可以根据优先级的表格来查询,
# 在表格中位置越靠下的运算符优先级越高,优先级越高的越优先计算
# 如果优先级一样则自左向右计算
# 关于优先级的表格,你知道有这么一个东西就够了,千万不要去记
# 在开发中如果遇到优先级不清楚的,则可以通过小括号来改变运算顺序
a = 1 + 2 * 3

# 一样 and高 or高
# 如果or的优先级高,或者两个运算符的优先级一样高
# 则需要先进行或运算,则运算结果是3
# 如果and的优先级高,则应该先计算与运算
# 则运算结果是1
a = 1 or 2 and 3

# print(a)

# 逻辑运算符(补充)
# 逻辑运算符可以连着使用
result = 1 < 2 < 3 # 相当于 1 < 2 and 2 < 3
result = 10 < 20 > 15

print(result)

二 流程控制

2.1 条件判断

缩进有两种方式,一种是使用tab键,一种是使用空格(四个)
Python的官方文档中推荐我们使用空格来缩进
Python代码中使用的缩进方式必须统一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 条件判断语句(if语句)
# 语法:if 条件表达式 :
# 代码块
# 执行的流程:if语句在执行时,会先对条件表达式进行求值判断,
# 如果为True,则执行if后的语句
# 如果为False,则不执行
# 默认情况下,if语句只会控制紧随其后的那条语句,如果希望if可以控制多条语句,
# 则可以在if后跟着一个代码块
# 代码块
# 代码块中保存着一组代码,同一个代码块中的代码,要么都执行要么都不执行
# 代码块就是一种为代码分组的机制
# 如果要编写代码块,语句就不能紧随在:后边,而是要写在下一行
# 代码块以缩进开始,直到代码恢复到之前的缩进级别时结束


# if False : print('你猜我出来么?')

num = 10
# if num > 10 : print('num比10大!')
# print('谁也管不了我')

if False :
print(123)
print(456)
print(789)
print(101112)
# print('hello')

num = 28

# 可以使用逻辑运算符来连接多个条件,
# 如果希望所有条件同时满足,则需要使用and
# 如果希望只要有一个条件满足即可,则需要使用or
if num > 10 and num < 20 :
print('num比10大,num比20小!')

if 10 < num < 20 :
print('num比10大,num比20小!')

2.1.1 if-else判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 让用户在控制台中输入一个年龄
# age = int(input('请输入你的年龄:'))

# 如果用户的年龄大于18岁,则显示你已经成年了
# if age >= 18 :
# print('你已经成年了~~~')

# if-else语句
# 语法:
# if 条件表达式 :
# 代码块
# else :
# 代码块
# 执行流程:
# if-else语句在执行时,先对if后的条件表达式进行求值判断
# 如果为True,则执行if后的代码块
# 如果为False,则执行else后的代码块

age = 7
if age > 17 :
print('你已经成年了~~')
else :
print('你还未成年~~')

2.1.2 if-elif-else判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# if-elif-else语句
# 语法:
# if 条件表达式 :
# 代码块
# elif 条件表达式 :
# 代码块
# elif 条件表达式 :
# 代码块
# elif 条件表达式 :
# 代码块
# else :
# 代码块
#
# 执行流程:
# if-elif-else语句在执行时,会自上向下依次对条件表达式进行求值判断,
# 如果表达式的结果为True,则执行当前代码块,然后语句结束
# 如果表达式的结果为False,则继续向下判断,直到找到True为止
# 如果所有的表达式都是False,则执行else后的代码块
# if-elif-else中只会有一个代码块会执行

age = 210

# if age > 200 :
# print('活着可真没劲呢!')
# elif age > 100 :
# print('你也是老大不小了!')
# elif age >= 60 :
# print('你已经退休了!')
# elif age >= 30 :
# print('你已经是中年了!')
# elif age >= 18 :
# print('你已经成年了!')
# else :
# print('你还是个小孩!')

age = 68

if age >= 18 and age < 30 :
print('你已经成年了!')
elif age >= 30 and age < 60 :
print('你已经中年了!')
elif age >= 60 :
print('你已经退休了!')

2.2 循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 循环语句
# 循环语句可以使指定的代码块重复指定的次数
# 循环语句分成两种,while循环 和 for循环
# while循环
# 语法:
# while 条件表达式 :
# 代码块
# else :
# 代码块
# 执行流程:
# while语句在执行时,会先对while后的条件表达式进行求值判断,
# 如果判断结果为True,则执行循环体(代码块),
# 循环体执行完毕,继续对条件表达式进行求值判断,以此类推,
# 直到判断结果为False,则循环终止,如果循环有对应的else,则执行else后的代码块

# 条件表达式恒为True的循环语句,称为死循环,它会一直运行,慎用!
# while True :
# print('hello')

# 循环的三个要件(表达式)
# 初始化表达式,通过初始化表达式初始化一个变量
# i = 0

# # 条件表达式,条件表达式用来设置循环执行的条件
# while i < 10 :
# print(i)
# # 更新表达式,修改初始化变量的值
# i += 1

# 创建一个执行十次的循环
i = 0
while i < 10 :
i += 1
print(i,'hello')
else :
print('else中的代码块')

三 序列

3.1 列表

类似于java中的list
创建列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建列表,通过[]来创建列表
my_list = [] # 创建了一个空列表
# print(my_list , type(my_list))

# 列表存储的数据,我们称为元素
# 一个列表中可以存储多个元素,也可以在创建列表时,来指定列表中的元素
my_list = [10] # 创建一个只包含一个元素的列表

# 当向列表中添加多个元素时,多个元素之间使用,隔开
my_list = [10,20,30,40,50] # 创建了一个保护有5个元素的列表

# 列表中可以保存任意的对象
my_list = [10,'hello',True,None,[1,2,3],print]

# 列表中的对象都会按照插入的顺序存储到列表中,
# 第一个插入的对象保存到第一个位置,第二个保存到第二个位置
# 我们可以通过索引(index)来获取列表中的元素
# 索引是元素在列表中的位置,列表中的每一个元素都有一个索引
# 索引是从0开始的整数,列表第一个位置索引为0,第二个位置索引为1,第三个位置索引为2,以此类推
my_list = [10,20,30,40,50]

通过索引获取列表中的元素

1
2
3
4
5
# 通过索引获取列表中的元素
# 语法:my_list[索引] my_list[0]
# print(my_list[4])
# 如果使用的索引超过了最大的范围,会抛出异常
# print(my_list[5]) IndexError: list index out of range

获取列表的长度

1
2
3
4
# 获取列表的长度,列表中元素的个数
# len()函数,通过该函数可以获取列表的长度
# 获取到的长度的值,是列表的最大索引 + 1
print(len(my_list)) # 5

3.1.1 切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 切片
# 切片指从现有列表中,获取一个子列表
# 创建一个列表,一般创建列表时,变量的名字会使用复数
stus = ['孙悟空','猪八戒','沙和尚','唐僧','蜘蛛精','白骨精']

# 列表的索引可以是负数
# 如果索引是负数,则从后向前获取元素,-1表示倒数第一个,-2表示倒数第二个 以此类推
# print(stus[-2])

# 通过切片来获取指定的元素
# 语法:列表[起始:结束]
# 通过切片获取元素时,会包括起始位置的元素,不会包括结束位置的元素
# 做切片操作时,总会返回一个新的列表,不会影响原来的列表
# 起始和结束位置的索引都可以省略不写
# 如果省略结束位置,则会一直截取到最后
# 如果省略起始位置,则会从第一个元素开始截取
# 如果起始位置和结束位置全部省略,则相当于创建了一个列表的副本
# print(stus[1:])
# print(stus[:3])
# print(stus[:])
# print(stus)

# 语法:列表[起始:结束:步长]
# 步长表示,每次获取元素的间隔,默认值是1
# print(stus[0:5:3])
# 步长不能是0,但是可以是负数
# print(stus[::0]) ValueError: slice step cannot be zero
# 如果是负数,则会从列表的后部向前边取元素
print(stus[::-1])

3.1.2 通用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# + 和 *
# +可以将两个列表拼接为一个列表
my_list = [1,2,3] + [4,5,6]

# * 可以将列表重复指定的次数
my_list = [1,2,3] * 5

# print(my_list)

# 创建一个列表
stus = ['孙悟空','猪八戒','沙和尚','唐僧','蜘蛛精','白骨精','沙和尚','沙和尚']

# in 和 not in
# in用来检查指定元素是否存在于列表中
# 如果存在,返回True,否则返回False
# not in用来检查指定元素是否不在列表中
# 如果不在,返回True,否则返回False
# print('牛魔王' not in stus)
# print('牛魔王' in stus)

# len()获取列表中的元素的个数

# min() 获取列表中的最小值
# max() 获取列表中的最大值
arr = [10,1,2,5,100,77]
# print(min(arr) , max(arr))

# 两个方法(method),方法和函数基本上是一样,只不过方法必须通过 对象.方法() 的形式调用
# xxx.print() 方法实际上就是和对象关系紧密的函数
# s.index() 获取指定元素在列表中的第一次出现时索引
# print(stus.index('沙和尚'))
# index()的第二个参数,表示查找的起始位置 , 第三个参数,表示查找的结束位置
# print(stus.index('沙和尚',3,7))
# 如果要获取列表中没有的元素,会抛出异常
# print(stus.index('牛魔王')) ValueError: '牛魔王' is not in list
# s.count() 统计指定元素在列表中出现的次数
print(stus.count('牛魔王'))

3.1.3 修改元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 创建一个列表
stus = ['孙悟空','猪八戒','沙和尚','唐僧','蜘蛛精','白骨精']

# print("修改前:",stus)
# 修改列表中的元素
# 直接通过索引来修改元素
stus[0] = 'sunwukong'
stus[2] = '哈哈'
# 通过del来删除元素
del stus[2] # 删除索引为2的元素

# print('修改后:',stus)

stus = ['孙悟空','猪八戒','沙和尚','唐僧','蜘蛛精','白骨精']

# print("修改前:",stus)

# 通过切片来修改列表
# 在给切片进行赋值时,只能使用序列
# stus[0:2] = ['牛魔王','红孩儿'] 使用新的元素替换旧元素
# stus[0:2] = ['牛魔王','红孩儿','二郎神']
# stus[0:0] = ['牛魔王'] # 向索引为0的位置插入元素
# 当设置了步长时,序列中元素的个数必须和切片中元素的个数一致
# stus[::2] = ['牛魔王','红孩儿','二郎神']

# 通过切片来删除元素
# del stus[0:2]
# del stus[::2]
# stus[1:3] = []

# print('修改后:',stus)

# 以上操作,只适用于可变序列
s = 'hello'
# s[1] = 'a' 不可变序列,无法通过索引来修改
# 可以通过 list() 函数将其他的序列转换为list
s = list(s)
print(s)

3.1.4 列表的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 列表的方法
stus = ['孙悟空','猪八戒','沙和尚','唐僧']
# print('原列表:',stus)

# append()
# 向列表的最后添加一个元素
# stus.append('唐僧')

# insert()
# 向列表的指定位置插入一个元素
# 参数:
# 1.要插入的位置
# 2.要插入的元素
# stus.insert(2,'唐僧')

# extend()
# 使用新的序列来扩展当前序列
# 需要一个序列作为参数,它会将该序列中的元素添加到当前列表中
# stus.extend(['唐僧','白骨精'])
# stus += ['唐僧','白骨精']

# clear()
# 清空序列
# stus.clear()

# pop()
# 根据索引删除并返回被删除的元素

# result = stus.pop(2) # 删除索引为2的元素
# result = stus.pop() # 删除最后一个
# print('result =',result)

# remove()
# 删除指定值得元素,如果相同值得元素有多个,只会删除第一个
# stus.remove('猪八戒')

# reverse()
# 用来反转列表
# stus.reverse()

# sort()
# 用来对列表中的元素进行排序,默认是升序排列
# 如果需要降序排列,则需要传递一个reverse=True作为参数
my_list = list('asnbdnbasdabd')
my_list = [10,1,20,3,4,5,0,-2]

print('修改前',my_list)

my_list.sort(reverse=True)
print('修改后',my_list)
# print('修改后:',stus)

3.1.5 遍历列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 遍历列表,指的就是将列表中的所有元素取出来
# 创建列表
stus = ['孙悟空','猪八戒','沙和尚','唐僧','白骨精','蜘蛛精']

# 遍历列表
# print(stus[0])
# print(stus[1])
# print(stus[2])
# print(stus[3])

# 通过while循环来遍历列表
# i = 0
# while i < len(stus):
# print(stus[i])
# i += 1

# 通过for循环来遍历列表
# 语法:
# for 变量 in 序列 :
# 代码块
# for循环的代码块会执行多次,序列中有几个元素就会执行几次
# 没执行一次就会将序列中的一个元素赋值给变量,
# 所以我们可以通过变量,来获取列表中的元素

for s in stus :
print(s)

3.2 range函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# range()是一个函数,可以用来生成一个自然数的序列
r = range(5) # 生成一个这样的序列[0,1,2,3,4]
r = range(0,10,2)
r = range(10,0,-1)
# 该函数需要三个参数
# 1.起始位置(可以省略,默认是0)
# 2.结束位置
# 3.步长(可以省略,默认是1)

# print(list(r))

# 通过range()可以创建一个执行指定次数的for循环
# for()循环除了创建方式以外,其余的都和while一样,
# 包括else、包括break continue都可以在for循环中使用
# 并且for循环使用也更加简单
# 将之前使用while循环做的练习,再使用for循环完成一次!
for i in range(30):
print(i)

# for s in 'hello':
# print(s)

3.3 元组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 元组 tuple
# 元组是一个不可变的序列
# 它的操作的方式基本上和列表是一致的
# 所以你在操作元组时,就把元组当成是一个不可变的列表就ok了
# 一般当我们希望数据不改变时,就使用元组,其余情况都使用列表

# 创建元组
# 使用()来创建元组
my_tuple = () # 创建了一个空元组
# print(my_tuple,type(my_tuple)) # <class 'tuple'>

my_tuple = (1,2,3,4,5) # 创建了一个5个元素的元组
# 元组是不可变对象,不能尝试为元组中的元素重新赋值
# my_tuple[3] = 10 TypeError: 'tuple' object does not support item assignment
# print(my_tuple[3])

# 当元组不是空元组时,括号可以省略
# 如果元组不是空元组,它里边至少要有一个,
my_tuple = 10,20,30,40
my_tuple = 40,
# print(my_tuple , type(my_tuple))

my_tuple = 10 , 20 , 30 , 40

# 元组的解包(解构)
# 解包指就是将元组当中每一个元素都赋值给一个变量
a,b,c,d = my_tuple

# print("a =",a)
# print("b =",b)
# print("c =",c)
# print("d =",d)

a = 100
b = 300
# print(a , b)

# 交互a 和 b的值,这时我们就可以利用元组的解包
a , b = b , a

# print(a , b)
my_tuple = 10 , 20 , 30 , 40


# 在对一个元组进行解包时,变量的数量必须和元组中的元素的数量一致
# 也可以在变量前边添加一个*,这样变量将会获取元组中所有剩余的元素
a , b , *c = my_tuple
a , *b , c = my_tuple
*a , b , c = my_tuple
a , b , *c = [1,2,3,4,5,6,7]
a , b , *c = 'hello world'
# 不能同时出现两个或以上的*变量
# *a , *b , c = my_tuple SyntaxError: two starred expressions in assignment
print('a =',a)
print('b =',b)
print('c =',c)

3.4 可变对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# # 可变对象
# a = [1,2,3]
# print('修改前:', a , id(a))

# # 通过索引修改列表
# a[0] = 10
# print('修改后:', a , id(a))

# # 为变量重新赋值
# a = [4,5,6]
# print('修改后:', a , id(a))


a = [1,2,3]
b = a
# b[0] = 10
b = [10,2,3]
# print("a",a,id(a))
# print("b",b,id(b))

# == != is is not
# == != 比较的是对象的值是否相等
# is is not 比较的是对象的id是否相等(比较两个对象是否是同一个对象)

a = [1,2,3]
b = [1,2,3]
print(a,b)
print(id(a),id(b))
print(a == b) # a和b的值相等,使用==会返回True
print(a is b) # a和b不是同一个对象,内存地址不同,使用is会返回False

3.5 字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 字典
# 使用 {} 来创建字典
d = {} # 创建了一个空字典

# 创建一个保护有数据的字典
# 语法:
# {key:value,key:value,key:value}
# 字典的值可以是任意对象
# 字典的键可以是任意的不可变对象(int、str、bool、tuple ...),但是一般我们都会使用str
# 字典的键是不能重复的,如果出现重复的后边的会替换到前边的
# d = {'name':'孙悟空' , 'age':18 , 'gender':'男' , 'name':'sunwukong'}
d = {
'name':'孙悟空' ,
'age':18 ,
'gender':'男' ,
'name':'sunwukong'
}

# print(d , type(d))

# 需要根据键来获取值
# print(d['name'],d['age'],d['gender'])

# 如果使用了字典中不存在的键,会报错
# print(d['hello']) KeyError: 'hello'

3.5.1 字典的使用

创建字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 创建字典
# 使用{}
# 语法:{k1:v1,k2:v2,k3:v3}

# 使用 dict()函数来创建字典
# 每一个参数都是一个键值对,参数名就是键,参数名就是值(这种方式创建的字典,key都是字符串)
d = dict(name='孙悟空',age=18,gender='男')

# 也可以将一个包含有双值子序列的序列转换为字典
# 双值序列,序列中只有两个值,[1,2] ('a',3) 'ab'
# 子序列,如果序列中的元素也是序列,那么我们就称这个元素为子序列
# [(1,2),(3,5)]
d = dict([('name','孙悟饭'),('age',18)])
# print(d , type(d))
d = dict(name='孙悟空',age=18,gender='男')

获取字典中键值对的个数

1
2
3

# len() 获取字典中键值对的个数
# print(len(d))

检查字典中是否包含指定的键
1
2
3
# in 检查字典中是否包含指定的键
# not in 检查字典中是否不包含指定的键
# print('hello' in d)

获取字典中的值
1
2
3
4
5
6
7
8
9
10
11
12
13
# 获取字典中的值,根据键来获取值
# 语法:d[key]
# print(d['age'])

# n = 'name'
# print(d[n])

# 通过[]来获取值时,如果键不存在,会抛出异常 KeyError
# get(key[, default]) 该方法用来根据键来获取字典中的值
# 如果获取的键在字典中不存在,会返回None
# 也可以指定一个默认值,来作为第二个参数,这样获取不到值时将会返回默认值
# print(d.get('name'))
# print(d.get('hello','默认值'))

修改字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 修改字典
# d[key] = value 如果key存在则覆盖,不存在则添加
d['name'] = 'sunwukong' # 修改字典的key-value
d['address'] = '花果山' # 向字典中添加key-value

# print(d)
# setdefault(key[, default]) 可以用来向字典中添加key-value
# 如果key已经存在于字典中,则返回key的值,不会对字典做任何操作
# 如果key不存在,则向字典中添加这个key,并设置value
result = d.setdefault('name','猪八戒')
result = d.setdefault('hello','猪八戒')

# print('result =',result)
# print(d)

# update([other])
# 将其他的字典中的key-value添加到当前字典中
# 如果有重复的key,则后边的会替换到当前的
d = {'a':1,'b':2,'c':3}
d2 = {'d':4,'e':5,'f':6, 'a':7}
d.update(d2)

删除

1
2
3
4
# print(d)
# 删除,可以使用 del 来删除字典中的 key-value
del d['a']
del d['b']

随机删除字典中的一个键值对

1
2
3
4
5
6
7
8
# print(d)
# popitem()
# 随机删除字典中的一个键值对,一般都会删除最后一个键值对
# 删除之后,它会将删除的key-value作为返回值返回
# 返回的是一个元组,元组中有两个元素,第一个元素是删除的key,第二个是删除的value
# 当使用popitem()删除一个空字典时,会抛出异常 KeyError: 'popitem(): dictionary is empty'
# d.popitem()
# result = d.popitem()

根据key删除字典中的key-value会将被删除的value返回

1
2
3
4
5
6
7
8
9
10
11
# pop(key[, default])
# 根据key删除字典中的key-value
# 会将被删除的value返回!
# 如果删除不存在的key,会抛出异常
# 如果指定了默认值,再删除不存在的key时,不会报错,而是直接返回默认值
result = d.pop('d')
result = d.pop('z','这是默认值')

# del d['z'] z不存在,报错
# result = d.popitem()
# result = d.popitem()

清空字典

1
2
3
4
5
# clear()用来清空字典
d.clear()

# print('result =',result)
# print(d)

对字典进行浅复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# copy()
# 该方法用于对字典进行浅复制
# 复制以后的对象,和原对象是独立,修改一个不会影响另一个
# 注意,浅复制会简单复制对象内部的值,如果值也是一个可变对象,这个可变对象不会被复制
d = {'a':1,'b':2,'c':3}
d2 = d.copy()
# d['a'] = 100

d = {'a':{'name':'孙悟空','age':18},'b':2,'c':3}
d2 = d.copy()
d2['a']['name'] = '猪八戒'


print('d = ',d , id(d))
print('d2 = ',d2 , id(d2))

3.5.2 遍历字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 遍历字典
# keys() 该方法会返回字典的所有的key
# 该方法会返回一个序列,序列中保存有字典的所有的键
d = {'name':'孙悟空','age':18,'gender':'男'}

# 通过遍历keys()来获取所有的键
# for k in d.keys() :
# print(k , d[k])

# values()
# 该方法会返回一个序列,序列中保存有字典的左右的值
# for v in d.values():
# print(v)

# items()
# 该方法会返回字典中所有的项
# 它会返回一个序列,序列中包含有双值子序列
# 双值分别是,字典中的key和value
# print(d.items())
for k,v in d.items() :
print(k , '=' , v)

3.6 集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 集合
# 使用 {} 来创建集合
s = {10,3,5,1,2,1,2,3,1,1,1,1} # <class 'set'>
# s = {[1,2,3],[4,6,7]} TypeError: unhashable type: 'list'
# 使用 set() 函数来创建集合
s = set() # 空集合
# 可以通过set()来将序列和字典转换为集合
s = set([1,2,3,4,5,1,1,2,3,4,5])
s = set('hello')
s = set({'a':1,'b':2,'c':3}) # 使用set()将字典转换为集合时,只会包含字典中的键

# 创建集合
s = {'a' , 'b' , 1 , 2 , 3 , 1}

# 使用in和not in来检查集合中的元素
# print('c' in s)

# 使用len()来获取集合中元素的数量
# print(len(s))

# add() 向集合中添加元素
s.add(10)
s.add(30)

# update() 将一个集合中的元素添加到当前集合中
# update()可以传递序列或字典作为参数,字典只会使用键
s2 = set('hello')
s.update(s2)
s.update((10,20,30,40,50))
s.update({10:'ab',20:'bc',100:'cd',1000:'ef'})

# {1, 2, 3, 100, 40, 'o', 10, 1000, 'a', 'h', 'b', 'l', 20, 50, 'e', 30}
# pop()随机删除并返回一个集合中的元素
# result = s.pop()

# remove()删除集合中的指定元素
s.remove(100)
s.remove(1000)

# clear()清空集合
s.clear()

# copy()对集合进行浅复制

# print(result)
print(s , type(s))

3.6.1 集合的运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 在对集合做运算时,不会影响原来的集合,而是返回一个运算结果
# 创建两个集合
s = {1,2,3,4,5}
s2 = {3,4,5,6,7}

# & 交集运算
result = s & s2 # {3, 4, 5}

# | 并集运算
result = s | s2 # {1,2,3,4,5,6,7}

# - 差集
result = s - s2 # {1, 2}

# ^ 异或集 获取只在一个集合中出现的元素
result = s ^ s2 # {1, 2, 6, 7}

# <= 检查一个集合是否是另一个集合的子集
# 如果a集合中的元素全部都在b集合中出现,那么a集合就是b集合的子集,b集合是a集合超集
a = {1,2,3}
b = {1,2,3,4,5}

result = a <= b # True
result = {1,2,3} <= {1,2,3} # True
result = {1,2,3,4,5} <= {1,2,3} # False

# < 检查一个集合是否是另一个集合的真子集
# 如果超集b中含有子集a中所有元素,并且b中还有a中没有的元素,则b就是a的真超集,a是b的真子集
result = {1,2,3} < {1,2,3} # False
result = {1,2,3} < {1,2,3,4,5} # True

# >= 检查一个集合是否是另一个的超集
# > 检查一个集合是否是另一个的真超集
print('result =',result)

四 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 比如有如下三行代码,这三行代码是一个完整的功能
# print('Hello')
# print('你好')
# print('再见')

# 定义一个函数
def fn() :
print('这是我的第一个函数!')
print('hello')
print('今天天气真不错!')

# 打印fn
# print(fn) <function fn at 0x03D2B618>
# print(type(fn)) <class 'function'>

# fn是函数对象 fn()调用函数
# print是函数对象 print()调用函数
# fn()

# 定义一个函数,可以用来求任意两个数的和
# def sum() :
# a = 123
# b = 456
# print(a + b)

# sum()

# 定义函数时指定形参
def fn2(a , b) :
# print('a =',a)
# print('b =',b)
print(a,"+",b,"=",a + b)

# 调用函数时,来传递实参
fn2(10,20)
fn2(123,456)

4.1 函数的参数

1
2
3
4
5
6
7
8
9
10
# 求任意三个数的乘积
def mul(a,b,c):
print(a*b*c)

# 根据不同的用户名显示不同的欢迎信息
def welcome(username):
print('欢迎',username,'光临')

mul(1,2,3)
welcome('张三')

输出的结果为

1
2
6
欢迎 张三 光临

参数默认值

1
2
3
4
5
6
7
8
9
10
11
12
# 定义一个函数
# 定义形参时,可以为形参指定默认值
# 指定了默认值以后,如果用户传递了参数则默认值没有任何作用
# 如果用户没有传递,则默认值就会生效
def fn(a = 5 , b = 10 , c = 20):
print('a =',a)
print('b =',b)
print('c =',c)

fn(1 , 2 , 3)
fn(1 , 2)
fn()

实参的传递方式

1
2
3
4
5
# 实参的传递方式
# 位置参数
# 位置参数就是将对应位置的实参复制给对应位置的形参
# 第一个实参赋值给第一个形参,第二个实参赋值给第二个形参 。。。
# fn(1 , 2 , 3)

关键字参数

1
2
3
4
5
6
7
8
9
10
# 关键字参数
# 关键字参数,可以不按照形参定义的顺序去传递,而直接根据参数名去传递参数
# fn(b=1 , c=2 , a=3)
# print('hello' , end='')
# 位置参数和关键字参数可以混合使用
# 混合使用关键字和位置参数时,必须将位置参数写到前面
# fn(1,c=30)

def fn2(a):
print('a =',a)

函数在调用时,解析器不会检查实参的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    
# 函数在调用时,解析器不会检查实参的类型
# 实参可以传递任意类型的对象
b = 123
b = True
b = 'hello'
b = None
b = [1,2,3]

# fn2(b)
fn2(fn)

def fn3(a , b):
print(a+b)

# fn3(123,"456")

def fn4(a):
# 在函数中对形参进行重新赋值,不会影响其他的变量
# a = 20
# a是一个列表,尝试修改列表中的元素
# 如果形参执行的是一个对象,当我们通过形参去修改对象时
# 会影响到所有指向该对象的变量
a[0] = 30
print('a =',a,id(a))

传递副本

1
2
3
4
5
6
7
8
c = 10
c = [1, 2, 3]

fn4(c)
fn4(c.copy())
fn4(c[:])

print('c =', c, id(c))

4.2不定参数

4.2.1 基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 不定长的参数
# 定义一个函数,可以求任意个数字的和
def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
print(result)


# sum(123,456,789,10,20,30,40)

# 在定义函数时,可以在形参前边加上一个*,这样这个形参将会获取到所有的实参
# 它将会将所有的实参保存到一个元组中
# a,b,*c = (1,2,3,4,5,6)

# *a会接受所有的位置实参,并且会将这些实参统一保存到一个元组中(装包)
def fn(*a):
print("a =",a,type(a))

带星号的形参只能有一个

1
2
3
4
5
6
7
8
9
10
11

# fn(1,2,3,4,5)
# 带星号的形参只能有一个
# 带星号的参数,可以和其他参数配合使用
# 第一个参数给a,第二个参数给b,剩下的都保存到c的元组中
def fn2(a, b, *c):
print('a =', a)
print('b =', b)


print('c =', c)

可变参数不是必须写在最后,但是注意,带*的参数后的所有参数,必须以关键字参数的形式传递
第一个参数给a,剩下的位置参数给b的元组,c必须使用关键字参数

1
2
3
4
def fn2(a, *b, c):
print('a =', a)
print('b =', b)
print('c =', c)

所有的位置参数都给a,b和c必须使用关键字参数

1
2
3
4
5
6
def fn2(*a, b, c):
print('a =', a)


print('b =', b)
print('c =', c)

对于上述实例,运行时会出现异常,结果如下

1
2
3
4
Traceback (most recent call last):
File "F:\python\main.py", line 5, in <module>
print('b =', b)
NameError: name 'b' is not defined

如果在形参的开头直接写一个*,则要求我们的所有的参数必须以关键字参数的形式传递

1
2
3
4
5
6
7
def fn2(*, a, b, c):
print('a =', a)
print('b =', b)
print('c =', c)


fn2(a=3, b=4, c=5)

形参只能接收位置参数,而不能接收关键字参数

1
2
def fn3(*a):
print('a =', a)

4.2.2 接收其他的关键字参数

形参可以接收其他的关键字参数,它会将这些参数统一保存到一个字典中
字典的key就是参数的名字,字典的value就是参数的值
形参只能有一个,并且必须写在所有参数的最后

1
2
3
4
5
6
7
def fn3(b, c, **a):
print('a =', a, type(a))
print('b =', b)
print('c =', c)


fn3(b=1, d=2, c=3, e=10, f=20)

输出的结果为

1
2
3
a = {'d': 2, 'e': 10, 'f': 20} <class 'dict'>
b = 1
c = 3

对于下面的示例

1
2
3
4
5
6
7
def fn3(b, f, **a):
print('a =', a, type(a))
print('b =', b)
print('f =', f)


fn3(b=1, d=2, c=3, e=10, f=20)

输出的结果为

1
2
3
a = {'d': 2, 'c': 3, 'e': 10} <class 'dict'>
b = 1
f = 20

对于另一个示例

1
2
3
4
5
6
7
def fn3(b, g, **a):
print('a =', a, type(a))
print('b =', b)
print('g =', g)


fn3(b=1, d=2, c=3, e=10, f=20)

运行程序出现问题,输出结果如下

1
2
3
4
Traceback (most recent call last):
File "F:\python\main.py", line 7, in <module>
fn3(b=1, d=2, c=3, e=10, f=20)
TypeError: fn3() missing 1 required positional argument: 'g'

4.2.3 参数的解包(拆包)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 参数的解包(拆包)
def fn4(a, b, c):
print('a =', a)
print('b =', b)
print('c =', c)


# 创建一个元组
t = (10, 20, 30)

# 传递实参时,也可以在序列类型的参数前添加星号,这样他会自动将序列中的元素依次作为参数传递
# 这里要求序列中元素的个数必须和形参的个数的一致
# fn4(*t)

# 创建一个字典
d = {'a': 100, 'b': 200, 'c': 300}
# 通过 **来对一个字典进行解包操作
fn4(**d)

运行上述程序,得到的结果如下

1
2
3
a = 100
b = 200
c = 300

4.3 返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 返回值,返回值就是函数执行以后返回的结果
# 可以通过 return 来指定函数的返回值
# 可以之间使用函数的返回值,也可以通过一个变量来接收函数的返回值

def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
print(result)

# sum(123,456,789)


# return 后边跟什么值,函数就会返回什么值
# return 后边可以跟任意的对象,返回值甚至可以是一个函数
def fn():
# return 'Hello'
# return [1,2,3]
# return {'k':'v'}
def fn2() :
print('hello')

return fn2 # 返回值也可以是一个函数

r = fn() # 这个函数的执行结果就是它的返回值
# r()
# print(fn())
# print(r)

# 如果仅仅写一个return 或者 不写return,则相当于return None
def fn2() :
a = 10
return

# 在函数中,return后的代码都不会执行,return 一旦执行函数自动结束
def fn3():
print('hello')
return
print('abc')

# r = fn3()
# print(r)

def fn4() :
for i in range(5):
if i == 3 :
# break 用来退出当前循环
# continue 用来跳过当次循环
return # return 用来结束函数
print(i)
print('循环执行完毕!')

# fn4()

def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
return result

r = sum(123,456,789)

# print(r + 778)

def fn5():
return 10

# fn5 和 fn5()的区别
print(fn5) # fn5是函数对象,打印fn5实际是在打印函数对象 <function fn5 at 0x05771BB8>
print(fn5()) # fn5()是在调用函数,打印fn5()实际上是在打印fn5()函数的返回值 10

4.4 文档字符串

help()是Python中的内置函数

通过help()函数可以查询python中的函数的用法

1
语法:help(函数对象)

help(print) # 获取print()函数的使用说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 文档字符串(doc str)
# 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明
# 当我们编写了文档字符串时,就可以通过help()函数来查看函数的说明
# 文档字符串非常简单,其实直接在函数的第一行写一个字符串就是文档字符串
def fn(a: int, b: bool, c: str = 'hello') -> int:
'''
这是一个文档字符串的示例

函数的作用:。。。。。
函数的参数:
a,作用,类型,默认值。。。。
b,作用,类型,默认值。。。。
c,作用,类型,默认值。。。。
'''
return 10


help(fn)

运行上述示例,输出结果如下

1
2
3
4
5
6
7
8
9
10
Help on function fn in module __main__:

fn(a: int, b: bool, c: str = 'hello') -> int
这是一个文档字符串的示例

函数的作用:。。。。。
函数的参数:
a,作用,类型,默认值。。。。
b,作用,类型,默认值。。。。
c,作用,类型,默认值。。。。

4.5 作用域和命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 作用域(scope)
# 作用域指的是变量生效的区域
b = 20 # 全局变量


def fn():
a = 10 # a定义在了函数内部,所以他的作用域就是函数内部,函数外部无法访问
print('函数内部:', 'a =', a)
print('函数内部:', 'b =', b)


fn()

print('函数外部:', 'a =', a) # 这里会提示 未解析的引用 'a'
print('函数外部:', 'b =', b)

运行上述示例,得到的结果如下

1
2
3
4
5
6
Traceback (most recent call last):
File "F:\python\main.py", line 14, in <module>
print('函数外部:', 'a =', a)
NameError: name 'a' is not defined
函数内部: a = 10
函数内部: b = 20

在Python中一共有两种作用域
全局作用域

  • 全局作用域在程序执行时创建,在程序执行结束时销毁
  • 所有函数以外的区域都是全局作用域
    • 在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问

函数作用域

  • 函数作用域在函数调用时创建,在调用结束时销毁
    • 函数每调用一次就会产生一个新的函数作用域
  • 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问

变量的查找

  • 当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,
  • 如果没有则继续去上一级作用域中寻找,如果有则使用,
  • 如果依然没有则继续去上一级作用域中寻找,以此类推
  • 直到找到全局作用域,依然没有找到,则会抛出异常NameError: name ‘a’ is not defined
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def fn2():
    def fn3():
    print('fn3中:', 'a =', a)

    fn3()


    fn2()

    a = 20
    运行上述示例,得到的输出如下
1
2
3
4
5
6
7
8
Traceback (most recent call last):
File "F:\python\main.py", line 8, in <module>
fn2()
File "F:\python\main.py", line 5, in fn2
fn3()
File "F:\python\main.py", line 3, in fn3
print('fn3中:', 'a =', a)
NameError: name 'a' is not defined

如果将上述示例改为

1
2
3
4
5
6
7
8
9
10
11
a = 20


def fn2():
def fn3():
print('fn3中:', 'a =', a)

fn3()


fn2()

则可以得到运行结果

1
fn3中: a = 20

运行以下示例

1
2
3
4
5
6
7
8
9
10
def fn3():
# a = 10 # 在函数中为变量赋值时,默认都是为局部变量赋值
# 如果希望在函数内部修改全局变量,则需要使用global关键字,来声明变量
global a # 声明在函数内部的使用a是全局变量,此时再去修改a时,就是在修改全局的a
a = 10 # 修改全局变量
print('函数内部:', 'a =', a)


fn3()
print('函数外部:', 'a =', a)

得到的结果如下

1
2
函数内部: a = 10
函数外部: a = 10

命名空间(namespace)

  • 命名空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间当中
  • 每一个作用域都会有一个它对应的命名空间
  • 全局命名空间,用来保存全局变量。函数命名空间用来保存函数中的变量
  • 命名空间实际上就是一个字典,是一个专门用来存储变量的字典

    locals()用来获取当前作用域的命名空间

如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间
返回的是一个字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
scope = locals()  # 当前命名空间
print(scope)
print(type(scope))
print("--------------------")
# print(a)
# print(scope['a'])
# 向scope中添加一个key-value
scope['c'] = 1000 # 向字典中添加key-value就相当于在全局中创建了一个变量(一般不建议这么做)

print(scope)
print("--------------------")


# print(c)

def fn4():
a = 10
scope = locals() # 在函数内部调用locals()会获取到函数的命名空间
# scope['b'] = 20 # 可以通过scope来操作函数的命名空间,但是也是不建议这么做
print("内部 scope :", scope)
print("--------------------")
# globals() 函数可以用来在任意位置获取全局命名空间
global_scope = globals()
print("内部 scope 的 c :", global_scope['c'])
print("--------------------")
global_scope['a'] = 30
print("内部的 global_scope :", scope)


fn4()

运行上述示例,得到的结果如下

1
2
3
4
5
6
7
8
9
10
已连接到 pydev 调试器 (内部版本 211.7142.13){'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000023CD7436CD0>, '__spec__': None, '__file__': 'F:/python/main.py', '__builtins__': <module 'builtins' (built-in)>, 'scope': {...}}
<class 'dict'>
--------------------
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000023CD7436CD0>, '__spec__': None, '__file__': 'F:/python/main.py', '__builtins__': <module 'builtins' (built-in)>, 'scope': {...}, 'c': 1000}
--------------------
内部 scope : {'a': 10}
--------------------
内部 scope 的 c : 1000
--------------------
内部的 global_scope : {'a': 10}

4.6 高阶函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# 高阶函数
# 接收函数作为参数,或者将函数作为返回值的函数是高阶函数
# 当我们使用一个函数作为参数时,实际上是将指定的代码传递进了目标函数

# 创建一个列表
l = [1,2,3,4,5,6,7,8,9,10]

# 定义一个函数
# 可以将指定列表中的所有的偶数,保存到一个新的列表中返回

# 定义一个函数,用来检查一个任意的数字是否是偶数
def fn2(i) :
if i % 2 == 0 :
return True

return False

# 这个函数用来检查指定的数字是否大于5
def fn3(i):
if i > 5 :
return True
return False

def fn(func , lst) :

'''
fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回

参数:
lst:要进行筛选的列表
'''
# 创建一个新列表
new_list = []

# 对列表进行筛选
for n in lst :
# 判断n的奇偶
if func(n) :
new_list.append(n)
# if n > 5 :
# new_list.append(n)




# 返回新列表
return new_list

# def fn4(i):
# if i % 3 == 0:
# return True
# return False

def fn4(i):
return i % 3 == 0

# print(fn(fn4 , l))

# filter()
# filter()可以从序列中过滤出符合条件的元素,保存到一个新的序列中
# 参数:
# 1.函数,根据该函数来过滤序列(可迭代的结构)
# 2.需要过滤的序列(可迭代的结构)
# 返回值:
# 过滤后的新序列(可迭代的结构)

# fn4是作为参数传递进filter()函数中
# 而fn4实际上只有一个作用,就是作为filter()的参数
# filter()调用完毕以后,fn4就已经没用
# 匿名函数 lambda 函数表达式 (语法糖)
# lambda函数表达式专门用来创建一些简单的函数,他是函数创建的又一种方式
# 语法:lambda 参数列表 : 返回值
# 匿名函数一般都是作为参数使用,其他地方一般不会使用

def fn5(a , b):
return a + b

# (lambda a,b : a + b)(10,20)
# 也可以将匿名函数赋值给一个变量,一般不会这么做
fn6 = lambda a,b : a + b
# print(fn6(10,30))


r = filter(lambda i : i > 5 , l)
# print(list(r))

# map()
# map()函数可以对可跌倒对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回
l = [1,2,3,4,5,6,7,8,9,10]

r = map(lambda i : i ** 2 , l)

# print(list(r))

# sort()
# 该方法用来对列表中的元素进行排序
# sort()方法默认是直接比较列表中的元素的大小
# 在sort()可以接收一个关键字参数 , key
# key需要一个函数作为参数,当设置了函数作为参数
# 每次都会以列表中的一个元素作为参数来调用函数,并且使用函数的返回值来比较元素的大小
l = ['bb','aaaa','c','ddddddddd','fff']
# l.sort(key=len)

l = [2,5,'1',3,'6','4']
l.sort(key=int)
# print(l)

# sorted()
# 这个函数和sort()的用法基本一致,但是sorted()可以对任意的序列进行排序
# 并且使用sorted()排序不会影响原来的对象,而是返回一个新对象

l = [2,5,'1',3,'6','4']
# l = "123765816742634781"

print('排序前:',l)
print(sorted(l,key=int))
print('排序后:',l)

4.7 闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 将函数作为返回值返回,也是一种高阶函数
# 这种高阶函数我们也称为叫做闭包,通过闭包可以创建一些只有当前函数能访问的变量
# 可以将一些私有的数据藏到的闭包中

def fn():

a = 10

# 函数内部再定义一个函数
def inner():
print('我是fn2' , a)

# 将内部函数 inner作为返回值返回
return inner

# r是一个函数,是调用fn()后返回的函数
# 这个函数实在fn()内部定义,并不是全局函数
# 所以这个函数总是能访问到fn()函数内的变量
r = fn()

# r()

# 求多个数的平均值
# nums = [50,30,20,10,77]

# sum()用来求一个列表中所有元素的和
# print(sum(nums)/len(nums))

# 形成闭包的要件
# ① 函数嵌套
# ② 将内部函数作为返回值返回
# ③ 内部函数必须要使用到外部函数的变量
def make_averager():
# 创建一个列表,用来保存数值
nums = []

# 创建一个函数,用来计算平均值
def averager(n) :
# 将n添加到列表中
nums.append(n)
# 求平均值
return sum(nums)/len(nums)

return averager

averager = make_averager()

print(averager(10))
print(averager(20))
print(averager(30))
print(averager(40))

4.8 装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建几个函数

def add(a , b):
'''
求任意两个数的和
'''
r = a + b
return r


def mul(a , b):
'''
求任意两个数的积
'''
r = a * b
return r

希望函数可以在计算前,打印开始计算,计算结束后打印计算完毕
我们可以直接通过修改函数中的代码来完成这个需求,但是会产生以下一些问题

  • 如果要修改的函数过多,修改起来会比较麻烦
  • 并且不方便后期的维护
  • 并且这样做会违反开闭原则(OCP)
  • 程序的设计,要求开发对程序的扩展,要关闭对程序的修改
1
2
3
4
5
6
7
8
9
10
11
12

# 我们希望在不修改原函数的情况下,来对函数进行扩展
def fn():
print('我是fn函数....')

# 只需要根据现有的函数,来创建一个新的函数
def fn2():
print('函数开始执行~~~')
fn()
print('函数执行结束~~~')

fn2()

运行上述程序,得到的结果如下

1
2
3
函数开始执行~~~
我是fn函数....
函数执行结束~~~

而对于下面的的示例

1
2
3
4
5
6
7
8
9
def new_add(a, b):
print('计算开始~~~')
r = add(a, b)
print('计算结束~~~')
return r


r = new_add(111, 222)
print(r)

运行结果为

1
2
3
计算开始~~~
计算结束~~~
333

上边的方式,已经可以在不修改源代码的情况下对函数进行扩展了
但是,这种方式要求我们每扩展一个函数就要手动创建一个新的函数,实在是太麻烦了
为了解决这个问题,我们创建一个函数,让这个函数可以自动的帮助我们生产函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def begin_end(old):
'''
用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束

参数:
old 要扩展的函数对象
'''
# 创建一个新函数
def new_function(*args , **kwargs):
print('开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('执行结束~~~~')
# 返回函数的执行结果
return result

# 返回新函数
return new_function

f = begin_end(fn)
f2 = begin_end(add)
f3 = begin_end(mul)

# r = f()
# r = f2(123,456)
# r = f3(123,456)
# print(r)
# 向begin_end()这种函数我们就称它为装饰器
# 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
# 在开发中,我们都是通过装饰器来扩展函数的功能的
# 在定义函数时,可以通过@装饰器,来使用指定的装饰器,来装饰当前的函数
# 可以同时为一个函数指定多个装饰器,这样函数将会安装从内向外的顺序被装饰

def fn3(old):
'''
用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束

参数:
old 要扩展的函数对象
'''
# 创建一个新函数
def new_function(*args , **kwargs):
print('fn3装饰~开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('fn3装饰~执行结束~~~~')
# 返回函数的执行结果
return result

# 返回新函数
return new_function

@fn3
@begin_end
def say_hello():
print('大家好~~~')

say_hello()

五 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
a = int(10) # 创建一个int类的实例
b = str('hello') # 创建一个str类的实例

# print(a , type(a))
# print(b , type(b))

# 定义一个简单的类
# 使用class关键字来定义类,语法和函数很像!
# class 类名([父类]):
# 代码块
# <class '__main__.MyClass'>
class MyClass():
pass

# print(MyClass)
# 使用MyClass创建一个对象
# 使用类来创建对象,就像调用一个函数一样
mc = MyClass() # mc就是通过MyClass创建的对象,mc是MyClass的实例
mc_2 = MyClass()
mc_3 = MyClass()
mc_4 = MyClass()
# mc mc_2 mc_3 mc_4 都是MyClass的实例,他们都是一类对象
# isinstance()用来检查一个对象是否是一个类的实例
result = isinstance(mc_2,MyClass)
result = isinstance(mc_2,str)

# print(mc , type(mc))
# print('result =',result)

# print(id(MyClass) , type(MyClass))

# 现在我们通过MyClass这个类创建的对象都是一个空对象
# 也就是对象中实际上什么都没有,就相当于是一个空的盒子
# 可以向对象中添加变量,对象中的变量称为属性
# 语法:对象.属性名 = 属性值
mc.name = '孙悟空'
mc_2.name = '猪八戒'

print(mc_2.name)

5.1 定义类

5.1.1 基本定义

类和对象都是对现实生活中的事物或程序中的内容的抽象

实际上所有的事物都由两部分构成:
1.数据(属性)
2.行为(方法)

在类的代码块中,我们可以定义变量和函数,
变量会成为该类实例的公共属性,所有的该类实例都可以通过 对象.属性名 的形式访问
函数会成为该类实例的公共方法,所有该类实例都可以通过 对象.方法名() 的形式调用方法

注意:
方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参!

实例为什么能访问到类中的属性和方法
类中定义的属性和方法都是公共的,任何该类实例都可以访问

属性和方法查找的流程
当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,
如果有,则直接返回当前的对象的属性值,
如果没有,则去当前对象的类对象中去寻找,如果有则返回类对象的属性值,
如果类对象中依然没有,则报错!

类对象和实例对象中都可以保存属性(方法)

  • 如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中
  • 如果这个属性(方法)是某个实例独有,则应该保存到实例对象中

  • 一般情况下,属性保存到实例对象中,而方法需要保存到类对象中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 尝试定义一个表示人的类
class Person:
# 在类的代码块中,我们可以定义变量和函数
# 在类中我们所定义的变量,将会成为所有的实例的公共属性
# 所有实例都可以访问这些变量
name = 'swk' # 公共属性,所有实例都可以访问

# 在类中也可以定义函数,类中的定义的函数,我们称为方法
# 这些方法可以通过该类的所有实例来访问

def say_hello(self):
# 方法每次被调用时,解析器都会自动传递第一个实参
# 第一个参数,就是调用方法的对象本身,
# 如果是p1调的,则第一个参数就是p1对象
# 如果是p2调的,则第一个参数就是p2对象
# 一般我们都会将这个参数命名为self

# say_hello()这个方法,可以显示如下格式的数据:
# 你好!我是 xxx
# 在方法中不能直接访问类中的属性
print('你好!我是 %s' % self.name)


# 创建Person的实例
p1 = Person()
p2 = Person()

print(p2.name) # 这里输出的是 swk

# 调用方法,对象.方法名()
# 方法调用和函数调用的区别
# 如果是函数调用,则调用时传几个参数,就会有几个实参
# 但是如果是方法调用,默认传递一个参数,所以方法中至少要定义一个形参


# 修改p1的name属性
p1.name = '猪八戒'
p2.name = '沙和尚'

p1.say_hello() # 这里输出的是: '你好!我是 猪八戒'
p2.say_hello() # 这里输出的是: '你好!我是 沙和尚'

del p2.name # 删除p2的name属性

print(p1.name) # 这里输出的是: 猪八戒
print(p2.name) # 这里输出的是: swk

5.1.2 name属性

一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用name属性来使该程序块仅在该模块自身运行时执行。

1
2
3
4
5
6
7
#!/usr/bin/python3
# Filename: using_name.py

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

运行输出如下:

1
2
$ python using_name.py
程序自身在运行
1
2
3
4
$ python
>>> import using_name
我来自另一模块
>>>

说明: 每个模块都有一个name属性,当其值是’main‘时,表明该模块自身在运行,否则是被引入。

说明:namemain 底下是双下划线, 是这样去掉中间的那个空格。

5.2 类的初始化

在类中可以定义一些特殊方法(魔术方法)

  • 特殊方法都是以 开头, 结尾的方法
  • 特殊方法不需要我们自己调用,不要尝试去调用特殊方法
  • 特殊方法将会在特殊的时刻自动调用

学习特殊方法:
1.特殊方法什么时候调用
2.特殊方法有什么作用

创建对象的流程

1
p1 = Person()的运行流程
  1. 创建一个变量
  2. 在内存中创建一个新对象
  3. init (self)方法执行
  4. 将对象的id赋值给变量
1
2
3
4
5
6
7
8
9
10
11
class Person :
# init会在对象创建以后离开执行
# init可以用来向新创建的对象中初始化属性
# 调用类创建对象时,类后边的所有参数都会依次传递到init()中
def __init__(self,name):
# print(self)
# 通过self向新建的对象中初始化属性
self.name = name

def say_hello(self):
print('大家好,我是%s'%self.name)

目前来讲,对于Person类来说name是必须的,并且每一个对象中的name属性基本上都是不同.而我们现在是将name属性在定义为对象以后,手动添加到对象中,这种方式很容易出现错误.我们希望,在创建对象时,必须设置name属性,如果不设置对象将无法创建并且属性的创建应该是自动完成的,而不是在创建对象以后手动完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person:
# init会在对象创建以后离开执行
# init可以用来向新创建的对象中初始化属性
# 调用类创建对象时,类后边的所有参数都会依次传递到init()中
def __init__(self, name):
# print(self)
# 通过self向新建的对象中初始化属性
self.name = name

def say_hello(self):
print('大家好,我是%s' % self.name)


p1 = Person()
# # 手动向对象添加name属性
p1.name = '孙悟空'

p3 = Person()
p3.name = '沙和尚'

p3.say_hello()

运行上述示例,运行时出现异常,结果如下

1
2
3
4
Traceback (most recent call last):
File "F:\python\main.py", line 14, in <module>
p1 = Person()
TypeError: __init__() missing 1 required positional argument: 'name'

修改上述示例为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Person:
# init会在对象创建以后离开执行
# init可以用来向新创建的对象中初始化属性
# 调用类创建对象时,类后边的所有参数都会依次传递到init()中
def __init__(self, name):
# print(self)
# 通过self向新建的对象中初始化属性
self.name = name

def say_hello(self):
print('大家好,我是%s' % self.name)


p1 = Person('孙悟空')
p2 = Person('猪八戒')
p3 = Person('沙和尚')
p4 = Person('唐僧')
# p1.__init__() 不要这么做

print(p1.name)
print(p2.name)
print(p3.name)
print(p4.name)

p4.say_hello()

运行程序,得到的结果为

1
2
3
4
5
孙悟空
猪八戒
沙和尚
唐僧
大家好,我是唐僧

5.3 类中的属性和方法

5.3.1 基本定义

类的私有属性

__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs


类的方法

在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。

self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定使用 self


类的私有方法

__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 定义一个类
class A(object):
# 类属性
# 实例属性
# 类方法
# 实例方法
# 静态方法

# 类属性,直接在类中定义的属性是类属性
# 类属性可以通过类或类的实例访问到
# 但是类属性只能通过类对象来修改,无法通过实例对象修改
count = 0

def __init__(self):
# 实例属性,通过实例对象添加的属性属于实例属性
# 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
self.name = '孙悟空'

# 实例方法
# 在类中定义,以self为第一个参数的方法都是实例方法
# 实例方法在调用时,Python会将调用对象作为self传入
# 实例方法可以通过实例和类去调用
# 当通过实例调用时,会自动将当前调用对象作为self传入
# 当通过类调用时,不会自动传递self,此时我们必须手动传递self
def test(self):
print('这是test方法~~~ ', self)

# 类方法

# 在类内部使用 @classmethod 来修饰的方法属于类方法
# 类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
# 类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
# 类方法可以通过类去调用,也可以通过实例调用,没有区别
@classmethod
def test_2(cls):
print('这是test_2方法,他是一个类方法~~~ ', cls)
print(cls.count)

# 静态方法
# 在类中使用 @staticmethod 来修饰的方法属于静态方法
# 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用
# 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
# 静态方法一般都是一些工具方法,和当前类无关
@staticmethod
def test_3():
print('test_3执行了~~~')


a = A()
# 实例属性,通过实例对象添加的属性属于实例属性
a.count = 10
A.count = 100
print('A.count = ,', A.count)
print('a.count = ,', a.count)
# print('A ,', A.name) 运行这一行会报错 AttributeError: type object 'A' has no attribute 'name'
print('a.name ,', a.name)

# a.test() 等价于 A.test(a)
a.test()
A.test(a)
# A.test() 运行这一行会报错 TypeError: test() missing 1 required positional argument: 'self'

# A.test_2() 等价于 a.test_2()

A.test_2()
a.test_2()

A.test_3()
a.test_3()

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
A.count = , 100
a.count = , 10
a.name , 孙悟空
这是test方法~~~ <__main__.A object at 0x00000190A1B3EF70>
这是test方法~~~ <__main__.A object at 0x00000190A1B3EF70>
这是test_2方法,他是一个类方法~~~ <class '__main__.A'>
100
这是test_2方法,他是一个类方法~~~ <class '__main__.A'>
100
test_3执行了~~~
test_3执行了~~~

5.3.2 静态方法、普通方法、类方法

静态方法: 用 @staticmethod 装饰的不带 self 参数的方法叫做静态方法,类的静态方法可以没有参数,可以直接使用类名调用。

普通方法: 默认有个self参数,且只能被对象调用。

类方法: 默认有个 cls 参数,可以被类和对象调用,需要加上 @classmethod 装饰器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Classname:
@staticmethod
def fun():
print('静态方法')

@classmethod
def a(cls):
print('类方法')

# 普通方法
def b(self):
print('普通方法')



Classname.fun()
Classname.a()

C = Classname()
C.fun()
C.a()
C.b()

运行结果如下

1
2
3
4
5
6

静态方法
类方法
静态方法
类方法
普通方法

类方法

使用装饰器@classmethod。

原则上,类方法是将类本身作为对象进行操作的方法。假设有个方法,且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。另外,如果需要继承,也可以定义为类方法。

静态方法

使用装饰器@staticmethod

静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。

5.4 特殊方法

类的专有方法:

  • init : 构造函数,在生成对象时调用
  • del : 析构函数,释放对象时使用
  • repr : 打印,转换
  • setitem : 按照索引赋值
  • getitem: 按照索引获取值
  • len: 获得长度
  • cmp: 比较运算
  • call: 函数调用
  • add: 加运算
  • sub: 减运算
  • mul: 乘运算
  • truediv: 除运算
  • mod: 求余运算
  • pow: 乘方

所有专有方法中,init()要求无返回值,或者返回 None。而其他方法,如str()、add()等,一般都是要返回值的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# 特殊方法,也称为魔术方法
# 特殊方法都是使用__开头和结尾的
# 特殊方法一般不需要我们手动调用,需要在一些特殊情况下自动执行

# 定义一个Person类
class Person(object):
"""人类"""

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

# __str__()这个特殊方法会在尝试将对象转换为字符串的时候调用
# 它的作用可以用来指定对象转换为字符串的结果 (print函数)
def __str__(self):
return 'Person [name=%s , age=%d]' % (self.name, self.age)

# __repr__()这个特殊方法会在对当前对象使用repr()函数时调用

# 它的作用是指定对象在 ‘交互模式’中直接输出的效果
def __repr__(self):
return 'Hello'

# object.__add__(self, other)

# object.__sub__(self, other)
# object.__mul__(self, other)
# object.__matmul__(self, other)
# object.__truediv__(self, other)
# object.__floordiv__(self, other)
# object.__mod__(self, other)
# object.__divmod__(self, other)
# object.__pow__(self, other[, modulo])
# object.__lshift__(self, other)
# object.__rshift__(self, other)
# object.__and__(self, other)
# object.__xor__(self, other)
# object.__or__(self, other)

# object.__lt__(self, other) 小于 <
# object.__le__(self, other) 小于等于 <=
# object.__eq__(self, other) 等于 ==
# object.__ne__(self, other) 不等于 !=
# object.__gt__(self, other) 大于 >
# object.__ge__(self, other) 大于等于 >=

# __len__()获取对象的长度

# object.__bool__(self)
# 可以通过bool来指定对象转换为布尔值的情况
def __bool__(self):
return self.age > 17

# __gt__会在对象做大于比较的时候调用,该方法的返回值将会作为比较的结果
# 他需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象
# self > other
def __gt__(self, other):
return self.age > other.age


# 创建两个Person类的实例
p1 = Person('孙悟空', 18)
p2 = Person('猪八戒', 28)

# 打印p1
# 当我们打印一个对象时,实际上打印的是对象的中特殊方法 __str__()的返回值
print("p1 = ", p1) # <__main__.Person object at 0x04E95090>
print("p2 = ", p2)

print("repr(p1) = ", repr(p1))

print("p1 > p2 : ", p1 > p2)
print("p2 > p1 : ", p2 > p1)

print("bool(p1) ", bool(p1))

if p1:
print(p1.name, '已经成年了')
else:
print(p1.name, '还未成年了')

运行上述程序,得到的结果

1
2
3
4
5
6
7
p1 =  Person [name=孙悟空 , age=18]
p2 = Person [name=猪八戒 , age=28]
repr(p1) = Hello
p1 > p2 : False
p2 > p1 : True
bool(p1) True
孙悟空 已经成年了

str函数

str 是一个类的方法,在打印类对象,获取其属性信息时调用。打印一个实例化对象时,默认打印的其实时一个对象的地址,但是我们可以对其进行重载,打印我们想要的信息。

针对 str 方法给出一个比较直观的例子

1
2
3
4
5
6
7
8
9
10
11

class people:
def __init__(self,name,age):
self.name=name
self.age=age

def __str__(self):
return '这个人的名字是%s,已经有%d岁了!'%(self.name,self.age)

a=people('孙悟空',999)
print(a)

输出:

1
2
3
4

这个人的名字是孙悟空,已经有999岁了!
如果没有重载函数的话输出的就是一串看不懂的字符串:
<__main__.people object at 0x00000272A730D278>

类的专有方法中,也是存在默认优先级的,多个方法都有返回值,但一般优先取 str() 的返回值,如下面例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14

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

v1 = Vector(2,10)
print (v1)

结果是 Vector(2,10),而不是 Vector(10,2)。这里优先使用 str() 的返回值。

1
v1.__repr__()

结果是:Vector(10,2)

5.5 封装

封装是面向对象的三大特性之一
封装指的是隐藏对象中一些不希望被外部所访问到的属性或方法

如何隐藏一个对象中的属性?

  • 将对象的属性名,修改为一个外部不知道的名字
  • 如何获取(修改)对象中的属性?
  • 需要提供一个getter和setter方法使外部可以访问到属性
  • getter 获取对象中的指定属性(get_属性名)
  • setter 用来设置对象的指定属性(set_属性名)

    使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性

  • 隐藏了属性名,使调用者无法随意的修改对象中的属性
  • 增加了getter和setter方法,很好的控制的属性是否是只读的
      如果希望属性是只读的,则可以直接去掉setter方法
       如果希望属性不能被外部访问,则可以直接去掉getter方法
    
  • 使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
  • 使用getter方法获取属性,使用setter方法设置属性
       可以在读取属性和修改属性的同时做一些其他的处理
    
  • 使用getter方法可以表示一些计算的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Dog:
'''
表示狗的类
'''

def __init__(self, name, age):
self.hidden_name = name
self.hidden_age = age

def say_hello(self):
print('大家好,我是 %s' % self.hidden_name)

def get_name(self):
'''
get_name()用来获取对象的name属性
'''
# print('用户读取了属性')
return self.hidden_name

def set_name(self, name):
# print('用户修改了属性')
self.hidden_name = name

def get_age(self):
return self.hidden_age

def set_age(self, age):
if age > 0:
self.hidden_age = age


d = Dog('旺财', 8)

d.say_hello()

# 调用setter来修改name属性
d.set_name('小黑')
d.set_age(-10)

d.say_hello()
print(d.get_age())

运行上述程序,得到的结果如下

1
2
3
大家好,我是 旺财
大家好,我是 小黑
8

5.5.1 属性隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Rectangle:
'''
表示矩形的类
'''

def __init__(self, width, height):
self.hidden_width = width
self.hidden_height = height

def get_width(self):
return self.hidden_width

def get_height(self):
return self.hidden_height

def set_width(self, width):
self.hidden_width = width

def set_height(self, height):
self.hidden_height = height

def get_area(self):
return self.hidden_width * self.hidden_height


r = Rectangle(5, 2)

r.set_width(10)
r.set_height(20)

print(r.get_area())

运行上述示例,结果为

1
200

可以为对象的属性使用双下划线开头,__xxx

双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
其实隐藏属性只不过是Python自动为属性改了一个名字
实际上是将名字修改为了,_类名属性名 比如 name -> _Person__name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person:
def __init__(self, name):
self.__name = name

def get_name(self):
return self.__name

def set_name(self, name):
self.__name = name


p = Person('孙悟空')

print(p.__name) #此语句会报错 __开头的属性是隐藏属性,无法通过对象访问

运行上述示例,得到的结果如下

1
2
3
4
Traceback (most recent call last):
File "F:\python\main.py", line 14, in <module>
print(p.__name) # __开头的属性是隐藏属性,无法通过对象访问
AttributeError: 'Person' object has no attribute '__name'

若将上述程序替换为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person:
def __init__(self, name):
self.__name = name

def get_name(self):
return self.__name

def set_name(self, name):
self.__name = name


p = Person('孙悟空')

# print(p.__name)
# __开头的属性是隐藏属性,无法通过对象访问
p.__name = '猪八戒'
print(p._Person__name)
p._Person__name = '猪八戒'

print(p.get_name())

则运行的结果为

1
2
孙悟空
猪八戒

重写示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 使用__开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用
# 一般我们会将一些私有属性(不希望被外部访问的属性)以_开头
# 一般情况下,使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性
class Person:
def __init__(self, name):
self._name = name

def get_name(self):
return self._name

def set_name(self, name):
self._name = name


p = Person('孙悟空')

print(p._name)

运行以上程序,得到的结果如下

1
孙悟空

5.5.2 装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Person:
def __init__(self, name, age):
self._name = name
self._age = age

# property装饰器,用来将一个get方法,转换为对象的属性
# 添加为property装饰器以后,我们就可以像调用属性一样使用get方法
# 使用property装饰的方法,必须和属性名是一样的
@property
def name(self):
print('get方法执行了~~~')
return self._name

# setter方法的装饰器:@属性名.setter
@name.setter
def name(self, name):
print('setter方法调用了')
self._name = name

@property
def age(self):
return self._age

@age.setter
def age(self, age):
self._age = age


p = Person('猪八戒', 18)

p.name = '孙悟空'
p.age = 28

print(p.name, p.age)

运行上述程序,得到的结果为

1
2
3
setter方法调用了
get方法执行了~~~
孙悟空 28

5.6 继承

定义一个类 Animal(动物)
这个类中需要两个方法:run() sleep()

1
2
3
4
5
6
7
8
9
class Animal:
def run(self):
print('动物会跑~~~')

def sleep(self):
print('动物睡觉~~~')

def bark(self):
print('动物嚎叫~~~')

定义一个类 Dog(狗)

这个类中需要三个方法:run() sleep() bark()

1
2
3
4
5
6
7
8
9
class Dog:
def run(self):
print('狗会跑~~~')

def sleep(self):
print('狗睡觉~~~')

def bark(self):
print('汪汪汪~~~')

有一个类,能够实现我们需要的大部分功能,但是不能实现全部功能

如何能让这个类来实现全部的功能呢?

① 直接修改这个类,在这个类中添加我们需要的功能

  • 修改起来会比较麻烦,并且会违反OCP原则

② 直接创建一个新的类

  • 创建一个新的类比较麻烦,并且需要大量的进行复制粘贴,会出现大量的重复性代码

③ 直接从Animal类中来继承它的属性和方法

  • 继承是面向对象三大特性之一
    • 通过继承我们可以使一个类获取到其他类中的属性和方法
    • 在定义类时,可以在类名后的括号中指定当前类的父类(超类、基类、super)
      子类(衍生类)可以直接继承父类中的所有的属性和方法

通过继承可以直接让子类获取到父类的方法或属性,避免编写重复性的代码,并且也符合OCP原则
所以我们经常需要通过继承来对一个类进行扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Animal:
def run(self):
print('动物会跑~~~')

def sleep(self):
print('动物睡觉~~~')

def bark(self):
print('动物嚎叫~~~')


class Dog(Animal):
def bark(self):
print('汪汪汪~~~')

def run(self):
print('狗跑~~~~')


class Hashiqi(Dog):
def fan_sha(self):
print('我是一只傻傻的哈士奇')


d = Dog()
h = Hashiqi()

d.run()
d.sleep()
d.bark()

r = isinstance(d, Dog)
r = isinstance(d, Animal)
print(r)

运行程序,得到的结果如下

1
2
3
4
狗跑~~~~
动物睡觉~~~
汪汪汪~~~
True

重写程序,得到以下结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 在创建类时,如果省略了父类,则默认父类为object

class Animal:
def run(self):
print('动物会跑~~~')

def sleep(self):
print('动物睡觉~~~')

def bark(self):
print('动物嚎叫~~~')


class Dog(Animal):
def bark(self):
print('汪汪汪~~~')

def run(self):
print('狗跑~~~~')


# object是所有类的父类,所有类都继承自object
class Person(object):
pass


# issubclass() 检查一个类是否是另一个类的子类
print(issubclass(Animal, Dog))
print(issubclass(Animal, object))
print(issubclass(Person, object))

# isinstance()用来检查一个对象是否是一个类的实例
# 如果这个类是这个对象的父类,也会返回True
# 所有的对象都是object的实例
print(isinstance(print, object))

运行结果如下

1
2
3
4
False
True
True
True

另外一种继承的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Animal:
def __init__(self,name):
self._name = name

def run(self):
print('动物会跑~~~')

def sleep(self):
print('动物睡觉~~~')

@property
def name(self):
return self._name

@name.setter
def name(self,name):
self._name = name

父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30


class Dog(Animal):

def __init__(self, name, age):
# 希望可以直接调用父类的__init__来初始化父类中定义的属性
# super() 可以用来获取当前类的父类,
# 并且通过super()返回对象调用父类方法时,不需要传递self
super().__init__(name)
self._age = age

def bark(self):
print('汪汪汪~~~')

def run(self):
print('狗跑~~~~')

@property
def age(self):
return self._age

@age.setter
def age(self, age):
self._age = age


d = Dog('旺财', 18)

print(d.name)
print(d.age)

运行结果如下

1
2
旺财
18

Python3 类方法总结

  • 普通方法:对象访问
  • 私有方法:两个下划线开头,只能在类内部访问
  • 静态方法:类和对象访问,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
  • 类方法:类和对象访问,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
  • 多继承情况下:从左到右查找方法,找到为止,不然就抛出异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class People:
# 定义基本属性
name = ''
age = 0
# 定义私有属性外部无法直接访问
__weight = 0

def __init__(self, n, a, w):
self.name = n
self.age = a
self.__weight = w

def speak(self):
print("%s say : i am %d." % (self.name, self.age))


print("People.age = ", People.age)
print("People.name = ", People.name)
# print("People.__weight = ", People.__weight) 这一行运行时会报错 AttributeError: type object 'People' has no attribute '__weight'

p = People('Python', 10, 20)
p.speak()
# __weight无法直接访问
print(p.name, '--', p.age) # ,'--',p.__weight)

print("People.age = ", People.age)
print("People.name = ", People.name)

# print(p.__weight) 这一行运行时会报错

运行结果如下

1
2
3
4
5
6
People.age =  0
People.name =
Python say : i am 10.
Python -- 10
People.age = 0
People.name =

单继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

class Student(People):
grade=''
def __init__(self,n,a,w,g):
People.__init__(self,n,a,w)
self.grade = g

# 覆写父类方法
def speak():
print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))

class Speak():
topic=''
name=''
def __init__(self,n,t):
self.name = n
self.topic = t
# 普通方法,对象调用
def speak(self):
print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))

# 私有方法,self调用
def __song(self):
print('唱一首歌自己听',self);

# 静态方法,对象和类调用,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
@staticmethod
def song():
print('唱一首歌给类听:静态方法');

# 普通方法,对象调用
def song(self):
print('唱一首歌给你们听',self);

# 类方法,对象和类调用,不能和其他方法重名,不然会相互覆盖,后面定义的会覆盖前面的
@classmethod
def song(self):
print('唱一首歌给类听:类方法',self)

5.6.1 多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class A:
def test(self):
print('AAA')


class B(object):
def test(self):
print('B中的test()方法~~')

def test2(self):
print('BBB')


# 在Python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类
# 可以在类名的()后边添加多个类,来实现多重继承
# 多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
# 在开发中没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂
# 如果多个父类中有同名的方法,则会现在第一个父类中寻找,然后找第二个,然后找第三个。。。
# 前边父类的方法会覆盖后边父类的方法
class C(A, B):
pass


# 类名.__bases__ 这个属性可以用来获取当前类的所有父类
print("C.__bases__ = ", C.__bases__)
print("B.__bases__ = ", B.__bases__)
print("A.__bases__ = ", A.__bases__)

c = C()

c.test()

运行结果如下

1
2
3
4
C.__bases__ =  (<class '__main__.A'>, <class '__main__.B'>)
B.__bases__ = (<class 'object'>,)
A.__bases__ = (<class 'object'>,)
AAA

5.7 重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 继承

# 定义一个类 Animal(动物)
# 这个类中需要两个方法:run() sleep()
class Animal:
def run(self):
print('动物会跑~~~')

def sleep(self):
print('动物睡觉~~~')


class Dog(Animal):
def bark(self):
print('汪汪汪~~~')

def run(self):
print('狗跑~~~~')

如果在子类中如果有和父类同名的方法,则通过子类实例去调用方法时,
会调用子类的方法而不是父类的方法,这个特点我们成为叫做方法的重写(覆盖,override)

1
2
3
4
# 创建Dog类的实例
# d = Dog()

# d.run()

当我们调用一个对象的方法时,会优先去当前对象中寻找是否具有该方法,如果有则直接调用.如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法,如果没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,则报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class A(object):
def test(self):
print('AAA')


class B(A):
def test(self):
print('BBB')


class C(B):
def test(self):
print('CCC')

# 创建一个c的实例


c = C()
c.test()

5.8 垃圾回收

就像我们生活中会产生垃圾一样,程序在运行过程当中也会产生垃圾。程序运行过程中产生的垃圾会影响到程序的运行的运行性能,所以这些垃圾必须被及时清理。没用的东西就是垃圾,在程序中没有被引用的对象就是垃圾,这种垃圾对象过多以后会影响到程序的运行的性能,所以我们必须进行及时的垃圾回收。所谓的垃圾回收就是讲垃圾对象从内存中删除
在Python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除,所以我们不用手动处理垃圾回收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A:
def __init__(self):
self.name = 'A类'

# del是一个特殊方法,它会在对象被垃圾回收前调用
def __del__(self):
print('A()对象被删除了~~~', self)


a = A()
b = a # 又使用一个变量b,来引用a对应的对象

print(a.name)

a = None # 将a设置为了None,此时没有任何的变量对A()对象进行引用,它就是变成了垃圾
b = None

# 使用下面的方法也可以
# del a
# del b
input('回车键退出...')

运行的结果如下

1
2
3
A类
A()对象被删除了~~~ <__main__.A object at 0x000001EE4A36DFD0>
回车键退出...

5.9 模块

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

模块(module)

模块化,模块化指将一个完整的程序分解为一个一个小的模块,通过将模块组合,来搭建出一个完整的程序。不采用模块化,统一将所有的代码编写到一个文件中,采用模块化,将程序分别编写到多个文件中
模块化的优点:
① 方便开发
② 方便维护
③ 模块可以复用!

在Python中一个py文件就是一个模块,要想创建模块,实际上就是创建一个python文件

注意:模块名要符号标识符的规范

在一个模块中引入外部模块

1
2
3
import 模块名 (模块名,就是python文件的名字,注意不要py)
或者
import 模块名 as 模块别名
  • 可以引入同一个模块多次,但是模块的实例只会创建一个
    • import可以在程序的任意位置调用,但是一般情况下,import语句都会统一写在程序的开头
    • 在每一个模块内部都有一个name属性,通过这个属性可以获取到模块的名字
    • name属性值为 main的模块是主模块,一个程序中只会有一个主模块
      主模块就是我们直接通过 python 执行的模块
1
2
3
4
5
6


import test_module as test

# print(test.__name__)
print(__name__)

import 语句

想使用 Python 源文件,只需在另一个源文件里执行 import 语句,语法如下:

1
import module1[, module2[,... moduleN]

当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。

搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support,需要把命令放在脚本的顶端:

support.py 文件代码

1
2
3
4
5
6
7

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

def print_func( par ):
print ("Hello : ", par)
return

test.py 引入 support 模块

test.py 文件代码

1
2
3
4
5
6
7
8
9

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

# 导入模块
import support

# 现在可以调用模块里包含的函数了
support.print_func("Runoob")

以上实例输出结果

1
2
$ python3 test.py 
Hello : Runoob

一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# import m

# # 访问模块中的变量:模块名.变量名
# # print(m.a , m.b)

# # m.test2()

# p = m.Person()

# print(p.name)

def test2():
print('这是主模块中的test2')


# 也可以只引入模块中的部分内容
# 语法 from 模块名 import 变量,变量....
# from m import Person
# from m import test
# from m import Person,test
# from m import * # 引入到模块中所有内容,一般不会使用
# p1 = Person()
# print(p1)
# test()
# test2()

# 也可以为引入的变量使用别名
# 语法:from 模块名 import 变量 as 别名
# from m import test2 as new_test2

# test2()
# new_test2()

from m import *
# print(_c)

# import xxx
# import xxx as yyy
# from xxx import yyy , zzz , fff
# from xxx import *
# from xxx import yyy as zz

from … import 语句

Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中,语法如下:

1
from modname import name1[, name2[, ... nameN]]

例如,要导入模块 fibo 的 fib 函数,使用如下语句:

1
2
3
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

这个声明不会把整个fibo模块导入到当前的命名空间中,它只会将fibo里的fib函数引入进来。

from … import * 语句

把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

1
from modname import *

这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

5.10 包

5.10.1 基本使用

包是一种管理 Python 模块命名空间的形式,采用”点模块名称”。

比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。

就好像使用模块的时候,你不用担心不同模块之间的全局变量相互影响一样,采用点模块名称这种形式也不用担心不同库之间的模块重名的情况。

这样不同的作者都可以提供 NumPy 模块,或者是 Python 图形库。

不妨假设你想设计一套统一处理声音文件和数据的模块(或者称之为一个”包”)。

现存很多种不同的音频文件格式(基本上都是通过后缀名区分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一组不断增加的模块,用来在不同的格式之间转换。

并且针对这些音频数据,还有很多不同的操作(比如混音,添加回声,增加均衡器功能,创建人造立体声效果),所以你还需要一组怎么也写不完的模块来处理这些操作。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)不小心的影响搜索路径中的有效模块。

最简单的情况,放一个空的 :file: init .py就可以了。当然这个文件中也可以包含一些初始化代码或者为(将在后面介绍的) all 变量赋值。

用户可以每次只导入一个包里面的特定模块,比如:

1
import sound.effects.echo

这将会导入子模块:sound.effects.echo。 他必须使用全名去访问

1
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

还有一种导入子模块的方法是

1
from sound.effects import echo

这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:

1
echo.echofilter(input, output, delay=0.7, atten=4)

还有一种变化就是直接导入一个函数或者变量:

1
from sound.effects.echo import echofilter

同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:

1
echofilter(input, output, delay=0.7, atten=4)

还有一种导入子模块的方法是:

1
from sound.effects import echo

这同样会导入子模块: echo,并且他不需要那些冗长的前缀,所以他可以这样使用:

1
echo.echofilter(input, output, delay=0.7, atten=4)

还有一种变化就是直接导入一个函数或者变量:

1
from sound.effects.echo import echofilter

同样的,这种方法会导入子模块: echo,并且可以直接使用他的 echofilter() 函数:

1
echofilter(input, output, delay=0.7, atten=4)

注意当使用 from package import item 这种形式的时候,对应的 item 既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 包 Package
# 包也是一个模块
# 当我们模块中代码过多时,或者一个模块需要被分解为多个模块时,这时就需要使用到包
# 普通的模块就是一个py文件,而包是一个文件夹
# 包中必须要一个一个 __init__.py 这个文件,这个文件中可以包含有包中的主要内容
from hello import a , b

print(a.c)
print(b.d)

# __pycache__ 是模块的缓存文件
# py代码在执行前,需要被解析器先转换为机器码,然后再执行
# 所以我们在使用模块(包)时,也需要将模块的代码先转换为机器码然后再交由计算机执行
# 而为了提高程序运行的性能,python会在编译过一次以后,将代码保存到一个缓存文件中
# 这样在下次加载这个模块(包)时,就可以不再重新编译而是直接加载缓存中编译好的代码即可

5.10.2 从一个包中导入*

如果我们使用 from sound.effects import * 会发生什么呢?

这种方式进行导入,如果不同模块之间有相同的函数命名,最后导入的会覆盖前面的,也就是说只会调用到最后导入进的函数。

Python 会进入文件系统,找到这个包里面所有的子模块,然后一个一个的把它们都导入进来。但这个方法在 Windows 平台上工作的就不是非常好,因为 Windows 是一个不区分大小写的系统。在 Windows 平台平台上,我们无法确定一个叫做 ECHO.py 的文件导入为模块是 echo 还是 Echo,或者是 ECHO。为了解决这个问题,我们只需要提供一个精确包的索引。导入语句遵循如下规则:如果包定义文件 init .py 存在一个叫做 all 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。

以下实例在 file:sounds/effects/init.py 中包含如下代码:

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

这表示当你使用from sound.effects import *这种用法时,你只会导入包里面这三个子模块。

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

这会把 init.py 里面定义的所有名字导入进来。并且他不会破坏掉我们在这句话之前导入的所有明确指定的模块。看下这部分代码:

1
2
3
import sound.effects.echo
import sound.effects.surround
from sound.effects import *

这个例子中,在执行 from…import 前,包 sound.effects 中的 echo 和 surround 模块都被导入到当前的命名空间中了。(当然如果定义了 all 就更没问题了)

通常我们并不主张使用 * 这种方法来导入模块,因为这种方法经常会导致代码的可读性降低。不过这样倒的确是可以省去不少敲键的功夫,而且一些模块都设计成了只能通过特定的方法导入。

记住,使用 from Package import specific_submodule 这种方法永远不会有错。事实上,这也是推荐的方法。除非是你要导入的子模块有可能和其他包的子模块重名。

如果在结构中包是一个子包(比如这个例子中对于包sound来说),而你又想导入兄弟包(同级别的包)你就得使用导入绝对的路径来导入。比如,如果模块sound.filters.vocoder 要使用包 sound.effects 中的模块 echo,你就要写成 from sound.effects import echo。

1
2
3
from . import echo
from .. import formats
from ..filters import equalizer

无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是”main“,一个Python应用程序的主模块,应当总是使用绝对路径引用。

包还提供一个额外的属性 path 。这是一个目录列表,里面每一个包含的目录都有为这个包服务的 init .py,你得在其他 init .py被执行前定义哦。可以修改这个变量,用来影响包含在包里面的模块和子包。

这个功能并不常用,一般用来扩展包里面的模块。

5.11 标准库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 开箱即用
# 为了实现开箱即用的思想,Python中为我们提供了一个模块的标准库
# 在这个标准库中,有很多很强大的模块我们可以直接使用,
# 并且标准库会随Python的安装一同安装
# sys模块,它里面提供了一些变量和函数,使我们可以获取到Python解析器的信息
# 或者通过函数来操作Python解析器
# 引入sys模块
import sys

# pprint 模块它给我们提供了一个方法 pprint() 该方法可以用来对打印的数据做简单的格式化
import pprint

# sys.argv
# 获取执行代码时,命令行中所包含的参数
# 该属性是一个列表,列表中保存了当前命令的所有参数
# print(sys.argv)

# sys.modules
# 获取当前程序中引入的所有模块
# modules是一个字典,字典的key是模块的名字,字典的value是模块对象
# pprint.pprint(sys.modules)

# sys.path
# 他是一个列表,列表中保存的是模块的搜索路径
# ['C:\\Users\\lilichao\\Desktop\\resource\\course\\lesson_06\\code',
# 'C:\\dev\\python\\python36\\python36.zip',
# 'C:\\dev\\python\\python36\\DLLs',
# 'C:\\dev\\python\\python36\\lib',
# 'C:\\dev\\python\\python36',
# 'C:\\dev\\python\\python36\\lib\\site-packages']
# pprint.pprint(sys.path)

# sys.platform
# 表示当前Python运行的平台
# print(sys.platform)

# sys.exit()
# 函数用来退出程序
# sys.exit('程序出现异常,结束!')
# print('hello')

# os 模块让我们可以对操作系统进行访问
import os

# os.environ
# 通过这个属性可以获取到系统的环境变量
# pprint.pprint(os.environ['path'])

# os.system()
# 可以用来执行操作系统的名字
# os.system('dir')
os.system('notepad')

六 异常

Python3 内置异常类型的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ArithmeticError
| +-- FloatingPointError
| +-- OverflowError
| +-- ZeroDivisionError
+-- AssertionError
+-- AttributeError
+-- BufferError
+-- EOFError
+-- ImportError
| +-- ModuleNotFoundError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- MemoryError
+-- NameError
| +-- UnboundLocalError
+-- OSError
| +-- BlockingIOError
| +-- ChildProcessError
| +-- ConnectionError
| | +-- BrokenPipeError
| | +-- ConnectionAbortedError
| | +-- ConnectionRefusedError
| | +-- ConnectionResetError
| +-- FileExistsError
| +-- FileNotFoundError
| +-- InterruptedError
| +-- IsADirectoryError
| +-- NotADirectoryError
| +-- PermissionError
| +-- ProcessLookupError
| +-- TimeoutError
+-- ReferenceError
+-- RuntimeError
| +-- NotImplementedError
| +-- RecursionError
+-- SyntaxError
| +-- IndentationError
| +-- TabError
+-- SystemError
+-- TypeError
+-- ValueError
| +-- UnicodeError
| +-- UnicodeDecodeError
| +-- UnicodeEncodeError
| +-- UnicodeTranslateError
+-- Warning
+-- DeprecationWarning
+-- PendingDeprecationWarning
+-- RuntimeWarning
+-- SyntaxWarning
+-- UserWarning
+-- FutureWarning
+-- ImportWarning
+-- UnicodeWarning
+-- BytesWarning
+-- ResourceWarning

对于下面的示例

1
2
3
4
5
6
7
8
9
10
11
12
print('hello')
try:
# try中放置的是有可能出现错误的代码
print(10 / 0)
except:
# except中放置的是出错以后的处理防暑
print('哈哈哈,出错了~~~')
else:
print('程序正常执行没有错误')
print('你好')

print(10 / 0) # 这里是第12行

运行结果为

1
2
3
4
5
6
7
Traceback (most recent call last):
File "F:\python\main.py", line 12, in <module>
print(10 / 0)
ZeroDivisionError: division by zero
hello
哈哈哈,出错了~~~
你好

而对于下面的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def fn():
print('Hello fn')
print(a) # 这里第3行
print(10 / 0)


def fn2():
print('Hello fn2')
fn() # 这里第9行


def fn3():
print('Hello fn3')
fn2() # 这里第14行


fn3() # 这里第17行

运行结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
Traceback (most recent call last):
File "F:\python\main.py", line 17, in <module>
fn3()
File "F:\python\main.py", line 14, in fn3
fn2()
File "F:\python\main.py", line 9, in fn2
fn()
File "F:\python\main.py", line 3, in fn
print(a)
NameError: name 'a' is not defined
Hello fn3
Hello fn2
Hello fn

6.1 异常对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
print('异常出现前')
l = []
try:
# print(c)
# l[10]
# 1 + 'hello'
print(10 / 0)
except NameError:
# 如果except后不跟任何的内容,则此时它会捕获到所有的异常
# 如果在except后跟着一个异常的类型,那么此时它只会捕获该类型的异常
print('出现 NameError 异常')
except ZeroDivisionError:
print('出现 ZeroDivisionError 异常')
except IndexError:
print('出现 IndexError 异常')
# Exception 是所有异常类的父类,所以如果except后跟的是Exception,他也会捕获到所有的异常
# 可以在异常类后边跟着一个 as xx 此时xx就是异常对象
except Exception as e:
print('未知异常', e, type(e))
finally:
print('无论是否出现异常,该子句都会执行')

print('异常出现后')

运行上述代码,结果为

1
2
3
4
异常出现前
出现 ZeroDivisionError 异常
无论是否出现异常,该子句都会执行
异常出现后

在 python3 中,处理带有参数的异常的方法如下

1
2
3
4
5
6
7
8
9
10
# 定义函数
def temp_convert(var):
try:
return int(var)
except (ValueError) as Argument:
print("参数没有包含数字\n", Argument)


# 调用函数
temp_convert("xyz");

运行结果为

1
2
参数没有包含数字
invalid literal for int() with base 10: 'xyz'

6.2 抛出异常

Python 使用 raise 语句抛出一个指定的异常。

raise语法格式如下:

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

以下实例如果 x 大于 5 就触发异常:

1
2
3
x = 10
if x > 5:
raise Exception('x 不能大于 5。x 的值为: {}'.format(x))

执行以上代码会触发异常:

1
2
3
4
Traceback (most recent call last):
File "test.py", line 3, in <module>
raise Exception('x 不能大于 5。x 的值为: {}'.format(x))
Exception: x 不能大于 5。x 的值为: 10

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

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

6.3 用户自定义异常

你可以通过创建一个新的异常类来拥有自己的异常。异常类继承自 Exception 类,可以直接继承,或者间接继承,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 也可以自定义异常类,只需要创建一个类继承Exception即可
class MyError(Exception):
pass


def add(a, b):
# 如果a和b中有负数,就向调用处抛出异常
if a < 0 or b < 0:
# raise用于向外部抛出异常,后边可以跟一个异常类,或异常类的实例
# raise Exception
# 抛出异常的目的,告诉调用者这里调用时出现问题,希望你自己处理一下
# raise Exception('两个参数中不能有负数!')
raise MyError('自定义的异常')

# 也可以通过if else来代替异常的处理
# return None
r = a + b
return r


print(add(-123, 456))

输出的结果为

1
2
3
4
5
6
Traceback (most recent call last):
File "F:\python\main.py", line 21, in <module>
print(add(-123, 456))
File "F:\python\main.py", line 13, in add
raise MyError('自定义的异常')
__main__.MyError: 自定义的异常

6.4 异常捕获

使用一个快捕捉多个异常

1
2
3
4
5
6
7
8
9
10
def model_exception(x, y):
try:
b = name
a = x / y
except(ZeroDivisionError, NameError, TypeError):
print('one of ZeroDivisionError or NameError or TypeError happend')


# 调用函数结果
model_exception(2, 0)

输出如下:

1
one of ZeroDivisionError or NameError or TypeError happend

捕获所有异常

1
2
3
4
5
try:
...
except Exception as e:
...
log('Reason:', e) # Important!

这个将会捕获除了 SystemExit 、 KeyboardInterrupt 和 GeneratorExit 之外的所有异常。 如果你还想捕获这三个异常,将 Exception 改成 BaseException 即可。

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

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

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

1
2
except (RuntimeError, TypeError, NameError):
pass

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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

运行结果为

1
OS error: [Errno 2] No such file or directory: 'myfile.txt'

一个最全面的示例

1
2
3
4
5
6
7
8
9
10
11
12
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('这句话,无论异常是否发生都会执行。')

运行结果为

1
2
3
4
5
Traceback (most recent call last):
File "F:\python\main.py", line 2, in <module>
runoob()
NameError: name 'runoob' is not defined
这句话,无论异常是否发生都会执行。

七 文件读写

7.1 打开文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# open(file, mode='r', buffering=-1, encoding_=None, errors=None, newline=None, closefd=True, opener=None)
# 使用open函数来打开一个文件
# 参数:
# file 要打开的文件的名字(路径)
# 返回值:
# 返回一个对象,这个对象就代表了当前打开的文件

# 创建一个变量,来保存文件的名字
# 如果目标文件和当前文件在同一级目录下,则直接使用文件名即可
file_name = 'demo.txt'

# 在windows系统使用路径时,可以使用/来代替 \
# 或者可以使用 \\ 来代替 \
# 或者也可以使用原始字符串
file_name = 'hello\\demo.txt'
file_name = r'hello\demo.txt'

# 表示路径,可以使用..来返回一级目录
file_name = '../hello/demo.txt'

# 如果目标文件距离当前文件比较远,此时可以使用绝对路径
# 绝对路径应该从磁盘的根目录开始书写
file_name = r'C:\Users\lilichao\Desktop\hello.txt'

file_obj = open(file_name) # 打开 file_name 对应的文件

# print(file_obj)

7.2 关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 打开文件
file_name = 'demo.txt'

# 调用open()来打开文件
# file_obj = open(file_name)

# # 当我们获取了文件对象以后,所有的对文件的操作都应该通过对象来进行
# # 读取文件中的内容
# # read()方法,用来读取文件中的内容,它会将内容全部保存为一个字符串返回
# content = file_obj.read()

# print(content)

# # 关闭文件
# # 调用close()方法来关闭文件
# file_obj.close()

# with ... as 语句
# with open(file_name) as file_obj :
# # 在with语句中可以直接使用file_obj来做文件操作
# # 此时这个文件只能在with中使用,一旦with结束则文件会自动close()
# print(file_obj.read())


file_name = 'hello'

try:
with open(file_name) as file_obj:
print(file_obj.read())
except FileNotFoundError:
print(f'{file_name} 文件不存在~~')

7.3 文件的读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
file_name = 'demo2.txt'

try:
# 调用open()来打开一个文件,可以将文件分成两种类型
# 一种,是纯文本文件(使用utf-8等编码编写的文本文件)
# 一种,是二进制文件(图片、mp3、ppt等这些文件)
# open()打开文件时,默认是以文本文件的形式打开的,但是open()默认的编码为None
# 所以处理文本文件时,必须要指定文件的编码
with open(file_name, encoding='utf-8') as file_obj:
# 通过 read() 来读取文件中的内容
# 如果直接调用read()它会将文本文件的所有内容全部都读取出来
# 如果要读取的文件较大的话,会一次性将文件的内容加载到内存中,容易导致内存泄漏
# 所以对于较大的文件,不要直接调用read()
# help(file_obj.read)
# read()可以接收一个size作为参数,该参数用来指定要读取的字符的数量
# 默认值为-1,它会读取文件中的所有字符
# 可以为size指定一个值,这样read()会读取指定数量的字符,
# 每一次读取都是从上次读取到位置开始读取的
# 如果字符的数量小于size,则会读取剩余所有的
# 如果已经读取到了文件的最后了,则会返回''空串
# content = file_obj.read(-1)
content = file_obj.read(6)
content = file_obj.read(6)
content = file_obj.read(6)
content = file_obj.read(6)
# print(content)
# print(len(content))
except FileNotFoundError:
print(f'{file_name} 这个文件不存在!')

读取一个大文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 读取大文件的方式
file_name = 'demo.txt'

try:
with open(file_name, encoding='utf-8') as file_obj:
# 定义一个变量,来保存文件的内容
file_content = ''
# 定义一个变量,来指定每次读取的大小
chunk = 100
# 创建一个循环来读取文件内容
while True:
# 读取chunk大小的内容
content = file_obj.read(chunk)

# 检查是否读取到了内容
if not content:
# 内容读取完毕,退出循环
break

# 输出内容
# print(content,end='')
file_content += content

except FileNotFoundError:
print(f'{file_name} 这个文件不存在!')

print(file_content)

另一个示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pprint
import os

file_name = 'demo.txt'

with open(file_name, encoding='utf-8') as file_obj:
# readline()
# 该方法可以用来读取一行内容
# print(file_obj.readline(),end='')
# print(file_obj.readline())
# print(file_obj.readline())

# readlines()
# 该方法用于一行一行的读取内容,它会一次性将读取到的内容封装到一个列表中返回
# r = file_obj.readlines()
# pprint.pprint(r[0])
# pprint.pprint(r[1])
# pprint.pprint(r[2])

for t in file_obj:
print(t)

7.4 文件的写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
file_name = 'demo5.txt'

# 使用open()打开文件时必须要指定打开文件所要做的操作(读、写、追加)
# 如果不指定操作类型,则默认是 读取文件 , 而读取文件时是不能向文件中写入的
# r 表示只读的
# w 表示是可写的,使用w来写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件
# 截断文件指删除原来文件中的所有内容
# a 表示追加内容,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容
# x 用来新建文件,如果文件不存在则创建,存在则报错
# + 为操作符增加功能
# r+ 即可读又可写,文件不存在会报错
# w+
# a+
# with open(file_name , 'w' , encoding='utf-8') as file_obj:
# with open(file_name , 'r+' , encoding='utf-8') as file_obj:
with open(file_name, 'x', encoding='utf-8') as file_obj:
# write()来向文件中写入内容,
# 如果操作的是一个文本文件的话,则write()需要传递一个字符串作为参数
# 该方法会可以分多次向文件中写入内容
# 写入完成以后,该方法会返回写入的字符的个数
file_obj.write('aaa\n')
file_obj.write('bbb\n')
file_obj.write('ccc\n')
r = file_obj.write(str(123) + '123123\n')
r = file_obj.write('今天天气真不错')
print(r)

将一个文件里的内容写入到另一个文件‘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
file_name = 'c:/Users/lilichao/Desktop/告白气球.flac'

# 读取模式
# t 读取文本文件(默认值)
# b 读取二进制文件

with open(file_name, 'rb') as file_obj:
# 读取文本文件时,size是以字符为单位的
# 读取二进制文件时,size是以字节为单位
# print(file_obj.read(100))

# 将读取到的内容写出来
# 定义一个新的文件
new_name = 'aa.flac'

with open(new_name, 'wb') as new_obj:

# 定义每次读取的大小
chunk = 1024 * 100

while True:
# 从已有的对象中读取数据
content = file_obj.read(chunk)

# 内容读取完毕,终止循环
if not content:
break

# 将读取到的数据写入到新对象中
new_obj.write(content)