1. 变量和简单数据类型
1.1 变量命名和使用
变量命名规则:
- 只能包含字母(abc…)、数字(123..)、下划线(_),且不能以数字开头
- 不能用空格、应用下划线来隔开不同单词
- 使用小写的变量名,且简短又具有描述性,如name_length比length_of_persons_name好,name比n好
- 慎用小写字母l和大写字母O,容易被错看成数字1和0
1.2 字符串
用引号或双引号括起来的都是字符串。在字符串中含有单引号时,需将字符串有双引号括起来。
函数用法
str.makerans
,创建字符与字符之间的映射表,str是一个类intab = "aeiou"
outtab = "12345"
trantab = str.maketrans(intab, outtab)
print(trantab)
# {97: 49, 101: 50, 105: 51, 111: 52, 117: 53}
string.translate(trantab)
,根据映射表对string进行映射,如果映射表没有该字符,则不映射。str1 = "this is string example....wow!!!" print (str1.translate(trantab)) #th3s 3s str3ng 2x1mpl2....w4w!!!
str.split(str="", num=string.count(str))
按照str
将字符串分割成num
个并存入列表中
str.strip(str='')
移除字符串中首尾指定的字符,首尾有多个连续一样的字符也会移除,
1.3 数字
python使用两个*来表示乘方(幂)运算。
在用+号进行字符串拼接时,如果包含数字,python无法自动将其转换为字符串,需使用str()函数。
1.4 格式化输出
- %06d,表示输出的整数显示位数,不⾜以0补全,超出当前位数则原样输出
- %6d,表示输出的整数显示位数,不足以空格补全
- %.2f,表示⼩数点后显示的⼩数位数。
- 还可用f’{表达式}’
```python
age = 18
name = ‘TOM’
weight = 75.5
student_id = 1
我的名字是TOM
print(‘我的名字是%s’ % name)我的学号是0001
print(‘我的学号是%4d’ % student_id)我的体重是75.50公⽄
print(‘我的体重是%.2f公⽄’ % weight)我的名字是TOM,今年18岁了
print(‘我的名字是%s,今年%d岁了’ % (name, age))
我的名字是TOM,明年19岁了
print(‘我的名字是%s,明年%d岁了’ % (name, age + 1))
我的名字是TOM,明年19岁了
print(f’我的名字是{name}, 明年{age + 1}岁了’)
---
<a name="7YVPn"></a>
# 2. 列表
<a name="yCMV2"></a>
## 2.1 在列表中添加元素
- `list.append()`,在列表末尾添加一个元素
- `list.insert(index,value)`,在列表index处添加一个元素,此操作将把index处以及index后的所有元素向右平移一个位置
---
在列表中删除元素:
- `del list[index]`,删除列表index处的元素。
- `value=list.pop(index)`,删除列表index处的值,并将该值返回。index默认为列表最后一个元素索引。
注意:在删除元素不是列表最后一个时,删除过后,列表中的所有元素的位置会调整,即索引会向左移一位。
- `list.remove(value)`,删除列表**第一个**值为value的元素。
---
<a name="RibwJ"></a>
## 2.2 对列表排序
- `list.sort(reverse=False)`,对原列表**按顺序(ASCLL码顺序)**从小到大排列,当reverse为True时,逆序。
- `sorted(list,reverse=False)`,与sort()一样,但sorted不改变原列表,而是返回一个新的排序后的列表。
- `list.reverse()`,对反转列表,这里的反转是**指索引顺序**的反转。
---
<a name="N1AQ5"></a>
## 2.3 创建数值列表
- `list(range(start,end,stride))`,使用range创建迭代式,使用list()将其转换成列表,创建从start到end的列表,不包含end,步长为stride,默认为1。
---
<a name="vTlms"></a>
## 2.4 对数字列表进行统计运算
- `min(list);max(list);sum(list)`
---
<a name="3YgFT"></a>
## 2.5 列表解析式
`list = [function(value),for value in range()]`
- `list = [value**2 for value in range(1,11)]`,表示遍历1到10,对其做求平方运算,并将运算后的值添加到列表中。
<a name="mcFmx"></a>
## 2.6 列表访问(切片)
- **访问最后一个元素**时,可使用`list[-1]`。避免索引出错。
- `newlist=list[x:y]`,切取原列表**索引位置x到y-1(不包含y**)的元素并返回一个新的列表。列表索引从0开始。
- `newlist=list[:y];newlist=list[x:]`切取0到y-1的元素;切取x到列表最后一个元素(包含该元素)。
- `newlist=list[-x:]`切取列表最后x个元素。
- `newlist=list[::x]`,对list列表进行切片,每隔x个元素提取一个元素出来
```python
list_1 = [1,2,3,4,5]
newlist = list_1[::2]
print(newlist) # [1,3,5]
2.7 复制列表
newlist=list[:]
,复制一个新列表,本质是切片。注意不要直接把列表赋给一个新的变量,这没有意义。
3. 集合
- 创建集合的方式
set = {'1','2','3'}
- 集合之间的运算
name1 = {'Alex', 'Jack', 'Rain', 'Ruby', 'Mack'} name2 = {'铁蛋', '催化', '赵四', 'Alex', 'Mack'} # 交集 name1.intersection(name2) # 返回一个集合{'Alex', 'Mack'} # 并集 合并两个集合的元素并去除重复,返回一个新的集合 name1.union(name2) # {'赵四', 'Alex', '催化', 'Ruby', '铁蛋', 'Mack', 'Jack', 'Rain'} # 差集 只在集合name1,不再集合name2的元素,返回一个新的集合 name1.difference(name2)# {'Ruby', 'Rain', 'Jack'}
集合的特点
- 集合内部不允许重复的元素
- 序
- 关系测试
4. 字典
4.1 创建与修改字典:
dict={}
,创建一个空字典dict[key]=value
,如果字典中没有键key,则添加新的键值对,如果已经有键key,则修改该键的value。del dict[key]
,删除字典中的键key对应的键值对。
4.2 字典遍历:
for key, value in dict.items()
,遍历字典的键和值。注意遍历的顺序不一定与存储时的顺序一样。for key in dict.keys()
,遍历字典的所有键。for value in dict.values()
,遍历字典的所有值,包含重复的值。可使用set()来去除重复的值
5. 用户输入与while循环小tips
return = input('字符串')
,获取键盘输入,并返回,注意:不管输入什么,return都是字符串类型。- 删除包含特定值的所有列表元素:
知道list中不再有value时,才停止循环,这可以删除list中的所有value元素。while value in list: list.remove(value)
- 在for循环和while循环后可以加else语句,该语句是循环正常结束之后执行的代码
```python
str1 = ‘itheima’
for i in str1:
if i == ‘e’:
else: print(‘循环正常结束之后执⾏的代码’)print('遇到e不打印') break print(i)
<a name="9WcWw"></a>
# 6. 函数
tips:在函数定义时,第一行加上三引号圈起来的字符串表示函数描述信息
```python
def function():
"""描述信息"""
pass
6.1 变量作用域
变量作⽤域指的是变量⽣效的范围,主要分为两类:局部变量和全局变量。
- 局部变量
定义在函数体内部的变量,只在函数体内部生效。当函数调用完成后,则销毁局部变量。
- 全局变量
在函数体内、外都能生效的变量。
如果有有一个数据,函数A和B都要使用,则将该数据存储在一个全局变量里。
a = 100
def testA():
print(a)
def testB():
a = 200
print(a)
testA() # 100
testB() # 200
print(f'全局变量a = {a}') # 全局变量a = 100
思考:在testB
函数内部的a = 200
中的变量a是在修改全局变量 a 吗?
答:不是。观察上述代码发现,15⾏得到a的数据是100,仍然是定义全局变量a时候的值,而没有返回。
也就是说,在函数体内部是不能修改全局变量的。
思考:如何在函数体内部修改全局变量?
在函数中用global
关键字声明该变量是全局变量。此时a=200
就是在修改全局变量的a而不是新建一个局部变量a
def testB():
# global 关键字声明a是全局变量
global a
a = 200
6.2 函数的参数
- 位置参数
调⽤函数时根据函数定义的参数位置来传递参数。
def user_info(name, age, gender):
print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
user_info('TOM', 20, '男')
注意:传递和定义参数的顺序及个数必须⼀致。
- 关键字参数
函数调⽤,通过“键=值”形式加以指定。可以让函数更加清晰、容易使⽤,同时也清除了参数的顺序需求。
def user_info(name, age, gender):
print(f'您的名字是{name}, 年龄是{age}, 性别是{gender}')
user_info('Rose', age=20, gender='⼥')
user_info('⼩明', gender='男', age=16)
注意:函数调⽤时,如果有位置参数时,位置参数必须在关键字参数的前⾯,但关键字参数之间不存在先后顺序。
- 不定长参数
不定⻓参数也叫可变参数。⽤于不确定调⽤的时候会传递多少个参数(不传参也可以)的场景。此时,可⽤包裹(packing)位置参数,或者包裹关键字参数,来进⾏参数传递,会显得⾮常⽅便。
- 包裹位置传递 ```python def user_info(*args): print(args)
user_info(‘TOM’) # (‘TOM’,)
user_info(‘TOM’, 18) # (‘TOM’, 18)
注意:传进的所有参数都会被args变量收集,它会**根据传进参数的位置合并为⼀个元组(tuple)**, args是元组类型,这就是包裹位置传递。
- 包裹关键字传递
```python
def user_info(**kwargs):
print(kwargs)
# {'name': 'TOM', 'age': 18, 'id': 110}
user_info(name='TOM', age=18, id=110)
6.2 引用
在python中,值是靠引⽤来传递来的。 我们可以⽤id()
来判断两个变量是否为同⼀个值的引⽤。 我们可以将id值理解为那块内存的地址标识。
# 1. int类型
a = 1
b = a
print(b) # 1
print(id(a)) # 140708464157520
print(id(b)) # 140708464157520
a = 2
print(b) # 1,说明int类型为不可变类型
print(id(a)) # 140708464157552,此时得到是的数据2的内存地址
print(id(b)) # 140708464157520
# 2. 列表
aa = [10, 20]
bb = aa
print(id(aa)) # 2325297783432
print(id(bb)) # 2325297783432
aa.append(30)
print(bb) # [10, 20, 30], 列表为可变类型
print(id(aa)) # 2325297783432
print(id(bb)) # 2325297783432
不可变类型的意思是如果变量a和b地址相同,此时改变变量a的值,b的值不会变。
可变类型的意思是,改变变量a的值,则b的值也会改变。
- 可变类型
- 列表
- 字典
- 集合
- 不可变类型
- 整型
- 浮点型
- 字符串
- 元组
对于不可变类型,如果值改变了,其id值也会变。而对于可变类型,值改变了,id值不会变
注意:因此当列表作为参数传入函数时,如果不希望改变原列表的值,则实参写成
list[:]
6.3 lambda表达式
1 lambda 参数列表 : 表达式
- lambda表达式的参数可有可⽆,函数的参数在lambda表达式中完全适⽤。
- lambda函数能接收任何数量的参数但只能返回⼀个表达式的值
fn = lambda x, y:x+y print(fn(2,3)) # 5
lambda的应用
- 带判断的lambda
列表数据按字典key的值排序 ```python students = [ {‘name’: ‘TOM’, ‘age’: 20}, {‘name’: ‘ROSE’, ‘age’: 19}, {‘name’: ‘Jack’, ‘age’: 22} ]print((lambda **kwargs: kwargs)(name='python', age=20))
按name值升序排列
students.sort(key=lambda x: x[‘name’]) print(students)
按name值降序排列
students.sort(key=lambda x: x[‘name’], reverse=True) print(students)
按age值升序排列
students.sort(key=lambda x: x[‘age’]) print(students)
这里,参数key是排序用关键字,lamda表达式中x是列表里的每个元素,即字典,对于name,返回字典键为name的值传给sort函数的参数key用于排序时比较的对象。
---
<a name="886d064c"></a>
## 6.4 高阶函数
把函数作为参数传⼊,这样的函数称为⾼阶函数,⾼阶函数是函数式编程的体现。函数式编程就是指这种⾼度抽象的编程范式。
- 高阶函数示例
```python
def sum_num(a, b, f):
return f(a) + f(b)
result = sum_num(-1, 2, abs)
print(result) # 3
- 内置高阶函数
- map()
map(func, lst),将传⼊的函数变量func作⽤到lst变量的每个元素中,并将结果组成新的列表(Python2)/ 迭代器(Python3)返回。
list1 = [1, 2, 3, 4, 5]
def func(x):
return x ** 2
result = map(func, list1)
print(result) # <map object at 0x0000013769653198>
print(list(result)) # [1, 4, 9, 16, 25]
- reduce()
reduce(func(x,y),lst),其中func必须有两个参数。每次func计算的结果继续和序列的下⼀个元素做累积计算。
注意:reduce()传⼊的参数func必须接受2个参数。
# 计算 list1 序列中各个数字的累加和。
import functools
list1 = [1, 2, 3, 4, 5]
def func(a, b):
return a + b
result = functools.reduce(func, list1) # 即func的返回值和列表中的下一个元素会作为参数传入func
print(result) # 15
- filter()
filter(func, lst)函数⽤于过滤序列, 过滤掉不符合条件的元素, 返回⼀个 filter 对象(属于迭代器,可以用for循环迭代,但不能用下标取出元素)。如果要转换为列表, 可以使⽤ list() 来转换。
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def func(x):
return x % 2 == 0
result = filter(func, list1)
print(result) # <filter object at 0x0000017AF9DC3198>
print(list(result)) # [2, 4, 6, 8, 10]
注意:func函数需要返回一个bool值用于判断是否过滤掉此元素。
7. 文件操作
7.1 文件的基本操作
- 打开
name:是要打开的⽬标⽂件名的字符串(可以包含⽂件所在的具体路径)。open(name, mode)
mode:设置打开⽂件的模式(访问模式):只读、写⼊、追加等。(字符串)
模式 | 描述 |
---|---|
r | 以只读⽅式打开⽂件。⽂件的指针将会放在⽂件的开头。这是默认模式。 |
rb | 以⼆进制格式打开⼀个⽂件⽤于只读。⽂件指针将会放在⽂件的开头。这是默认模式。 |
r+ | 打开⼀个⽂件⽤于读写。⽂件指针将会放在⽂件的开头。 |
rb+ | 以⼆进制格式打开⼀个⽂件⽤于读写。⽂件指针将会放在⽂件的开头。 |
w | 打开⼀个⽂件只⽤于写⼊。如果该⽂件已存在则打开⽂件,并从开头开始编辑,即原有内容会被删除。如果该⽂件不存在,创建新⽂件。 |
wb | 以⼆进制格式打开⼀个⽂件只⽤于写⼊。如果该⽂件已存在则打开⽂件,并从开头开始编辑,即原有内容会被删除。如果该⽂件不存在,创建新⽂件。 |
w+ | 打开⼀个⽂件⽤于读写。如果该⽂件已存在则打开⽂件,并从开头开始编辑,即原有内容会被删除。如果该⽂件不存在,创建新⽂件。 |
wb+ | 以⼆进制格式打开⼀个⽂件⽤于读写。如果该⽂件已存在则打开⽂件,并从开头开始编 辑,即原有内容会被删除。如果该⽂件不存在,创建新⽂件。 |
a | 打开⼀个⽂件⽤于追加。如果该⽂件已存在,⽂件指针将会放在⽂件的结尾。也就是说, 新的内容将会被写⼊到已有内容之后。如果该⽂件不存在,创建新⽂件进⾏写⼊。 |
ab | 以⼆进制格式打开⼀个⽂件⽤于追加。如果该⽂件已存在,⽂件指针将会放在⽂件的结 尾。也就是说,新的内容将会被写⼊到已有内容之后。如果该⽂件不存在,创建新⽂件进 ⾏写⼊。 |
a+ | 打开⼀个⽂件⽤于读写。如果该⽂件已存在,⽂件指针将会放在⽂件的结尾。⽂件打开时会是追加模式。如果该⽂件不存在,创建新⽂件⽤于读写。 |
ab+ | 以⼆进制格式打开⼀个⽂件⽤于追加。如果该⽂件已存在,⽂件指针将会放在⽂件的结尾。如果该⽂件不存在,创建新⽂件⽤于读写。 |
- 文件对象方法
- 写 ```python 对象.write(‘内容’)
1. 打开⽂件
f = open(‘test.txt’, ‘w’)
2.⽂件写⼊
f.write(‘hello world’)
3. 关闭⽂件
f.close()
- 读
- `⽂件对象.read(num)`
> num表示要从⽂件中读取的数据的⻓度(单位是字节),如果没有传⼊num,那么就表示读取⽂件中所有的数据。
- `文件对象.readlines()`
readlines可以按照**⾏的⽅式**把整个⽂件中的内容进⾏**⼀次性读取**,并且**返回的是⼀个列表**,其中每⼀⾏ 的数据为⼀个元素。
- `文件对象.readline()`
readline()⼀次读取⼀⾏内容。
<a name="UTu9D"></a>
## 7.2 文件备份
需求:⽤户输⼊当前⽬录下任意⽂件名,程序完成对该⽂件的备份功能(备份⽂件名为xx[备份]后缀,例如:test[备份].txt)。
```python
# 1. 接收⽤户输⼊⽬标⽂件名
old_name = input('请输⼊您要备份的⽂件名:')
# 2. 规划备份⽂件名
# 2.1 提取⽂件后缀点的位置下标
index = old_name.rfind('.')
if index > 0: # 如果index==0,则说明文件名有误
postfix = old_name[index:]
# print(index) # 后缀中.的下标
# print(old_name[:index]) # 源⽂件名(⽆后缀)
# 2.2 组织新⽂件名 旧⽂件名 + [备份] + 后缀
new_name = old_name[:index] + '[备份]' + postfix
# 打印新⽂件名(带后缀)
# print(new_name)
# 3. 备份⽂件写⼊数据
# 3.1 打开⽂件
old_f = open(old_name, 'rb')
new_f = open(new_name, 'wb')
# 3.2 将源⽂件数据写⼊备份⽂件
while True:
con = old_f.read(1024)
if len(con) == 0:
break
new_f.write(con)
# 3.3 关闭⽂件
old_f.close()
new_f.close()
7.3 文件和文件夹的操作
在Python中⽂件和⽂件夹的操作要借助os模块⾥⾯的相关功能。
- 文件重命名
os.remove(目标文件名,新文件名)
- 删除文件
os.remove(目标文件名)
- 创建文件夹
os.mkdir(文件夹名字)
- 删除文件夹
os.rmdir(文件夹名字)
- 获取当前目录
os.getcwd()
- 改变默认目录
os.chdir(目录)
- 获取目录列表
os.listdir(目录)
应用案例:批量修改文件名
import os
# 设置重命名标识:如果为1则添加指定字符,flag取值为2则删除指定字符
flag = 1
# 获取指定⽬录
dir_name = './'
# 获取指定⽬录的⽂件列表
file_list = os.listdir(dir_name)
# print(file_list)
# 遍历⽂件列表内的⽂件
for name in file_list:
# 添加指定字符
if flag == 1:
new_name = 'Python-' + name
# 删除指定字符
elif flag == 2:
num = len('Python-')
new_name = name[num:]
# 打印新⽂件名,测试程序正确性
print(new_name)
# 重命名
os.rename(dir_name+name, dir_name+new_name)
7.4 编码解码的问题
在文件存储和读取文件时涉及到编码、解码的问题。对于GBK
编码与utf-8
编码的转换,需要先转换成Unicode
编码再转换成其他编码,因为Unicode
编码和其他编码都有映射关系。
- 编码与解码
- 任意编码转换成unicode的过程,都叫解码
- 把unicode转换成任意编码的过程 ,都叫编码
不同字符编码间的转换
字符串编码成
utf-8
s='路飞' utf = s.encode("utf-8") # b'\xe8\xb7\xaf\xe9\xa3\x9e' # 会变成bytes字节格式,
编码过后会变成二进制,如果要编码成其他格式需要将其解码,在编码
# 解码 s = utf.decode("utf-8") # 因为utf是以utf-8来编码的,所以解码也需要用utf-8 #在编码成其他格式 gbk = s.encode("gbk")
bytes字节类型是⽤16进制表示的, 像这样 \xe8 这样2个16进制数是代表⼀个字节(因为⼀个16 进制是占4位,2个就是8位,共1个字节啦),其实就是二进制格式
8. 面向对象基础
8.1 self
self是指调用该函数的对象。
class Washer():
def wash(self):
print('我会洗⾐服')
print(self)
# 2. 创建对象
haier1 = Washer()
print(haier1) #<__main__.Washer object at 0x000001D0DA1C3F28>
# haier1对象调⽤实例⽅法
haier1.wash() #<__main__.Washer object at 0x000001D0DA1C3F28>
#-----------------------------------------------------------------
haier2 = Washer()
# <__main__.Washer object at 0x0000022005857EF0>
print(haier2)
注意:打印对象和self得到的结果是⼀致的,都是当前对象的内存中存储地址。
8.2 添加和获取对象属性
属性即是特征,⽐如:洗⾐机的宽度、⾼度、重量…
对象属性既可以在类外⾯添加和获取,也能在类⾥⾯添加和获取
- 类外面添加对象属性
对象名.属性名=值
- 类外面获取对象属性
对象名.属性名
- 类里面获取对象属性
self.属性名
注意:在类定义那里,用self.属性名定义的属性属于对象属性,每个不同对象的对象属性都是独立的,互不影响的,而直接用属性名定义的属于类属性,类属性是所有对象共有的,类属性只能通过类.属性修改。
8.3 魔法方法
在Python中, __xx__()
的函数叫做魔法⽅法,指的是具有特殊功能的函数。
__init__()
,初始化对象,在创建对象时自动被调用,并能够接收创建对象时传入的参数class Washer(): def __init__(self, width, height): self.width = width self.height = height def print_info(self): print(f'洗⾐机的宽度是{self.width}') print(f'洗⾐机的⾼度是{self.height}') haier1 = Washer(10, 20) haier1.print_info() haier2 = Washer(30, 40) haier2.print_info()
init(self) 中的self参数,不需要开发者传递,python解释器会⾃动把当前的对象引⽤(类似指针)传递过去。
__str__()
,当使⽤print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__
⽅法,那么就会打印从在这个⽅法中return
的数据。 ```python class Washer(): def init(self, width, height):self.width = width self.height = height
def str(self):
return '这是海尔洗⾐机的说明书'
haier1 = Washer(10, 20)
这是海尔洗⾐机的说明书
print(haier1)
- `__del__()`,当**删除对象**时,python解释器也会默认调用`__del__()`方法
```python
class Washer():
def __init__(self, width, height):
self.width = width
self.height = height
def __del__(self):
print(f'{self}对象已经被删除')
haier1 = Washer(10, 20)
del haier1
# <__main__.Washer object at 0x0000026118223278>对象已经被删除
- getitem(),该函数允许对象可以用
对象[key]
的方式来操作对象,此时会调用该魔法方法 ```python class Washer(): def init(self, width, height):
def getitem(self, key):self.width = width self.height = height
wash = Washer(10,20) print(wash[1]) # 1return key
- `__len__()`,对该对象用`len()`函数时,调用执行__len__()
```python
class Washer():
def __init__(self, *args):
self.name = args
def __len__(self):
return len(self.name)
wash = Washer(10,20,30,40)
print(len(wash)) # 4
__call__()
,该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。class CLanguage: # 定义__call__方法 def __call__(self,name,add): print("调用__call__()方法",name,add) clangs = CLanguage() clangs("C语言中文网","http://c.biancheng.net")
9. 面向对象-继承
9.1 单继承与多继承
单继承 ```python
1. 师⽗类
class Master(object): def init(self):
self.kongfu = '[古法煎饼果⼦配⽅]'
def make_cake(self):
print(f'运⽤{self.kongfu}制作煎饼果⼦')
2. 徒弟类
class Prentice(Master): pass
- 多继承
```python
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果⼦配⽅]'
def make_cake(self):
print(f'运⽤{self.kongfu}制作煎饼果⼦')
# 创建学校类
class School(object):
def __init__(self):
self.kongfu = '[⿊⻢煎饼果⼦配⽅]'
def make_cake(self):
print(f'运⽤{self.kongfu}制作煎饼果⼦')
class Prentice(School, Master):
pass
注意:当⼀个类有多个⽗类的时候,默认使⽤第⼀个⽗类的同名属性和⽅法。也就是说这里虽然继承了四个方法,但实际上只能调用School类里的两个方法。
9.2 子类重写与调用父类同名方法和属性
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果⼦配⽅]'
def make_cake(self):
print(f'运⽤{self.kongfu}制作煎饼果⼦')\
class School(object):
def __init__(self):
self.kongfu = '[⿊⻢煎饼果⼦配⽅]'
def make_cake(self):
print(f'运⽤{self.kongfu}制作煎饼果⼦')
# 独创配⽅
class Prentice(School, Master):
def __init__(self):
self.kongfu = '[独创煎饼果⼦配⽅]'
def make_cake(self):
# 如果是先调⽤了⽗类的属性和⽅法,⽗类属性会覆盖⼦类属性,故在调⽤属性前,先调⽤⾃⼰
⼦类的初始化
self.__init__()
print(f'运⽤{self.kongfu}制作煎饼果⼦')
# 调⽤⽗类⽅法,但是为保证调⽤到的也是⽗类的属性,必须在调⽤⽅法前调⽤⽗类的初始化
def make_master_cake(self):
Master.__init__(self)
Master.make_cake(self)
def make_school_cake(self):
School.__init__(self)
School.make_cake(self)
- 子类重写父类方法,只需要子类的方法名和参数一致即可。⼦类和⽗类具有同名属性和⽅法,默认使⽤⼦类的同名属性和⽅法。
- 如果子类想要使用父类方法,则需要用
类.方法名
的形式,且需要把对象传进去。
9.3 super()调用父类方法
class Master(object):
def __init__(self):
self.kongfu = '[古法煎饼果⼦配⽅]'
def make_cake(self):
print(f'运⽤{self.kongfu}制作煎饼果⼦')
class School(Master):
def __init__(self):
self.kongfu = '[⿊⻢煎饼果⼦配⽅]'
def make_cake(self):
print(f'运⽤{self.kongfu}制作煎饼果⼦')
# ⽅法2.1
# super(School, self).__init__()
# super(School, self).make_cake()
# ⽅法2.2
super().__init__()
super().make_cake()
注意:使⽤super() 可以⾃动查找⽗类。调⽤顺序遵循 mro 类属性的顺序。⽐较适合单继承 使⽤。
9.4 私有属性和方法
在Python中,可以为实例属性和⽅法设置私有权限,即设置某个实例属性或实例⽅法不继承给⼦类,或是不能直接通过对象调用。
设置私有权限的⽅法:在属性名和⽅法名前⾯加上两个下划线 __。
注意:私有属性和私有⽅法只能在类⾥⾯访问和修改。 这里的私有属性和私有方法不是真的强制给设置私有权限,而是python解释器自动给私有属性和方法换了一个名字,你不能通过原有的属性名或方法名直接调用。
10. 面向对象其他特性
10.1 多态
多态指的是⼀类事物有多种形态,(⼀个抽象类有多个⼦类,因⽽多态的概念依赖于继承)。
- 定义:多态是⼀种使⽤对象的⽅式,⼦类重写⽗类⽅法,调⽤不同⼦类对象的相同⽗类⽅法,可以 产⽣不同的执⾏结果
- 好处:调⽤灵活,有了多态,更容易编写出通⽤的代码,做出通⽤的编程,以适应需求的不断变化!
- 实现步骤:
- 定义⽗类,并提供公共⽅法
- 定义⼦类,并重写⽗类⽅法
- 传递⼦类对象给调⽤者,可以看到不同⼦类执⾏效果不同
class Dog(object): def work(self): # ⽗类提供统⼀的⽅法,哪怕是空⽅法 print('指哪打哪...') class ArmyDog(Dog): # 继承Dog类 def work(self): # ⼦类重写⽗类同名⽅法 print('追击敌⼈...') class DrugDog(Dog): def work(self): print('追查毒品...') class Person(object): def work_with_dog(self, dog): # 传⼊不同的对象,执⾏不同的代码,即不同的work函数 dog.work() ad = ArmyDog() dd = DrugDog() daqiu = Person() daqiu.work_with_dog(ad) daqiu.work_with_dog(dd)
注意:与JAVA不同的是,由于python定义变量时不需要声明数据类型,所以对于JAVA来说需要一个父类来作为声明变量时使用,而对于python来说,即使不需要父类,在
def work_with_dog(self, dog)
这里,也能传入不同的对象,只要对象都有work方法。
10.2 类属性和实例属性
- 类属性就是 类对象 所拥有的属性,它被该类的所有实例对象所共有。
- 类属性可以使⽤ 类对象 或 实例对象 访问。
注意:虽然能使用实例对象访问(不能修改)类属性,但如果有同名的对象属性,则访问的是对象属性 因此:类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了⼀个实例属性。
class Dog(object):
tooth = 10
wangcai = Dog()
xiaohei = Dog()
print(Dog.tooth) # 10
print(wangcai.tooth) # 10
print(xiaohei.tooth) # 10
类属性的优点
- 类的实例: 记录的某项数据 始终保持⼀致时,则定义类属性。
- 实例属性: 要求 每个对象 为其 单独开辟⼀份内存空间 来记录数据,⽽ 类属性 为全类所共有 ,仅占⽤⼀份内存,更加节省内存空间。
10.3 类方法和静态方法
类方法特点:
- 第⼀个形参是类对象的⽅法
- 需要⽤装饰器 @classmethod 来标识其为类⽅法,对于类⽅法,第⼀个参数必须是类对象,⼀般以 cls 作为第⼀个参数。类和实例对象都可以调用。
使用场景:
- 当⽅法中 需要使⽤类对象 (如访问私有类属性等)时,定义类⽅法
- 类⽅法⼀般和类属性配合使⽤(即对于一些公共属性的访问,只访问,不能修改,所以设成私有属性)
class Dog(object): __tooth = 10 @classmethod def get_tooth(cls): return cls.__tooth wangcai = Dog() result = wangcai.get_tooth() print(result) # 10
静态方法特点:
- 需要通过装饰器 @staticmethod 来进⾏修饰,静态⽅法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。
- 静态⽅法 也能够通过 实例对象 和 类对象 去访问
使用场景:
- 当⽅法中 既不需要使⽤实例对象(如实例对象,实例属性),也不需要使⽤类对象 (如类属性、类⽅法、创建实例等)时,定义静态⽅法
- 取消不需要的参数传递,有利于减少不必要的内存占⽤和性能消耗
- 静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
11. 异常
- 捕获指定异常
```python
try:
可能发⽣错误的代码
except 异常类型:
如果捕获到该异常类型执⾏的代码
—————————————————————
try: print(num) except NameError: print(‘有错误’)
捕获多个指定异常
try: print(1/0) except (NameError, ZeroDivisionError): print(‘有错误’)
捕获异常描述信息
try: print(num) except (NameError, ZeroDivisionError) as result: print(result)
- 捕获所有异常
**Exception是所有程序异常类的父类**
```python
try:
print(num)
except Exception as result:
print(result)
异常的else与finally
try: f = open('test.txt', 'r') except Exception as result: f = open('test.txt', 'w') else: print('没有异常,真开⼼') finally: f.close()
- else表示的是如果没有异常要执⾏的代码。
- finally表示的是⽆论是否异常都要执⾏的代码,例如关闭⽂件。
- 异常的传递
- 异常的传递 —— 当 函数/方法 执行 出现异常,会 将异常传递 给 函数/方法 的 调用一方
- 如果 传递到主程序,仍然 没有异常处理,程序才会被终止
提示:
- 在开发中,可以在主函数中增加 异常捕获
- 而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的 异常捕获 中
- 这样就不需要在代码中,增加大量的 异常捕获,能够保证代码的整洁
- 自定义异常
在Python中,抛出⾃定义异常的语法为 raise 异常类对象 。
# ⾃定义异常类,继承Exception
class ShortInputError(Exception):
def __init__(self, length, min_len):
self.length = length
self.min_len = min_len
# 设置抛出异常的描述信息
def __str__(self):
return f'你输⼊的⻓度是{self.length}, 不能少于{self.min_len}个字符'
def main():
try:
con = input('请输⼊密码:')
if len(con) < 3:
raise ShortInputError(len(con), 3)
except Exception as result:
print(result)
else:
print('密码已经输⼊完成')
可以将自定义异常看做是对一些逻辑错误统一写成的一个类。即也许多个地方都会出现同一种逻辑错误,为了避免每个地方都去用一个if语句else语句来判断,不如把它抽象成一个类,即异常类
12. 模块与包
Python 模块(Module),是⼀个 Python ⽂件,以 .py 结尾,包含了 Python 对象定义和Python语句。 模块能定义函数,类和变量,模块⾥也能包含可执⾏的代码。
12.1 导入模块
导入模块的方式
- import 模块名1,模块名 2。。。
- 该方法导入模块时,调用功能需要用
模块名.功能名()
- 该方法导入模块时,调用功能需要用
- from 模块名 import 功能名 1,功能名2。。。
- 可直接用
功能名()
调用
- 可直接用
- from 模块名 import *
- 可直接用
功能名()
调用
- 可直接用
- import 模块名 as 别名
- from 模块名 import 功能名 as 别名
注意:如果使⽤ from .. import .. 或 from .. import * 导⼊多个模块的时候,且模块内有同名功能。当调用这个同名功能的时候,调用到的是后面导入的模块的功能。
12.2 制作模块
在Python中,每个Python⽂件都可以作为⼀个模块,模块的名字就是⽂件的名字。也就是说⾃定义模块名必须要符合标识符命名规则。
- 定义模块
新建⼀个Python⽂件,命名为 my_module1.py ,并定义 testA 函数。
def testA(a, b):
print(a + b)
- 测试模块
在实际开中,当⼀个开发⼈员编写完⼀个模块后,为了让模块能够在项⽬中达到想要的效果,这个开发 ⼈员会⾃⾏在py⽂件中添加⼀些测试信息.,例如,在my_module1.py
⽂件中添加测试代码。
def testA(a, b):
print(a + b)
# 只在当前⽂件中调⽤该函数,其他导⼊的⽂件内不符合该条件,则不执⾏testA函数调⽤
if __name__ == '__main__':
testA(1, 1)
注意:⽆论是当前⽂件,还是其他已经导⼊了该模块的⽂件,如果不加‘main’的限制代码,在运⾏的时候都会⾃动执⾏ testA 函数的调⽤。因此加一个限制,只有当该文件为主函数时才能运行。
12.3 模块定位顺序
当导⼊⼀个模块,Python解析器对模块位置的搜索顺序是:
1. 当前⽬录
2. 如果不在当前⽬录,Python则搜索在shell变量PYTHONPATH下的每个⽬录。
3. 如果都找不到,Python会察看默认路径。UNIX下,默认路径⼀般为/usr/local/lib/python/
模块搜索路径存储在system模块的sys.path变量中。变量⾥包含当前⽬录,PYTHONPATH和由安装过程决定的默认⽬录。 注意:
- ⾃⼰的⽂件名不要和已有模块名重复,否则导致模块功能⽆法使⽤
- 使⽤from 模块名 import 功能 的时候,如果功能名字重复,调⽤到的是最后定义或导⼊的功能。
12.4 all
如果⼀个模块⽂件中有__all__
变量,当使⽤from xxx import *
导⼊时,只能导入这个列表中的元素。
__all__ = ['testA']
def testA():
print('testA')
def testB():
print('testB')
使用from module1 import *
时,只有testA会被导入。
12.5 包
包将有联系的模块组织在⼀起,即放到同⼀个⽂件夹下,并且在这个⽂件夹创建⼀个名字为__init__.py
⽂件,那么这个⽂件夹就称之为包。
- 制作包
[New] — [Python Package] — 输⼊包名 — [OK] — 新建功能模块(有联系的模块)。
注意:新建包后,包内部会⾃动创建 init.py ⽂件,这个⽂件控制着包的导⼊⾏为。
导入包
方法1
import 包名.模块名 包名.模块名.⽬标
方法2
from 包名 import * 模块名.⽬标
注意添加all变量。