文章目录
一、python基础
# 利用python定义比变量的语法来实现 变量的数据交换
a,b = b,a
s = 'iloveyou'
res = type(s) # type() 函数,获取当前的数据类型
print(res)
# <class 'str'> str == string
常见数据类型:
'''
字符串 string
数字类型 Number
整型 int
浮点 float
复数
布尔 bool
列表 list
元组 tuple
字典 dict
集合 set
可变数据类型:列表,字典,集合
不可不数据类型: 字符串,数字,元组
容器类型数据 : 字符串,列表,元组,集合,字典
非容器类型数据: 数字,布尔类型
'''
1.字符串类型
- 单双引号都可以定义字符串
- 三引号也可以定义字符串
- 单双引号定义的字符串不能随意换行,需要在换行时指明换行符
- 使转义字符串失效 -> 加
s = r'\nihao \shijie'
2.数字类型
- int 整型
- float 浮点类型
- complex 复数
- bool 布尔类型(True,False)
特别点的:
var = 0x10 # 十六进制
var = b'001100111' # bytes
var = 5+6j # complex
var = True
# print(varn,type(varn))
3.List列表类型 用[]定义 可变
- 列表中存储的数据可以是任意类型的
- 在需要记录多个数据时,可以使用中括号进行定义 []
- 并且每个数据之间使用逗号分隔
- 列表中存储的数据,可以通过下标的方式进行获取
- 可以存储一个列表,称为 二级列表(二维列表) 或者 多级列表 (多维列表)
'''
关于列表中的下标
0 1 2 3 4
l = ['a','b',[5,1,3],'pai',3.1415926]
-5 -4 -3 -2 -1
'''
print(l[3][0])
4. tuple 元组 用()定义 不可变
- 元组的最大特点就是值不能被改变
- 单个元素时需要加逗号, 不然就不是元组类型了
vart = (1,2,3,'a','b')
vart = (1,)
# 元组的其它定义方式
vart = 1,2,3
print(type(vart)) #<class 'tuple'>
5.Dict字典 用{}定义
- 字典是 键值对 的存储方式 name :admin
- 键和值之间使用冒号进行分隔,多组键值对之间使用逗号分隔
- 键必须是字符串或数字类型,值可以是任意类型
- 键名不能重复 否则会覆盖,值可以重复
6.set集合类型
- set集合使用 中括号或者set()方法来定义
- 如果需要定义一个空集合时 只能使用 set()方法,因为大括号时定义的空字典
- 集合主要用于运算,交集,差集,并集,对称集合
a = {1,2,3,'a'}
# 给集合添加元素
# a.add('b')
# 无法获取集合中的单个元素,但是可以添加和删除
# a.discard('a')
# print(a)
# 检查当前的元素是否在集合中
# print(1 in a)
# 集合主要用于运算,交集,差集,并集,对称集合
a = {1,2,3,'a','b'}
b = {1,'a',22,33}
print(a & b) # 交集 {1, 'a'}
print(a - b) # 差集 {'b', 2, 3} a 集合有,b集合没有的
print(a | b) # 并集 {1, 2, 3, 33, 'a', 'b', 22} 两个集合,放到一起,并且去重
print(a ^ b) # 对称差集 {33, 2, 3, 'b', 22}
7.数据类型转换
- 自动类型转换:
当两个不同的值进行运算时,结果会向更高的精度进行计算:True ==> 整型 ==> 浮点 ==> 复数
- 强制类型转换:
str() 、int() 、float() 、bool() 转换为对应的类型
- 容器类型转换:
- list() 列表
- 数字类型是 非容器类型,不能转换为列表
- 字符串 转换为列表时 会把字符串中的每一个字符当做列表的元素
- 集合 可以转换为 list列表类型
- 元组 可以转换为 list列表类型
- 字典 可以转换为 list列表类型**,只保留了字典中的键**
- tuple() 元组
- 数字类型 非容器类型,不能转换为元组
- 其它容器类型的数据进行转换时,和列表一样
- set() 集合
- 数字类型 非容器类型,不能转换为 集合
- 字符串,列表,元组 可以转为 集合 结果是无序的
- 字典转换为集合时,只保留了字典的键 key
- dict() 字典
- 数字类型 非容器类型,不能转换为 字典
- 字符串、集合不能直接转换为 字典
- 列表可以转换为字典,要求是一个二级列表,并且每个二级元素只能有两个值
- 元组可以转换为字典,要求是一个二级元组,并且每个二级元素只能有两个值
运算符
字符串运算
'''
1. 字符串与数字不能直接参与运算
2. 字符串和字符串使用 + 结果是字符串的拼接
3. 字符串如果和数字 使用 * 那么就是重复前面的字符串
'''
l = 'love'
i = 'i '+l+' you'
i = f'i {l} you' #替换拼接
i = 'i {} you'.format(l)
i = 'i {l} you'.format(l=l)#替换拼接
Python算术运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 - 两个对象相加 | a + b 输出结果 31 |
- | 减 - 得到负数或是一个数减去另一个数 | a - b 输出结果 -11 |
* | 乘 - 两个数相乘或是返回一个被重复若干次的字符串 | a * b 输出结果 210 |
/ | 除 - x 除以 y | b / a 输出结果 2.1 |
% | 取模 - 返回除法的余数 | b % a 输出结果 1 |
** | 幂 - 返回x的y次幂 | a**b 为10的21次方 |
// | 取整除 - 向下取接近除数的整数 | >>> 9//2 4 >>> -9//2 -5 |
Python逻辑运算符
Python语言支持逻辑运算符,以下假设变量 a 为 10, b为 20:
运算符 | 逻辑表达式 | 描述 | 实例 |
---|---|---|---|
and | x and y | 布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 | (a and b) 返回 20。 |
or | x or y | 布尔"或" - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。 | (a or b) 返回 10。 |
not | not x | 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 | not(a and b) 返回 False |
Python成员运算符
除了以上的一些运算符之外,Python还支持成员运算符,测试实例中包含了一系列的成员,包括字符串,列表或元组。
运算符 | 描述 | 实例 |
---|---|---|
in | 如果在指定的序列中找到值返回 True,否则返回 False。 | x 在 y 序列中 , 如果 x 在 y 序列中返回 True。 |
not in | 如果在指定的序列中没有找到值返回 True,否则返回 False。 | x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。 |
Python身份运算符
身份运算符用于比较两个对象的存储单元
运算符 | 描述 | 实例 |
---|---|---|
is | is 是判断两个标识符是不是引用自一个对象 | x is y, 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 False |
is not | is not 是判断两个标识符是不是引用自不同对象 | x is not y , 类似 id(a) != id(b)。如果引用的不是同一个对象则返回结果 True,否则返回 False。 |
注: id() 函数用于获取对象内存地址。
流程控制
特殊语句:
1.exit()
2.quit()
用于结束程序的执行,exit()和quit()之后的代码不会执行。在单纯的循环结构中的作用与break很像,但绝不能混为一谈!!!
模块化编程
模块化编程指把程序进行封装(函数封装,面向对象,文件。。。)
1.参数
参数有顺序:形参声明的位置顺序:
普通参数 -> 默认参数 -> 收集参数 -> 命名关键字参数 -> 关键字收集参数
(4)收集参数:
专门收集在函数调用时传递的多余的实参
1.普通收集参数:专门用于收集多余的普通参数,形成一个新的 元组
语法:参数前面加* 例: *args2.关键字收集参数:用于专门收集多余关键字实参,形成一个新的 字典
语法:参数前面加** 例: **kwargs
(5)命名关键字参数
定义时放在*号后面的参数,调用时强制必须传入指定参数名才能调用
# 命名关键字参数
'''
1. 关键字参数定义在 收集参数 后面,
2. 关键字参数必须通过行参的名字来进行传递
'''
def love(a,b,c=3,*args,name):
print(a,b,c)
print(args)
print(name)
# 在调用时,必须按照行参的名字进行参数的传递
love(1,2,4,5,6,7,8,9,name="amdin") #关键字参数必须传递
# 普通函数
def love(age,name):
print(name)
print(age)
# love('abc')
# 在调用普通函数时,需要按照顺序进行参数的传递
# 也可以把普通函数的普通参数按照关键字参数进行传递,
love(name="aabbcc",age=123)
2.返回值
- 函数中可以使用 return 进行数据的返回
- 可以使用return 返回 任意内容或数据
- return 会把返回值,返回到函数调用出
- return 意味着函数的结束,return之后的代码不在执行
- 如果在函数中没有使用return 或者 return后面没有任何内容,那么默认返回 None
3.变量类型
局部变量
函数内定义的变量,局部变量,在函数外不能使用
全局变量
在函数内部使用 global 直接定义的变量,就是全局变量,函数内外都可以使用
在函数外定义的变量,在函数内使用 global 关键字进行声明,那么也是全局变量
globals() 获取全局数据
locals() 获取当前作用域的数据
在函数外定义的变量,函数可以访问,但是不能更改
数据类型的分类:
可变数据类型:在函数外定义的变量,在函数可以使用,
列表和字典
不可变数据类型:在函数外定义的变量,在函数内只能访问,不能使用其它操作
nonlocal:嵌套的局部函数使用上层函数中定义的变量
函数的文档
def outer():
'''
这里是让你些当前函数的文档说明的。
需要说明当前函数的作用,
如果当前函数还有行参,那么也需要对行参进行一一说明
name: 这个是一个name参数,有什么作用。。。
age : 这个表示当前的一个年龄
:return: 此处说明当前函数的返回值。。。
'''
print(globals())
print(__name__) # 获取当前脚本的文件名,
print(__doc__) # 获取当前脚本的说明文档
# print(outer.__doc__) # 获取当前函数的说明文档
'''
魔术变量
__name__ ==> 当前脚本如果作为主程序,那么值是 __main__,如果是当做一个模块,在另外一个脚本中引用去使用,那么值就是当前文件的名字
__doc__ ==> 当前脚本的文档说明 在当前脚本当中的第一个 三引号注释就是当前脚本的说明文档
{
'__name__': '__main__',
'__doc__': '\n在内函数中如果想使用外层函数的变',
'__package__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x110444350>,
'__spec__': None,
'__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__file__': '/Users/yc/Desktop/code/8.nonlocal关键字.py',
'__cached__': None, 'outer': <function outer at 0x1104938c0>
}
'''
4.函数
回调函数
在一个函数中要求传递的参数是一个函数作为参数,并且在函数中使用了传递进来的函数,那么这个函数我们就可以称为是一个回调函数
# 定义一个函数,函数中的一个参数要求是另一个函数
# 带有回调函数参数的函数
def func(f):
# 并且在函数中调用了传递进来的行参函数
f()
# 回调函数
def love():
print('123')
func(love)
闭包函数
在一个函数内返回了一个内函数, 并且这个返回的内函数还使用了外函数中局部变量,这就是闭包函数
特点:
- 在外函数中定义了局部变量,并且在内部函数中使用了这个局部变量
- 在外函数中返回了内函数,返回的内函数就是闭包函数
- ⚠主要在于保护了外函数中的局部变量,既可以被使用,又不会被破坏
- 检测一个函数是否为闭包函数,可以使用
函数名.__closure__
如果是闭包函数返回 cell
# 定义一个函数
def person():
money = 0 # 函数中定义了一个局部变量
# 工作 定义的内函数
def work():
nonlocal money # 在内函数中使用了外函数的临时变量
money += 100
print(money)
# 在外函数中返回了内函数,这个内函数就是闭包函数
return work
res = person() # return work res = work
res() # res() == work()
res()
res()
res()
# 此时 就不能够在全局中对money这个局部变量进行任何操作了,
# 闭包的作用:保护了函数中的变量不受外部的影响,但是又能够不影响使用
匿名函数
'''
语法:
lambda [参数列表]:返回值
'''
# 封装一个函数做加法运算
# 普通函数
def jia(x,y):
return x+y
# print(jia(2,3))
# 改成lambda表达式来封装
res = lambda x,y:x+y
# print(res(4,4))
# 带有分支结构的lambda 表达式
# lambda 参数列表: 真区间 if 表达式判断 else 假区间
res = lambda sex:"很man" if sex=='男' else "很nice"
print(res('女'))
迭代器
迭代器是python中最具特色的功能之一,是访问集合元素的一种方式
迭代器是一个可以记住访问遍历的位置的对象
从集合的第一个元素开始访问,直到集合中的所有元素被访问完毕
迭代器只能从前往后一个一个的便利,不能后退
能被next()函数调用,并不断返回下一个值的对象称为迭代器(Iterator 迭代器对象)
iter()
功能:把可迭代的对象,转为一个迭代器对象
参数:可迭代的对象 (str,list,tuple,dict
返回值: 迭代器对象
注意:迭代器一定是一个可以迭代的对象,但是可迭代对象不一定是迭代器
next()
next()函数可以去调用迭代器,并返回迭代器中的下一个数据
迭代器的取值
迭代器取值的特点,取出一个少一个,直到都取完,最后再获取就会报错
- next() 调用一次获取一次,直到数据被取完
- list() 使用list函数直接取出迭代器中的所有数据
- for 使用for循环遍历迭代器的数据
f4 = ['赵四','刘能','小沈阳','海参炒面']
res = iter(f4)
print(next(res))
# 检测迭代器和可迭代对象的方法
from collections.abc import Iterator,Iterable
# type() 函数返回当前数据的类型,
# isinstance() 检测一个数据是不是一个指定的类型
r3 = isinstance(res,Iterable) # True 可迭代对象
r4 = isinstance(res,Iterator) # True 是一个迭代器
# 迭代器一定是一个可迭代的对象,可迭代对象不一定是迭代器
5.内置函数
由python解释器给提供好的函数
range()
函数
zip() 函数:按照最短的元素进行组合
高阶函数:
filter(func,iterable)
# 要求 保留所有的偶数,丢弃所有的奇数
varlist = [1,2,3,4,5,6,7,8,9]
# 使用 filter 进行处理
# 定义一个函数,判断当前这个函数是否为偶数,偶数返回True,奇数返回False
# def myfunc(n):
# if n % 2 == 0:
# return True
# else:
# return False
#
# # 调用 过滤器 函数进行处理
# it = filter(myfunc,varlist)
# print(it,list(it))
# 优化版
it = filter(lambda n:True if n % 2 == 0 else False,varlist)
print(it,list(it))
map(func, *iterables)
map(func, *iterables)
功能: 对传入的可迭代数据中的每个元素放入到函数中进行处理,返回一个新的迭代器
参数:
func 函数 自定义函数|内置函数
iterables:可迭代的数据
返回值:迭代器
# (1)把一个字符串数字的列表转为 整型的数字列表
['1','2','3','4'] # ==> [1,2,3,4]
# 普通的处理方法
varlist = ['1','2','3','4'] # ==> [1,2,3,4]
newlist = []
for i in varlist:
newlist.append(int(i))
print(newlist)
# 使用map函数进行处理
varlist = ['1','2','3','4']
res = map(int,varlist) # <map object at 0x104ea8890>
print(list(res))
#也可以用lambda表达式进行优化
sorted(iterable,[reverse,key])
sorted()
运行原理:
把可迭代数据里面的元素,一个一个的取出来,放到key这个函数中进行处理,
并按照函数中return的结果进行排序,返回一个新的列表
功能: 排序
参数:
iterable 可迭代的数据 (容器类型数据,range数据序列,迭代器)
reverse 可选,是否反转,默认为False,不反转, True反转
key 可选, 函数,可以是自定义函数,也可以是内置函数
返回值: 排序后的结果
arr = [3,7,1,-9,20,10]
res = sorted(arr,reverse=True) # [20, 10, 7, 3, 1, -9]
# 使用abs这个函数(求绝对值)作为sorted的key关键字参数使用
res = sorted(arr,key=abs)
#使用lambda函数
res = sorted(arr,key=lambda x:x%2)
reduce(func,iterable)
数据类型方法
1.字符串操作
- 字符串 + 操作
- 字符串 * 操作
- 字符串 [] 切片操作
- 字符串[开始值:结束值:步进值]
开始值:默认为0,结束值默认是最后一个下标,步进值默认为1
- 字符串[开始值:结束值:步进值]
字符串格式化:
- format
- f
字符串查找
res = vars.find('you')
res = vars.index('youe') # 找到则返回索引位置,未找到则报错 ValueError
# ** join() 方法 ,使用指定的字符串,把一个容器中的元素链接成一个字符串
varlist = ['user', 'admin', 'id', '123']
res = '_'.join(varlist)
2.列表操作 []
- 列表定义-[],list()
- 列表相加-拼接
- 列表相乘-重复
- 列表的下标- 获取,更新
- 列表元素的添加-append()
- 列表元素的删除
- del 列表[下标]
- pop()函数 删除元素
深拷贝与浅拷贝
浅拷贝
浅拷贝只能拷贝列表中的一维元素,如果列表中存在二维元素或容器,则引用而不是拷贝
使用cpoy函数或者copy模块中的copy函数拷贝的都是浅拷贝
深拷贝
深拷贝就是不光拷贝了当前的列表,同时把列表中的多维元素或容器也拷贝了一份,而不是引用
使用copy模块中的 deepcopy 函数可以完成深拷贝
varlist = [1,2,3]
# 简单的拷贝 就可以把列表复制一份
newlist = varlist.copy()
# 使用 copy模块中 深拷贝方法 deepcopy
newlist = copy.deepcopy(varlist)
列表推导式
结果变量 = [变量或变量的处理结果 for 变量 in 容器类型数据]
结果变量 = [变量或变量的处理结果 for i in 容器类型数据 条件表达式]
对于嵌套循环的列表推到式
#基本推导式
varlist = [i**2 for i in range(10)]
#带条件的推导
newlist = [i for i in range(10) if i % 2 == 0]
3.元组
- 定义空元组 变量 = (),或者 变量=tuple()
- 还可以使用 变量 = (1,2,3) 定义含有数据的元组
- 注意:如果元组中只有一个元素时,必须加逗号 变量=(1,)
- 特例:变量=1,2,3 这种方式也可以定义为一个元组
4.生成器和yield关键字
# 。yield 关键字
'''
yield关键字使用在 生成器函数中
+ yield 和函数中的 return 有点像
+ 共同点:执行到这个关键字后会把结果返回
+ 不同点:
+ return 会把结果返回,并结束当前函数的调用
+ yield 会返回结果,并记住当前代码执行的位置,下一次调用时会从上一次离开的位置继续向下执行
'''
# 定义一个普通函数
def hello():
print('hello 1')
return 1 # return在函数中会把结果返回,并且结束当前的函数,后面的代码不再执行
print('world 2')
return 2
#
# hello()
# hello()
# 使用 yield定义一个 生成器函数
def hello():
print('hello 1')
yield 1
print('world 2')
yield 2
print('haha 3')
yield 3
# 调用生成器函数,返回一个迭代器
res = hello()
# 使用生成器返回的迭代器
# r = next(res)
# print(r)
# r = next(res)
# print(r)
# 使用生成器返回的迭代器
# 适应list类似的函数 去调用生成器返回的迭代器时,会把迭代器的返回结果,作为容器的元素
# r = list(res)
# print(r)
# 使用生成器返回的迭代器
# for i in res:
# print(i)
'''
上面的生成器函数调用时的过程
首先 调用来生成器函数,返回来一个迭代器
1。第一次去调用迭代器:
走到当前的生成器函数中,遇到了 yield 1,把1返回,并且记住来当前的执行状态(位置),暂停了执行,等待下一次的调用
2。第二层去调用迭代器
从上一次遇到的yield位置开始执行,遇到了 yield 2 ,把2返回,并记住状态,暂停执行,等待下一次调用
3。第三次去调用迭代器
从上一次遇到的yield位置开始执行,遇到 yield 3 , 把3返回,并记住了状态,暂停执行,等待下一次调用
如果在最后又调用了迭代器,那么会从上一次的 yield位置开始,结果后面没有了,直接就超出范围,报错
'''
5.字典
字典不能+
# 获取当前字典中的所有 key 键
res = var1.keys()
# 获取字典中所有的 value 值
res = var1.values()
# 获取当前字典中所有 键值对
res = var1.items()
遍历:
(1)在遍历当前的字典时,只能获取当前的key
6.集合
- 可以直接使用 {} 来定义集合
- 可以使用set()进行集合的定义和转换
- 使用集合推导式完成集合的定义
注意:集合中的元素不能重复,集合中存放的数据:Number,Strings,Tuple,冰冻集合
不存在深浅拷贝问题
File文件操作
针对磁盘中的文件的读写。文件I/O I 输入(input) O输出(Output)
文件操作步骤:1.打开文件 2.读写文件 3.关闭文件
写入文件的操作:(把大象装进冰箱)
- 打开文件 open() ==> 打开冰箱
- 写入内容 write() ==> 把大象装进冰箱
- 关闭文件 close() ==> 关闭冰箱门
读取文件的操作:(把大象从冰箱中取出)
- 打开文件 open() ==> 打开冰箱门
- 读取内容 read() ==> 把大象拿出来
- 关闭文件 close() ==> 关闭冰箱门
文件操作相关函数
open() 打开文件
格式: open(文件的路径,打开的方式,[字符集])
Seek() 设置文件指针的位置
fp.seek(0) # 把文件指针设置到文件的开头位置 fp.seek(10) # 设置文件指针的位置 fp.seek(0,2) # 0,2是把文件指定设置在文件的末尾
write() 写入内容
格式:文件对象.write(字符串)
writelines() 写入容器类型数据
格式:文件对象.write(容器类型数据)
注意:容器类型数据中的元素也必须是可写入的字符串类型
read() 读取内容
格式:文件对象.read() 从当前指针位置读取到最后
格式:文件对象.read(读取的字节数) 可以读取指定长度的字符
readline() 读取一行
格式: 文件对象.readline() 一次读取一行
格式: 文件对象.readline(字节数) 一次读取一行中指定长度的字节
readlines() 读取所有行
格式: 文件对象.readlines() 读取所有行,每一行作为一个元素,返回了一个列表
格式:文件对象.readlines(6) 按照行进行读取,可以设置读取的字节数,设置的字节数不足一行按一行算
close() 关闭文件
格式:文件对象.close() 关闭打开的文件
文件操作的高级便捷写法
# 文件操作的 高级写法
'''
with open(文件路径,打开模式) as 变量:
变量.操作()
'''
with open('./1.txt','r+',encoding='utf-8') as fp:
res = fp.read()
print(res)
文件的路径:
文件路径 路径 url 统一资源定位符
#相对路径: 就像给别人指路一样: 在某某大厦的对面。。。
针对文件的相对路径的表示,从当前目录开始计算
1.txt ==> 具体文件前没有任何表示时,默认为当前目录 和 ./1.txt 是一个位置
./1.txt ==> ./ 代表当前目录中的 1.txt
../1.txt ==> ../ 代表当前目录中的 上一级目录中的1.txt
#绝对路径: 就像买东西写收货地址一样: 北京市海淀区中关村大街128号xxx小区,xxx号楼xx单元xx室
windows: c:/users/appdata/1.txt
Linux: /user/home/yc/1.txt
打开的模式:
#一,基础模式: w r x a
w w模式 write 写入模式
1。 文件如果不存在,则创建这个文件
2。 文件如果存在,则打开这个文件,并且清空文件内容
3。 文件打开后,文件的指针在文件的最前面
r r模式: read 读取模式
1。 如果文件不存在,则报错
2。 文件如果存在,则打开文件
3。 文件指针在文件的最前面
x x模式: xor 异或模式
1。文件不存在,则创建这个文件
2。文件已存在,则报错(防止覆盖)
3。文件的指针在文件的最前面
a a模式: append 追加模式
1。文件不存在,则创建文件
2。文件如果存在,则打开文件 (和w模式的区别在于。a模式打开文件后不会清空)
3。文件指针在当前文件的最后
#二,扩展模式:
b b模式 bytes 二进制
+ +模式 plus 增强模式(可读可写)
#三,文件操作模式的组合:
w,r,a,x
wb,rb,ab,xb,
w+,r+,a+,x+
wb+,rb+,ab+,xb+
模块和包
导入方式的分类
绝对导入
# 绝对导入的方式会使用[搜索路径]去查找和导入指定的包或模块
import 模块
import 包
import 包.模块
from 模块 import 内容
from 包 import 模块
from 包.模块 import 内容
相对导入
注意:相对导入只能在非主程序的模块中使用,不需要直接运行的模块文件
# 相对导入
from .包名/模块名 import 模块/内容
from ..包名/模块名 import 模块/内容
. 代表当前
..代表上一级
搜索路径
在导入模块或包时,程序查找的路径
'''
主要的搜索路径
1. 当前导入模块的程序所在的文件
2. python的扩展目录中 C:/Users/username/AppData/local/.../Python37/lib
3. python解释器指定的其它 第三方模块位置 /lib/sitepackages
'''
# 在当前脚本中查看 包或模块 的 搜索路径
import sys
print(sys.path)
'''
[
'',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages'
]
'''
# 可以自己定义一个路径,加入到搜索路径中
sys.path.append('/Users/yc/Desktop')
单入口程序
单入口程序是指整个程序都是经过一个主程序文件在运行,其它程序都封装成了包或模块
# 单入口文件是作为程序直接被运行的唯一文件,其它都是作为模块或包,被导入单入口中去执行
'''
ATM/
|---- main.py # 当前程序的主入口文件,单入口文件,唯一直接运行的文件
|---- package/ # 主要程序模块包
|---- |----- __init__.py # 包的初始化文件
|---- |----- View.py # 视图函数模块
|---- |----- Controller.py# 控制器模块
|---- |----- Card.py # 银行卡模块
|---- |----- User.py # 用户模块
|---- databases/ # 数据存储文件夹
|---- |---- user.txt
|---- |---- user_id_card.txt
main是程序的主入口文件,会被直接作为主程序运行,所以main.py文件中必须使用 绝对导入 方式
'''
第三方库和虚拟环境
虚拟环境就是在当前的系统环境中,去配置另外一个python的运行环境,是可以创建多个不同的虚拟环境。
python的虚拟环境相互独立,互不影响。
- 虚拟环境中可以在没有权限的情况下安装新的库(Linux系统中可能会出现的问题)
- 不同的应用可以使用不同的库或不同的版本。
- 虚拟环境中的库升级也不影响其它环境
- 虚拟环境可以作为一个项目的专有环境。在需要部署时,一键导出项目的所需要的包
如何去使用python的虚拟环境
1.在pycharm中可以直接创建虚拟环境
2.自己安装独立的虚拟环境
python -m venv 虚拟环境名
-
进入虚拟环境,激活虚拟环境
- linux
# 使用 source 命令 去执行 v1/bin/ 目录下的 activate localhost:code yc$ source v1/bin/activate (v1) localhost:code yc$
- windows
# windows系统需要 进入 v1/Scripts/ 这个目录 cd v1/Scripts/ # 运行 activate.bat 文件 activate.bat (v1) F:\code>
-
接下来就可以在虚拟环境中安装一些包
pip install pymysql
pip show pymysql
如果安装过则能显示信息。
# 查看所有安装的包
pip list
'''
Package Version
------------ -------
Click 7.0
Flask 1.1.1
itsdangerous 1.1.0
Jinja2 2.10.3
MarkupSafe 1.1.1
pip 19.0.3
PyMySQL 0.9.3
setuptools 40.8.0
Werkzeug 0.16.0
'''
# 导出所有包到文件
pip freeze > ./requirements.txt
退出虚拟环境后,直接删除虚拟环境文件夹即可
二、异常值处理
如何处理异常?
- 如果错误发生的情况是可以预知的,那么就可以使用流程控制进行预防处理
#比如: 两个数字的运算,其中一个不是数字,运算就会出错。这时可以去判断来预防
n2 = 3
if isinstance(n2,int):
res = 10+n2
print(res)
- 如果错误的发生条件不可预知,就可以使用 try。。。except。。 在错误发生时进行处理
'''语法:
try:
可能发生异常错误的代码
except:
如果发生异常则进入 except 代码块进行处理
'''
# 假设读取的文件不存在,会发生错误,可以使用两种方式进行处理,
# 1。可以在文件读取前先判断当前的文件是否存在
# 2。也可以使用try 。。。 except。。在错误发生时进行处理
try:
with open('./user.txt','r') as fp:
res = fp.read()
print(res)
except:
print('文件不存在')
print('程序的继续执行。。。')
注意:try。。except。。是在错误发生后进行的处理。和if有着根本性的区别。
自定义异常处理类
当异常出现时,对异常信息进行写入日志。
traceback 回溯模块 https://docs.python.org/3.7/library/traceback.html
logging 日志模块 https://docs.python.org/3.7/library/logging.html
# 自定义异常日志处理类
class Myexception():
def __init__(self):
import traceback
import logging
# logging的基本配置
logging.basicConfig(
filename='./error.log',# 日志存储的文件及目录
format='%(asctime)s %(levelname)s \n %(message)s',# 格式化存储的日志格式
datefmt='%Y-%m-%d %H:%M:%S'
)
# 写入日志
logging.error(traceback.format_exc())
# 使用自定义异常处理类
try:
int('bb')
except:
print('在此处进行异常的处理')
Myexception() # 在异常处理的代码块中去调用自定义异常类
三、面向对象
对象
区别于其他:对象中的方法可以修改、添加、重新定义
总结:
一个类定义类成员属性和成员方法,那么通过这个类实例化的对象,也具备这些方法和属性吗?
实际上,创建对象的时候,并不会把类中的属性和方法复制一份给对象,而是在对象中引用父类的方法
因此在访问对象的属性时,会先去找对象自己的属性,如果没有就去找这个类的属性和方法一个对象由类创建以后,是一个独立的对象,会引用父类中的属性和方法
如果在对象创建后,给对象的属性或方法,进行修改或添加,那么此时等于给这个对象创建了一个自己的属性和方法
所以在删除时,只能删除对象被修改或添加的成员
成员属性和成员方法的操作
对象操作成员
成员属性:
访问: 对象.成员属性名
修改: 对象.成员属性名法 = 新值。(此时等于给这个对象创建了一个自己的属性)
添加: 对象.新成员属性 = 值 (此时是给这个对象自己新建了一个属性)
删除: del 对象.成员属性 (注意:只能删除这个对象自己的属性)
成员方法:
访问: 对象.成员方法名()
修改: 对象.成员方法名 = func(此时等于给这个对象创建了一个自己的方法)
添加: 对象.方法名 = func (此时是给这个对象自己新建了一个方法)
删除: del 对象.方法名 (注意:只能删除这个对象自己的方法)
类操作成员(不推荐)
成员属性:
访问: 类名.成员属性名
修改: 类名.成员属性名法 = 新值。(此时通过这个类创建的对象都具有这个属性)
添加: 类名.新成员属性 = 值 (此时通过这个类创建的对象都具有这个属性)
删除: del 类名.成员属性 (注意:删除这个类的属性后,这个类创建的对象也没有这几个属性了)
成员方法:
访问: 类名.成员方法名()
修改: 类名.成员方法名 = func(此时通过类创建的对象都被修改)
添加: 类名.方法名 = func (此时通过类创建的对象都被修改)
删除: del 类名.方法名 (注意:此时通过类创建的对象都被修改)
总结
- 一个类可以实例化出多个对象,每个对象在内存中都独立存在的
- 当通过类实例化对象时,并不会把类中的成员复制一份给对象,而去给对象了一个引用
- 访问对象成员的时候,如果对象自己没有这个成员,对象会向实例化它的类去查找
- 对象成员的添加和修改,都只会影响当前对象自己,不会影响类和其它对象
- 删除对象的成员时,必须是该对象自己具备的成员才可以,不能删除类中引用的成员
- 对类的成员操作,会影响通过这个类创建的对象,包括之前创建的。
成员方法中的self
- self在方法中只是一个形参,并不是关键字
- self 单词本身的意思 自己
- self 在类的方法中 代表 当前这个对象
- self 代表调用这个方法的对象,谁调用了这个方法,self就代表谁
- self 就可以在类的内部代替对象进行各种操作
方法的分类
- 含有self或者可以接受对象作为参数的方法: 非绑定类方法
- 不含self或者不能接受对象作为参数的方法:绑定类方法
非绑定类方法,可以使用对象去访问
绑定类方法,只能通过类去访问
魔术方法
魔术方法也和普通方法一样都是类中定义的成员方法
魔术方法不需要去手动调用的,魔术方法会在某种情况下,自动触发(自动执行)
魔术方法还有一个比较特殊的地方:就是多数的魔术方法 前后都有两个连续的下划线
魔术方法不是我们自己定义的,而是系统定义好的,我们来使用
__init__
初始化方法
__init__ 初始化方法
触发机制:在通过类实例化对象后,自动触发的一个方法
作用: 可以在对象实例化后完成对象的初始化(属性的赋值,方法的调用。。)
应用场景: 文件的打开,数据的获取。。。干活前的一些准备功能。。。
__del__
析构方法
__del__ 析构方法
触发机制:析构方法会在对象被销毁时自动触发
作用:关闭一些开发的资源
注意:是对象被销毁时触发了析构方法,而不是析构方法销毁了对象
对象会在哪些情况下被销毁?
1。 当程序执行完毕,内存中所有的资源都会被销毁释放
2。 使用 del 删除时
3。 对象没有被引用时,会自动销毁
示例:
定义一个类,完成一个日志的记录
调用这个对象的时候,传递一个日志信息
这个对象会创建一个文件,开始写入,并在最后关闭这个文件
import time
class writeLog():
# 成员属性
# 文件的路径
fileurl = './'
# 日志文件的名称
filename = '2019-09-19'
# 初识化 打开文件
def __init__(self):
#完成文件的打开
print('初始化方法触发类。完成文件的打开')
self.fileobj = open(self.fileurl+self.filename,'a+',encoding='utf-8')
# 写日志的方法
def log(self,s):
print(f'把日志:{s} 写入文件中')
# 析构方法
def __del__(self):
print('析构方法触发了,关闭打开的文件')
# 在对象被销毁时,关闭在初始化方法中打开的文件对象
self.fileobj.close()
封装、继承、多态
1.封装
封装就是使用特殊的语法,对成员属性和成员方法进行包装,达到保护和隐藏的目的
但是一定注意,不能把成员全部封装死,就失去意义了
被封装的成员主要是供类的内部使用
被特殊语法封装的成员,会有不同的访问的权限
封装的级别
封装的级别
成员 ==> 公有的
_成员 ==> 受保护的 (约定俗成,而python没有具体实现)
__成员 ==> 私有的
公有的 public 受保护的 protected 私有的 private
在类的内部 OK OK OK
在类的外部 OK No(python可以) No
封装的实现
公有的封装:
定义:默认定义的成员都属于公有成员
特征:公有的成员可以在任何位置进行访问和操作
受保护封装
定义:在成员名称前面加一个下划线 _成员名称
特征:受保护的成员和公有成员一样可以在任何位置进行访问,但是一般不要随便访问和操作受保护成员
私有的封装
定义:在成员名称前面加两个下划线 __成员名称
特征:私有的成员只能在当前类的内部去访问和操作,不能在类的外部进行操作
查看对象的成员
# 查看对象的所以成员
print(ym.__dict__) # 可以获取当前对象的所有成员信息
# print(Person.__dict__) # 可以获取当前类的所有成员信息
#{'name': '杨幂', '_age': 28, '_Person__sanwei': '60 55 60'}
了解:
- 在python中并没有实现受保护的封装,属于开发者的约定俗成。
- python中的私有化封装是通过改名策略实现的,并不是真正的私有化
2.继承
计算机中的继承
在面向对象中,一个类去继承父类,那么这个类就拥有了父类中的所有成员(除了私有成员)
概念:
-
被其它类继承的类,这个类称为 父类 也叫做 基类 或者 超类
-
继承其它类的类,这个类称为 子类,也叫做 派生类
继承的意义:
提高代码的重用性,建立新的类与类的关系,方便其它逻辑的操作
继承语法格式
class 父类():
pass
class 子类(父类):
pass
继承的特征
- 在不指定继承的父类时,所有类都继承自object类(系统提供) 了解
- 子类继承了父类后,就拥有了父类中的所有成员包括魔术方法(除了私有成员)
- 子类继承父类后,并不会把父类的成员复制给子类,而去引用
- 子类继承父类后可以重写父类中的方法,叫做 重写
- 子类重写父类的方法,依然可以使用
super().父类方法名()
的方式调用父类的方法 - 子类中如果定义了父类中不存在的方法,称为对父类的扩展
- 一个父类可以被多个子类继承,还可以存在 链式继承 。
- 链式继承:A类继承了B类,B类继承了C类,C类继承了D类。。。
单继承和多继承
单继承
单继承:一个类只能继承一个父类的方式。
语法格式:
class 父类():
pass
class 子类(父类):
pass
多继承
多继承:一个类去继承多个父类的方式。
语法格式:
class 父亲():
pass
class 母亲():
pass
class 子类(父亲,母亲):
pass
菱形继承(钻石继承)
A
B C
D
# D类去继承了B类和C类,然后B类和C类又分别继承了A类,这种继承关系称为 菱形继承
问题:在这种菱形继承关系中,类与类的关系,及super()调用时的顺序
'''
在定义类后,程序会自动生成一个继承的列表 MRO (Method Realtion Order) 方法关系列表
MRO列表生成原则:
1. 子类永远在父类的前面
2. 同一等级的类,按照子类中的继承顺序摆放
3. 先子类,后父类的顺序原则,最终的类时系统提供的object类
MRO的调用方法
类名.mro()
'''
C.mro()
# [<class 'C'>, <class 'F'>, <class 'M'>, <class 'HuMan'>, <class 'object'>]
# super()在调用时,并不是查找父类,而是去MRO列表上找上一个类。
# super()方法在调用时,会自动把当前self传入到上一级的类的方法中
类关系检测 issubclass()
issubclass() 检测一个类是否时另一个类的子类
# 检测一个类是否是另一个类的子类
res = issubclass(D,B) # True 检测D类是不是B类的子类
res = issubclass(D,C) # True 检测D类是不是C类的子类
res = issubclass(D,A) # True 检测D类是不是A类的子类
res = issubclass(A,D) # False 检测A类是不是D类的子类
3.多态
对于同一个方法,由于调用的对象不同,产生了不同形态的结果
示例:
# 多态 普通版本
# 对于同一个方法,由于调用的对象不同(或者传入的对象不同),最终实现了不同的结果
# 定义电脑类
class Computer():
# 在电脑类中定义一个 sub 的规范的接口 方法
def usb(self,obj):
obj.start()
# 定义鼠标类
class Mouse():
def start(self):
print('鼠标启动成功,可以双击单击嗨起来。。。')
# 定义键盘类
class KeyBord():
def start(self):
print('键盘启动成功了,赶紧双击666。。。')
# 定义 U盘 类
class Udisk():
def start(self):
print('U盘启动了,赶紧检查一下我的种子还在不在。。。')
# 实例化对象
c = Computer() # 电脑对象
m = Mouse() # 鼠标对象
k = KeyBord() # 键盘对象
u = Udisk() # u盘对象
# 把不同的设备插入到电脑的usb的接口中
c.usb(m)
c.usb(k)
c.usb(u)
四、面向对象
高阶
内置成员
# 获取类/对象的所属成员 类/对象.__dict__
res = Demo.__dict__ # 获取当前类的所属成员
res = obj.__dict__ # 获取当前对象的所属成员
# 获取类的文档信息 类/对象.__doc__
res = Demo.__doc__
res = obj.__doc__
# 获取类名称组成的字符串
res = Demo.__name__
# 获取类所在的文件名称,如果是当前文件,显示为__main__
res = Demo.__module__
# __bases__ 获取当前类的父类列表
res = Demo.__base__ # 获取继承的第一个父类
res = Demo.__bases__ # 获取继承的所有的父类列表
# MRO列表 获取当前类的继承链
res = Demo.__mro__
方法的分类
1. 对象方法
特征:
1. 在类中定义的方法,含有self参数
2. 含有self的方法,只能使用对象进行调用
3. 该方法会把调用的对象传递进来
2. 类方法
特征:
1。在类中定义的方法,使用装饰器 @classmethod 进行了装饰
2。方法中有cls这个行参。不需要实例化对象,直接使用类进行调用
3。会把调用这个方法的类传递进来
3. 绑定类方法
特征:
1。在类中定义的方法
2。只能使用类进行调用
3。不会传递对象或者类进来
4. 静态方法
特征:
1。在类中定义的方法,使用了 装饰器 @staticmethod 进行了装饰
2。可以使用对象或者类进行调用
3。不会传递对象或者类进来
常用函数
# 检测类和对象相关
# issubclass(子类,父类) # 检测一个类是否为另一个类的子类
# res = issubclass(D,B)
# isinstance(对象,类) # 检测一个对象是否是该类或该类的子类的实例化结果
# res = isinstance(d,A)
# 操作类和对象成员相关
# hasattr(对象/类,'成员名称') 检测类/对象是否包含指定名称的成员
# res = hasattr(d,'name')
#getattr(对象/类,'成员名称') # 获取类/对象的成员的值
# res = getattr(d,'say')
# setattr(对象/类,'成员名称','成员的值') 设置类/对象的成员的属性值
res = setattr(d,'name','ooo')
# print(d.name)
#delattr(类/对象,'成员名称') 删除类/对象的成员属性 和 del 直接删除对象的成员是一样的结果
# delattr(D,'name')
# dir() #获取当前对象所以可以访问的成员的列表
res = dir(d)
魔术方法
魔术方法就是不需要手动调用就可以自动执行的方法
1. __init__ 初始化方法 *****
触发机制:当实例化对象之后就会立即触发的方法
作用: 为当前创建的对象完成一些初始化的操作,比如:成员属性的赋值,方法的调用,打开或创建一些资源。。
参数: 一个self,接受当前对象,其它参数根据需求进行定义即可
返回值: 无
注意事项:无
2. __new__ 构造方法 ****
触发机制:实例化对象时自动触发(在__init__之前触发)
作用: 管理控制对象创建的过程
参数: 一个cls 接收当前类,其它参数根据初始化方法的参数进行决定
返回值: 必须返回object.__new__(cls)进行对象的创建,如果没有返回值,则实例化对象的结果为None
注意事项:
__new__方法的参数和__init__方法的参数要保持一致,除了第一个参数
必须返回object.__new__(cls)进行对象的创建,如果没有返回值,则实例化对象的结果为None
应用场景:设计模式中的单例设计模式
3. __del__ 析构方法 *****
触发机制:当该类对象被销毁时,自动触发
作用: 关闭或释放对象创建时打开或创建的一些资源
参数: 一个self,接受当前的对象
返回值:无
注意事项: 无
4. __call__ ***
触发机制: 把对象当作函数直接调用时自动触发
作用: 一般用于归纳类或对象的操作步骤,方便调用
参数: 一个self接收当前对象,其它参数根据调用需求缺点
返回值: 可有可无
5.__len__
触发机制: 当使用len函数去检测当前对象的时候自动触发
作用: 可以使用len函数检测当前对象中某个数据的信息
参数: 一个self 接收当前对象
返回值: 必须有,并且必须是一个整型
注意事项:len要获取什么属性的值,就在返回值中返回哪个属性的长度即可
6.__str__
触发机制: 当使用str或者print函数对对象进行操作时自动触发
作用: 代码对象进行字符串的返回,可以自定义打印的信息
参数: 一个self,接收当前对象
返回值: 必须有,而去必须是字符串类型的值
7.__repr__
触发机制:在使用repr方法对当前对象进行转换时自动触发
作用: 可以设置repr函数操作对象的结果
参数: 一个self,接收当前对象
返回值: 必须有,而去必须是字符串类型的值
注意:正常情况下,如果没有__str__这个魔术方法,__repr__方法就会代替__str__魔术方法
8.__bool__
触发机制: 当前使用bool函数转换当前对象时,自动触发.默认情况下,对象会转为True
作用: 可以代替对象进行bool类型的转换,可以转换任何数据
参数 : 一个self 接收对象
返回值: 必须是一个布尔类型的返回值
成员相关魔术方法
## 1. __getattribute__ 优先级最高
触发机制:当访问对象成员时,自动触发,无论当前成员是否存在
作用: 可以在获取对象成员时,对数据进行一些处理
参数: 一个self接收对象,一个item接收当前访问的成员名称
返回值: 可有可无,返回的值就是访问的结果
注意事项:在当前的魔术方法中,禁止对当前对象的成员进行访问,会触发递归。
如果想要在当前魔术方法中访问对象的成员必须使用 object 来进行访问
格式: object.__getattribute__(self,item)
## 2. __getattr__
触发机制: 当访问对象中不存在的成员时,自动触发
作用: 防止访问不存在的成员时报错,也可以为不存在的成员进行赋值操作
参数: 一个self接收当前对象,一个item接收当前访问的成员名称
返回值: 可有可无
注意事项: 当存在 __getattribute__ 方法时,会去执行 __getattribute__ 方法
也要注意,不要在当前的方法中再次去访问这个不存在的成员,会触发递归操作
## 3. __setattr__
触发机制: 当给对象的成员进行赋值操作时会自动触发(包括添加,修改)
作用: 可以限制或管理对象成员的添加和修改操作
参数: 1。self 接收当前对象 2。key 设置的成员名 3。val 设置的成员值
返回值: 无
注意事项:在当前的魔术方法中禁止给当前对象的成员直接进行赋值操作,会触发递归操作
如果想要给当前对象的成员进行赋值,需要借助 object
格式: object.__setattr__(self,key,value)
## 4. __delattr__
触发机制: 当删除对象成员时自动触发
作用: 可以去限制对象成员的删除,还可以删除不存在成员时防止报错
参数:1,self 接收当前对象 2。item 删除的成员名称
返回值: 无
注意事项: 在当前魔术方法中禁止直接删除对象的成员,会触发递归操作。
如果想要删除当前对象的成员,那么需要借助 object
格式: object.__delattr__(self,item)
访问成员的顺序!!!
- 调用
__getattribute__
魔术方法 - 调用数据描述符【后面会讲】
- 调用当前对象的成员
- 调用当前类的成员
- 调用非数据描述符【后面会讲】
- 调用父类的成员
- 调用
__getattr__
魔术方法
以上步骤时调用某个成员时的顺序,前面的能够调用成功,后面则不在执行
描述符
当一个类中,包含了三个魔术方法(
__get__,__set__,__delete__
)之一,或者全部时,那么这个类就称为描述符类
作用
描述符的作用就是对一个类中的某个成员进行一个详细的管理操作(获取,赋值,删除)
描述符就是代理了一个类中的成员的操作,描述符属于类,只能定义为类的属性
三个魔术方法
'''
__get__(self, instance, owner)
触发机制:在访问对象成员属性时自动触发(当该成员已经交给描述符管理时)
作用:设置当前属性获取的值
参数:1. self 描述符对象 2.被管理成员的类的对象。3.被管理成员的类
返回值:返回值作为成员属性获取的值
注意事项:无
__set__(self, instance, value)
触发机制:在设置对象成员属性时自动触发(当该成员已经交给描述符管理时)
作用:对成员的赋值进行管理
参数:1. self 描述符对象 2.被管理成员的类的对象。3.要设置的值
返回值:无
注意事项:无
__delete__(self, instance)
触发机制:在删除对象成员属性时自动触发(当该成员已经交给描述符管理时)
作用:对成员属性的删除进行管理
参数:1. self 描述符对象 2.被管理成员的类的对象。
返回值:无
注意事项:无
'''
数据描述符:(完整)
同时具备三个魔术方法的类就是 数据描述符
非数据描述符:(不完整)
没有同时具备三个魔术方法的类就是 非描述符类
基本使用格式
把当前的描述符类赋值给一个需要代理的类中的成员属性
代码示例:
# 定义描述符类
class PersonName():
__name = 'abc'
def __get__(self, instance, owner):
# print(self,instance,owner)
return self.__name
def __set__(self, instance, value):
# print(self,instance,value)
self.__name = value
def __delete__(self, instance):
# print(self,instance)
# del self.__name
print('不允许删除')
# 定义的普通类
class Person():
# 把类中的一个成员属性交给一个描述符类来实现
# 一个类中的成员的值是另一个描述符类的对象()
# 那么当对这个类中得成员进行操作时,可以理解为就是对另一个对象的操作
name = PersonName()
# 实例化对象
zs = Person()
print(zs.name)
zs.name = '张三'
print(zs.name)
del zs.name
print(zs.name)
描述符应用解析
#定义一个学生类,需要记录 学员的id,名字,分数
class Student():
def __init__(self,id,name,score):
self.id = id
self.name = name
self.score = score
def returnMe(self):
info = f'''
学员编号:{self.id}
学员姓名:{self.name}
学员分数:{self.score}
'''
print(info)
'''
# 要求:学员的分数只能在0-100范围中
解决方法:
1。在__init__方法中检测当前分数范围
# 检测分数范围
if score >= 0 and score <= 100:
self.score = score
这个解决方案只能在对象初始化时有效。
2。 定义一个setattr魔术方法检测
检测如果给score分数进行赋值时,进行分数的检测判断
def __setattr__(self, key, value):
# 检测是否是给score进行赋值操作
if key == 'score':
# 检测分数范围
if value >= 0 and value <= 100:
object.__setattr__(self, key, value)
else:
print('当前分数不符合要求')
else:
object.__setattr__(self,key,value)
假如 学员的分数不止一个时怎么办,比如 语文分数,数学分数,英语分数
另外就是当前这个类中的代码是否就比较多了呢?
3。可以思考使用描述符来代理我们的分数这个属性
1.定义Score描述符类
2.把学生类中的score这个成员交给描述符类进行代理
3.只要在代理的描述符类中对分数进行赋值和获取就ok了
'''
#定义描述符类 代理分数的管理
class Score():
def __get__(self, instance, owner):
return self.__score
def __set__(self, instance, value):
if value >= 0 and value <= 100:
self.__score = value
else:
print('分数不符合要求')
# 使用描述符类代理score分数属性
class Student():
score = Score()
def __init__(self,id,name,score):
self.id = id
self.name = name
self.score = score
def returnMe(self):
info = f'''
学员编号:{self.id}
学员姓名:{self.name}
学员分数:{self.score}
'''
print(info)
# 实例化对象
zs = Student(1011,'张三疯',99)
zs.returnMe()
zs.score = -20
zs.score = 88
zs.returnMe()
描述符的三种定义格式
# 格式一 通过定义 描述符类来实现 推荐
'''
class ScoreManage():
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
class Student():
score = ScoreManage()
'''
# 格式二, 使用 property 函数 来实现
'''
class Student():
# 在当前需要被管理的类中 直接定义类似下面三个方法
def getscore(self):
print('getscore')
def setscore(self,value):
print('setscore',value)
def delscore(self):
print('delscore')
# 在 property 函数中指定对应的三个方法,对应的方法 1。__get__,2。__set__,3。__delete__
score = property(getscore,setscore,delscore)
zs = Student()
# print(zs.score)
# zs.score = 200
# del zs.score
'''
# 格式三 使用 @property 装饰器语法来实现
'''
class Student():
__score = None
@property
def score(self):
print('get')
return self.__score
@score.setter
def score(self,value):
print('set')
self.__score = value
@score.deleter
def score(self):
print('delete')
del self.__score
zs = Student()
# print(zs.score)
zs.score = 199
print(zs.score)
del zs.score
'''
设计模式
设计模式是前人为完成某个功能或需求,根据经验和总结,对实现的代码步骤和代码设计进行了总结和归纳,成为了实现某个需求的经典模式。
设计模式并不是固定的代码格式,而是一种面向对象编程的设计
单例(单态)设计模式
在当前脚本中,同一个类只能创建出一个对象去使用。这种情况就成为单例(单态)。
'''
实现单例的案例,思考:
单例和婚姻法的关系,特别像,一个人只能有一个结婚对象
在社会中是如何完成一夫一妻制的?
如果要结婚,必须要到 民政局 登记
民政局 需要检测两个人的户口本,看上面是否属于 结婚的状态
如果是已婚,肯定就撵出去了。
如果没有结婚,可以给盖个章了,开始登记。
那么按照这样的思路如何去实现 python中的单例设计模式呢?
1。需要有一个方法,可以去控制当前对象的创建过程?
构造方法 __new__
2。需要有一个标示来存储和表示是否有对象
创建一个私有属性 进行存储,默认值为None
3。在创建对象的方法中去检测和判断是否有对象?
如果没有对象,则创建对象,并且把对象存储起来,返回对象
如果存储的是对象,则直接返回对象,就不需要创建新的对象了
'''
class Demo():
# 2.定义私有属性存储对象,默认值为None
__obj = None
# 1.定义构造方法
def __new__(cls, *args, **kwargs):
# 3。在创建对象的过程中,判断是否有对象
if not cls.__obj:
# 判断如果没有对象,则创建对象,并且存储起来
cls.__obj = object.__new__(cls)
# 直接把存储的对象返回
return cls.__obj
# 实例化对象
a = Demo()
b = Demo()
print(a)
print(b)
'''
<__main__.Demo object at 0x106f4d850>
<__main__.Demo object at 0x106f4d850>
'''
Mixin 混合设计模式
Mixin类
- Mixin 必须是表示一种功能,而不是一个对象。
- Mixin 的功能必须单一,如果有多个功能,那就多定义Mixin类
- python 中的Mixin是通过多继承实现的
- Mixin 这个类通常不单独使用,而是混合到其它类中,去增加功能的
- Mixin 类不依赖子类的实现,即便子类没有继承这个Mixin,子类也能正常运行,可能就是缺少了一些功能。。
使用Mixin混入类的好处?
-
Mixin 这个混入类的设计模式,在不对类的内容修改的前提下,扩展了类的功能
-
Mixin 混入类为了提高代码的重用性,使得代码结构更加简单清晰
-
可以根据开发需要任意调整功能(创建新的Mixin混入类)避免设计多层次的复杂的继承关系。
示例:
'''
继承需要有一个必要的前提,继承应该是一个 'is-a' 的关系
例如:
苹果可以去继承水果,因为苹果就是一个水果
苹果不能继承午饭,因为午饭可以有苹果也可以没有
比如 汽车可以继承 交通工具,因为汽车本身就是一个交通工具
交通工具有哪些?
汽车,飞机,直升机,这些都属于 交通工具
那么如何去设计这些类的关系呢?
比如创建一个交通工具类,然后属于交通工具的都来继承,再去实现。。。
但是,飞机和直升机都有飞行的功能,而汽车并没有,那么在交通工具中如果去定义 飞行这个功能,那就不太合适了。。
能不能在飞机和直升机类中分别实现 飞行 这个功能呢?可以,但是代码又无法重用。
怎么办?
单独去定义交通工具类,和 飞行器 这个两个父类,这样飞机和直升机就可以去继承这两个类.
'''
# 交通工具 vehicle
class vehicle():
# 运输货物
def huo(self):
print('运输货物')
# 搭载乘客
def ren(self):
print('搭载乘客')
# 飞行器
class FlyingMixin():
def fly(self):
print('可以起飞了。。。')
# 定义汽车类
class cart(vehicle):
pass
# 定义飞机
class airplane(vehicle,FlyingMixin):
pass
# 定义直升机
class helicopter(vehicle,FlyingMixin):
pass
# 此时去定义一个飞行器的类 Flying,让需要飞行的交通工具,直接继承这个类。可以解决这个问题。
# 但是,1。出现类多继承,违背了'is-a' 2。飞行器这个类很容易被误解
# 解决方案也是使用多继承,但是给飞行器这个类,定义成为一个 Mixin 混合类,
# 此时就是等于把飞行器这个类,作为了一个扩展的功能,来扩展其它类
'''
在上面的代码中,虽然直升机和飞机都使用了多继承,也就是继承了FlyingMixin
但是由于 FlyingMixin 类加了 Minin这个名,就告诉了后面阅读代码的人,这个类是一个Mixin类
'''
抽象类(了解)
抽象类是一个特殊的类: 1. 抽象类不能用,不能直接实例化成为一个对象。 2. 抽象类中包含了抽象方法,抽象方法就是没有实现代码的方法。 3. 抽象类需要子类继承,并重写父类的抽象方法。才可以使用。
抽象类,一般应用在程序设计,程序设计中一般是要对功能和需求进行规划,其中有一些需求是明确的并且可以完成的,
但是也可能会有一些需求是不明确的,或者不确定具体需要怎么实现,
那么此时就可以把这个不确定怎么实现或者需要后面再去实现的方法,定义为抽象方法(只定义方法名,不写具体代码)
抽象类的应用:
例如要开发一个框架,这个框架要有一些基本功能和扩展功能。。。。
但是你具体用这个框架开发什么样的产品,开发框架的人并不清楚或者确定。
因此框架就具备一定的功能,并且留下来一些方法的定义,剩下的就是需要自己在方法中具体实现自己业务逻辑。
抽象类的定义:
import abc
# 如果要定义为抽象类,那么这个类的 metaclass属性必须是 metaclass=abc.ABCMeta
class WriteCode(metaclass=abc.ABCMeta):
#需要抽象的方法,使用装饰器进行装饰
@abc.abstractmethod
def write_php(self):
pass
def write_java(self):
print('实现了java代码的开发')
def write_python(self):
print('实现了python代码的开发')
# 抽象类不能直接实例化对象
# obj = WriteCode()
# print(obj)
#TypeError: Can't instantiate abstract class WriteCode with abstract methods write_php
# 定义子类,继承抽象类,并实现抽象类中的抽象方法
class Demo(WriteCode):
def write_php(self):
print('实现了php代码的开发')
a = Demo()
print(a)
a.write_java()
a.write_php()
a.write_python()
五、装饰器 decorator
装饰器定义
在不改变原有函数代码,且保持原函数调用方法不变的情况下,给原函数增加新的功能(或者给类增加属性和方法)
核心思想:用一个函数(或者类)去装饰一个旧函数(或者类),造出一个新函数(或者新类)
应用场景:引入日志,函数执行时间的统计,执行函数前的准备工作,执行函数后的处理工作,权限校验,缓存等
语法规则:在原有的函数上加上 @符,装饰器会把下面的函数当作参数传递到装饰器中,@符又被成为 语法糖
1.装饰器原型(闭包)
# 1。 装饰器的原型
### 利用闭包,把函数当作参数传递,并且在函数内去调用传递进来的函数,并返回一个函数
# 定义外函数,接收一个函数作为参数
def outer(f):
# 定义内函数,并且在内函数中调用了外函数的参数
def inner():
print('我是外函数中的内函数1')
f()
print('我是外函数中的内函数2')
return inner
# 定义普通函数
# def old():
# print('我是一个普通的函数')
#
# # old() # 作为普通函数直接调用
# old = outer(old) # outer返回了inner函数,赋值给了old
# old() # 此时再调用old函数时,等同于调用了 inner 函数
# 改为装饰器用法
@outer # 此处使用的@outer的语法就是把outer作为了装饰器,等同于 old = outer(old)
def old():
print('我是一个普通的函数')
old() # old函数经过 outer装饰器进行了装饰,代码和调用方法不变,但是函数的功能发送了改变
2.装饰器的应用:统计函数的执行时间
# 装饰器应用场景-统计函数执行时间
import time
# 定义一个统计函数执行时间的 装饰器
def runtime(f):
def inner():
start = time.perf_counter()
f()
end = time.perf_counter() - start
print(f'函数的调用执行时间为:{end}')
return inner
# 定义一个函数
@runtime
def func():
for i in range(5):
print(i,end=" ")
time.sleep(1)
func()
3.装饰器嵌套语法
# 1.定义装饰器
# 外函数
def outer(func):
#内函数
def inner():
print('找到妹子,成功拿到微信。。。3')
func() # 在内函数中调用外函数中的行参-函数
print('约妹子,看一场午夜电影。。。4')
# 在外函数中返回内函数
return inner
# 2。在定义一个装饰器
def kuozhan(f):
def kzinner():
print('扩展1')
f()
print('扩展2')
return kzinner
# 3. 装饰器的嵌套 先执行下面的,再执行上面的。
@kuozhan # 2。再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数
@outer # 1。先使用离得近的 outer装饰器 装饰love函数,返回了一个 inner函数
def love():
print('跟妹子畅谈人生和理想。。。5')
love()
''' 结果和过程的解析
1 3 5 4 2
1 先使用离得近的 outer装饰器 装饰love函数,返回了一个 inner函数
2 再使用上面的 kuozhan 装饰器,装饰 上一次返回的 inner 函数,又返回了 kzinner 函数
最后在调用love函数的时候是怎么执行的
love() == kzinner()
===> 1
===> inner()
===> 3
===> love() ===> 5
===> 4
===> 2
'''
4.对带有参数的函数进行装饰
# 定义装饰器
def outer(func):
# 如果装饰器带有参数的函数,需要在内函数中定义行参,并传递给调用的函数。因为调用原函数等于调用内函数
def inner(var):
print(f'找到{var}妹子,成功拿到微信。。')
func(var)
print(f'约{var}妹子,看一场午夜电影。。')
return inner
# 有参数的函数
@outer
def love(name):
print(f'跟{name}妹子畅谈人生。。。')
love('思思') #love() ==> inner() love('思思') ===> inner('思思')
5.对多参数的函数进行装饰
# 装饰带有多参数的函数
def outer(func):
def inner(who,name,*args,**kwargs):
print('约到妹子,聊微信。。。')
func(who,name,*args,**kwargs)
print('天色一晚,怎么办?')
return inner
# 定义多参数的 函数
@outer
def love(who,name,*args,**kwargs):
print(f'{who}跟{name}畅谈人生。。。')
print('完事去吃了好多美食',args)
print('看了一场电影',kwargs)
love('三多','思思','火锅','辣条','7块钱的麻辣烫',mov='唐山大地震')
'''
love() ==> inner()
love(...) ==> inner(...)
inner(...) ==> love(...)
'''
6.带有参数的装饰器
你会遇到带有参数的装饰器,例如Django框架中的 @login_required(login_url=‘/accounts/login/’)
# 如果你的装饰器需要有参数,那么给当前的装饰器套一个壳,用于接收装饰器的参数
def kuozhan(var):
def outer(func):
def inner1():
print('妹子给了你微信')
func()
def inner2():
print('妹子给介绍了个大妈')
func()
# 装饰器壳的参数,可以用于在函数内去做流程控制
if var == 1:
return inner1
else:
return inner2
return outer
@kuozhan(2) # kuozhan(var) ==> outer() ==> outer(love) ==> inner()
def love():
print('谈谈人生。。。')
love()
7.用类装饰器装饰函数
# 类装饰器装饰函数
class Outer():
# 魔术方法:当把该类的对象当作函数调用时,自动触发 obj()
def __call__(self,func):
self.func = func # 把传进来的函数作为对象的成员方法
return self.inner # 返回一个函数
# 在定义的需要返回的新方法中 去进行装饰和处理
def inner(self,who):
print('拿到妹子的微信。。。')
self.func(who)
print('看一场午夜电影。。。')
@Outer() # Outer() ==> obj @obj==>obj(love) ==> __call__(love) ==> inner()
def love(who):
print(f'{who}和妹子谈谈人生和理想。。。')
love('川哥') # inner('川哥')
print(love) # 此时的 love就是属于Outer类这个对象中的inner方法
8.用类方法装饰函数
# 用类方法装饰函数
class Outer():
def newinner(func):
Outer.func = func # 把传递进来的函数定义为类方法
return Outer.inner # 同时返回一个新的类方法
def inner():
print('拿到妹子微信')
Outer.func()
print('看一场午夜电影')
@Outer.newinner # Outer.newinner(love) ==> Outer.inner
def love():
print('和妹子谈谈人生喝喝茶。。。')
love() # love() ==> Outer.inner()
到目前为止以上所以形式的装饰器,包括 函数装饰器,类装饰器,类方法装饰器,都有一个共同特点:都是在给函数去进行装饰,增加功能。
用装饰器装饰类
还有一种装饰器,是专门装饰类的。也就是在类的定义的前面使用@装饰器这种语法
@装饰器
class Demo():
pass
装饰器给函数进行装饰,目的是不改变函数调用和代码的情况下给原函数增加了新的功能。
装饰器给类进行装饰,目的是不改变类的定义和调用的情况下给类增加新的成员(属性或方法)。
9.用函数装饰器装饰类
# 使用函数装饰器,给类进行装饰,增加新的属性和方法
# 定义函数,接收一个类。返回修改后的类
def kuozhan(cls):
def func2():
print('我是在装饰器中追加的新方法,func2')
cls.func2 = func2 # 把刚才定义的方法赋值给 类
cls.name = '我是在装饰器中追加的新属性 name'
#返回时,把追加类新成员的 类 返回去
return cls
@kuozhan # kuozhan(Demo) ==> cls ==> Demo
class Demo():
def func():
print('我是Demo类中定义的func方法')
Demo.func() # 此时在调用的Demo类是通过装饰器,更新过的Demo类
Demo.func2()
print(Demo.name)
10.使用类装饰器装饰类
class KuoZhan():
def __call__(self, cls):
# 把接收的类,赋值给当前对象,作为一个属性
self.cls = cls
# 返回一个函数
return self.newfunc
def newfunc(self):
self.cls.name = '我是在类装饰器中追加的新属性 name'
self.cls.func2 = self.func2
# 返回传递进来的类的实例化结果,obj
return self.cls()
def func2(self):
print('我是在类装饰器中追加的新方法 func2')
@KuoZhan() # KuoZhan() ==> obj ==> @obj(Demo) ==> __call__(Demo) ==> newfunc
class Demo():
def func(self):
print('我是Demo类中定义的func方法')
obj = Demo() # Demo() ==> newfunc() ==> obj
obj.func()
obj.func2()
print(obj.name)
# 思考: 此时的 obj这个对象,是哪个类的对象。Demo还是KuoZhan
print(obj) # 此时的obj依然是Demo类的实例化对象,只不过经过装饰后,增加了新的属性和方法