python学习笔记 第八章 回顾补充1
Python学习笔记 第八章 回顾补充1
1.数据类型的补充
数据类型的分类:
- 可变类型(不可哈希) list dict set
- 不可变类型(可哈希)str bool int tuple
1.1元组
应用:重要的数据:用户名、密码、个人信息,不想让别人改动的一些数据
元组的拆包
a, b = (1, 2)
#分别将元组中的值赋值
如果元组中只包含一个元素且没有逗号,则该元组不是元组,与该元素数据类型一致,如果有逗号,则其也是元组
元组中也有一些其他的方法:
index 通过元素找索引,找到第一个索引的下标进行返回
count 获取某元素在列表中出现的次数
1.2range
类似于列表,自定制数字范围的数组列表
和切片的规则一样
rang(1, 100, 2)
1.3字典
{}括号括起来,一键值对形式存储的容器型数据
- 键必须是不可变的数据类型:int str (bool 和 tuple几乎不用)
- 值可以是任意的数据类型,对象
- 字典3.5版本之前且包括3.5都是无序的
- 字典3.6版本会按照初次建立的字典的顺序进行排列,学术上不认为是有序的
- 字典3.7以后都是有序的
字典创建的方式
#方式一
dic = dict((('one', 1), ('two', 2)))
#生成的字典为
dic = {'one':1, 'two':2}
#方式二
dic = dict(one = 1, two = 2)
#方式三
dic = dict({'one':1, 'two':2})
字典的键必须是唯一的
字典的增删改查
#增 / 改
dic['sex'] = '男'
dic.update(hobby='运动') #如果字典中没有hobby键则会增加key和value,若存在则修改value的值
dic = dict.fromkeys('abc', 100)
print(dic) #{'a':100, 'b':100, 'c':100} 键来自于一个可迭代对象,共用一个值
#setdefault 设置默认值,有则不变,无则增加
dic.setdefault('hobby', 'ball')
#删除
pop('age') #按照键删除,返回的是值,如果没有age此键,则会报错
pop('age', '无') #如果设置第二个参数,则字典中无论字典中是否有此键都不会报错,且无此键返回的值为第二个参数
#清空
dic.clear()
#删除2
del dic['age'] #如果没有这个键会报错
#查
dic['age'] #按照键去查询值,但是没有此键则会报错
dic.get('hobby') #按照键进行查询,没有此键返回None
dic.keys() #输出所有的键,此数据类型可以转换为列表
dic.values() #输出所有的值
dic.items() #输出所有的键值对,返回一个((‘键’, ‘值’), (‘键’, ‘值’))的元组,可以利用元组拆分的方式分别获取到键和值
for key, value in dic.values():
print(key, value)
注意:在循环字典的时候,不能删除字典的元素,否则会报错
1.4集合 set
里面的元素是不可变的元素,但是集合本身是可变的
所以集合里面的元素只能是:int bool str tuple
集合是无序的
- 集合的作用:
- 列表的去重
- 关系测试:交集、并集、差集, ...
set1 = set({'one', 1, 'good'})
#增
set1.add('xx')
#迭代着增加
set1.add('asdfadsf') #把每一个元素都增加进去,重复的也会去重
#按照元素进行删除 (只能按照元素进行删除,集合是无序的)
set1.remove(1)
#随机删除
set1.pop()
#集合的其他操作
set1 = {1, 2, 3, 4, 5}
set2 = {2, 3, 5, 6, 7}
print(set1 & set2) #交集 or set1.intersection(set2)
print(set1 | set2) #并集 or set1.union(set2)
print(set1 - set2) #差集(set1独有的) or set1.difference(set2)
print(set1 ^ set2) #反交集 (两个集合之间不共有的元素) or set1.symmetric_difference(set2)
print(set1 < set2) #子集 or set1.issubset(set2)
print(set1 > set2) #超集 or set1.issuperset(set2)
1.5字符串
字符串一些方法的补充
-
capitalize 首字母大写,其余字母变小写
-
swapcase 大小写翻转
-
center 居中
s1 = 'apple' print(s1.center(20)) #字符串的总长度为20,让s1字符串位于中间 -
find 寻找字符串的元素是否存在,返回找到的元素的索引,如果找不到返回-1
-
index 返回的找到的元素的索引,找不到报错
%s和format和f格式化输出
name = 'apple'
age = 18
#方式一
msg = '我叫%s, 年龄%s' %(name, age)
#方式二
msg1 = '{}, {}'.format(name, age)
#方式三
msg2 = f'{name}, {age}'
msg3 = f'{name.upper()}'
#结合函数来写
def func(a,b):
return a + b
msg4 = f'{func(1 + 1)}'
#优点:1.结构更加简化 2.可以结合表达式,函数进行使用 3.效率提升更多
1.6列表
补充一些列表的方法:
l1 = [1, 6, 7, 2, 5, 4, 9]
l1.sort() #默认从小到大进行排序
l1.sort(reverse = True) #从大到小进行排序
l1.reverse() #反转
#列表之间相加
l2 = ['apple', 'pear']
print(l1 + l2) #
#列表与数字相乘
print(l2 * 3)
列表的特性
-
索引会随着元素被删除而改变
#删除奇数位置的索引 #最简单的方法 del list1[1::2] #倒序删除元素 #循环一个列表的时候最好不要改变列表的大小,会影响索引的位置
1.7基础数据类型的总结
按照存储空间的占用分(从低到高)
- 数字
- 字符串
- 集合:无序,即无序存索引相关信息
- 元组:有序,需要存索引相关信息,不可变
- 列表:有序,需要存索引相关信息,可变,需要处理数据的增删改查
- 字典:有序,需要存key和value映射的相关信息,可变,需要处理数据的增删改(3.6版本以后有序)
按照存值个数区分
| 标量/原子类型 | 数字,字符串 |
|---|---|
| 容器类型 | 列表,字典, 元组 |
按照可变不可变区分
| 可变 | 列表,字典 |
|---|---|
| 不可变 | 数字,字符串,元组,布尔值 |
按访问顺序区分
| 直接访问 | 数字 |
|---|---|
| 顺序访问(序列类型) | 字符串,列表,元组 |
| key访问(映射类型) | 字典 |
2.is 和 id 和 ==
Id()现实的是内存的地址,两个对象如果内存地址相同则就是一个
l1 = [1, 2, 3]
l2 = [1, 2, 3]
print(l1 == l1) # True
# ==比较的是值
print(l1 is l2) # False
# is判断的是内存地址是否相同
当id相同,则其值一定相同
值相同,其id不一定相同
注意:python是高级语言,id查看的地址其实是虚拟的地址
3.代码块
python是由代码块构造的,块是一个python程序的文本,他是作为一个单环执行的。
代码块:一个模块,一个函数,一个类,一个文件等都是代码块
而作为交互方式输入(如cmd)的每一个命令都是一个代码块
同一个代码块下的缓存机制
- 目的:节省内存,提升性能
- 前提条件:在同一个代码块内
- 适用的对象:int str bool
- 具体细则:所有的数组,布尔值,几乎所有的字符串
不同代码块下的缓存机制:小数据池
- 目的:节省内存,提升性能
- 前提条件:在不同的代码块内
- 适用的对象:int str bool
- 具体细则:-5~256的数字,以及满足规则的字符串
4.深浅拷贝
浅copy:会在内存中开辟一个空间存放这个copy的列表,但是列表中的内容还是沿用之前对象的内存地址,所以l1和l2的id不同,但是内容和id相同。
深copy:将可变的数据类型在内存中重新创建一份,而不可变数据类型沿用之前的
总结:深拷贝则会在内存中开辟新的空间,将原列表以及列表里面的可变的数据类型重新创建一份,不可变的数据类型则沿用之前的。
区分:浅拷贝就是嵌套可变的数据类型是同一个,深拷贝的是嵌套可变的数据类型不是同一个
5.编码的进阶
-
ascii:包含英文字母,数字,特殊字符与01010101对应关系
a 01010101 一个字符一个字节表示
-
GBK:包含本国文字(以及英文字母,数字,特殊字符)与01010101对应关系
a 01010101 ascii码中字符:一个字符一个字节表示
中 01010101 01010101 中文:一个字符两个字节表示
-
unicode:包含全世界所有的文字与二进制01010101对应关系
a 01010101 01010101 01010101 01010101
中 01010101 01010101 01010101 00000001
-
Utf-8:包含全世界所有的文字与二进制01010101之间的对应关系(最少用一个字节表示一个字符)
a 01010101 ascii码中的字符:一个字符一个字节表示
To 01010101 01010101 欧洲文字;葡萄牙;西班牙; 一个字符两个字节表示
中 01010101 01010101 01010101 亚洲文字:一个字符三个字节表示
数据在内存中全部都是unicode编码的,但是当你的数据用于网络传输或者存储到硬盘中,必须是以非unicode编码
str类型 ----------转换------------>bytes特殊的字符串类型------->这样可以存储到磁盘中或者进行网络传输
内部编码为unicode 内部编码:非unicode
英文:
str:
内存中的编码方式:unicode
表现形式:'hello'
bytes:
内存中的编码方式:非unicode
表现形式:b'hello'
中文:
str:
内存中的编码方式:unicode
表现形式:'中国'
bytes:
内存中的编码方式:非unicode (utf-8)
表现形式:b'\xe4\xb8\xad\xe5\x9b\xbd'
#Str -------> bytes 编码
s1 = '中国'
b1 = s1.encode('utf-8')
print(b1, type(b1)) #b'\xe4\xb8\xad\xe5\x9b\xbd' <class 'bytes'>
#bytes ------> str 解码
s2 = b1.decode('utf-8')
print(s2) # 中国
6.文件操作
文件操作三部曲:
- 打开文件
- 对文件句柄进行相应的操作
- 关闭文件
open 内置函数,底层调用的是操作系统的接口
encoding 可以不写,不写参数默认使用操作系统的默认编码,windows默认为gbk,linux和mac默认为utf-8
报错:
- UnicodeDecodeError:文件存储时与文件打开时的编码运用不一致
- 第二个错误:路径分隔符产生问题
文件操作的读:
r, rb, r+, r+b 四种模式
rb操作的是非文本的文件,图片、视频、音频
文件操作的写:
w, wb, w+, w+b 四种模式
如果文件存在,会先清空原文件,重新写入新内容
文件操作的追加模式:
a, ab, a+, a+b
没有文件创建文件,追加内容
有文件,在原文件的最后面追加内容
相应的功能:对文件句柄的操作:read read(n) readline() readlines() write() tell() seek() flush()
- seek() 调整光标的位置
- tell() 打印光标的位置
- flush() 强制刷新
with 的优点
-
不用手动关闭文件的句柄
-
可以同时打开多个文件
with open('a.txt', mode='r') as f1, open('b.txt', mode='w') as f2: #在读取原文件的时候以写的模式创建一个新文件 for line in f1: new_line = line.replace('apple', 'pen') f2.write(new_line)
7.函数
以功能(完成一件事)为导向,减少重复的代码,增强代码的可读性
函数的结构:
def 关键字,定义函数
meet 函数名: 与变量设置相同,具有可描述性
函数体:缩进。函数中尽量不要出现print
return 函数的返回值:在函数遇到return 直接结束函数,将数据返回给函数的执行,写多个返回元素是以元组的形式返回函数执行者
参数:
-
函数执行传的参数(实际参数)
-
函数定义的参数,接受参数形式参数(形式参数)
-
默认值参数 a=1可以不用写此参数,普遍需要经常使用
补充一下三元运算:简单的if else
a = 10
b = 20
c = a if a > b else b #如果a>b则返回a,否则返回b
万能参数:
可以接受所有的形式参数
*args:约定俗成的用法
函数定义时:*代表聚合。他将所有的位置参数聚合成一个元组,赋值给args
def eat(*args):
print(args) #('apple', 'pen', 'banana')
print('%s', '%s', '%s' % args)
eat('apple', 'pen', 'banana') #apple, pen, banana
**kwargs:
**将所有的关键字参数聚合到一个字典中,赋值给kwargs
def func(**kwargs):
print(kwargs)
func(name = 'apple', age = '18')
args得到实参的前提:最好放在位置参数后面,默认参数的前面
**kwargs放在最后面:为了不影响默认参数
形式参数最终位置:位置参数,*args, ,默认参数,仅限关键字参数, **kwargs
在函数的调用的时候*表示打散
def func(*args):
print(args) #(1, 2, 3, 44, 55)
func(*[1, 2, 3], *[44, 55]) #func(1, 2, 3, 44, 55)
func(*'abc', *'def') #func('a', 'b', 'c', 'd', 'e', 'f')
从空间的角度,内存级别去研究:
python解释器开始运行的时候,遇到一个变量就会把变量名和值之间的关系记录下来(全局命名空间),当python代码遇到函数的时候,会在函数内存中开辟一个空间,遇到一个变量的时候,将变量名和值之间对应的关系记录下来(命名空间),直到函数调用的时候,解释器会再开辟一块内存(临时名称空间/局部名称空间)来存储这个函数里面的内容,这个时候才会关注函数里面有哪些变量,而函数中的变量只会在函数内部使用,并且随函数执行完毕,这些内存中的所有内容也会被清空。
内置名称空间: 存放的是input, print, len等这些内置函数
全局名称空间:存放的是这个py文件变量的一些对应关系
临时名称空间:随着函数的执行开辟出来,随函数结束而消失
作用域:
- 全局作用域:内置名称空间、全局名称空间
- 局部作用域:局部名称空间
- 局部作用域可以引用全局变量
- 局部作用域不能改变全局作用域的变量,当python解释器读取到局部作用域时,发现了你对一个变量进行修改的操作,解释器会认为你在局部已经定义过这个局部变量了,他就从局部找这个局部变量,报错了。
LEGB原则: B:builtin G:global E:eclose L:local 取值就近原则
globals( ) #返回的是字典:字典里面的键值对,全局作用域的所有内容
locals() #返回的是字典:字典里面的键值对,当前作用域的所有内容
陷阱只针对于默认参数是可变的数据类型
def func(name, alist=[]):
alist.append(name)
return alist
ret1 = func('apple')
print(ret1, id(ret1)) #['apple']
ret2 = func('pear')
print(ret2, id(ret2)) #['apple', 'pear']
#发现alist都是相同的,不会再创建一个[ ]
#如果你的默认参数指向的是可变的数据类型,那么你无论调用多少次这个默认参数,都是同一个
global
- 在局部作用域声明一个全局变量:global xxx
- 在局部作用域修改一个全局变量:global xxx 引入全局变量
nonlocal
-
不能操作全局变量
-
局部作用域:内层函数对外层函数的局部变量进行修改
def wrapper(): count = 1 def inner(): nonlocal count count += 1 print(count) #1 inner() wrapper()
函数名就是函数的内存地址,函数名+括号即可执行次函数
函数名可以作为容器数据类型的元素,函数名可以作为函数的参数,函数名可以作为函数的返回值
7.1迭代器
迭代:更新迭代,重复的,循环的一个过程,更新迭代每次都有新的内容
对象?python中一切皆对象。一个实实在在存在的值。
可迭代对象的解释:字面意义为可以进行循环更新的一个实实在在的值,专业角度上为iter方法的对象即可迭代对象
#获取一个对象的所有方法
s1 = 'apple'
print(dir(s1))
print('__iter__' in dir(s1))
优点:1.存储的数据能直接显示,比较直观 2.拥有的方法比较多,操作方便
缺点:1.占用内存 2.不能直接通过for循环,不能直接取值(key 索引)
迭代器的定义:
- 字面意思是指可更新迭代的工具
- 专业角度上是哪部含有'iter'和'next'方法的对象,就是迭代器
l1 = [1, 2, 3, 4, 5, 6, 7]
obj = iter(l1) #l1.__iter__()
print(next(obj)) #1 l1.__next__()
print(next(obj)) #2
print(next(obj)) #3
for i in range(3):
print(next(obj)) #4 #5 #6
#只要程序没结束或者还没迭代完,就会沿着之前的继续迭代
优点:1.节省内存 2.惰性机制,next一次,只取一个值
缺点:1.速度慢 2.不走回头路
可迭代对象和迭代器的对比
- 可迭代对象是一个操作方法比较多,比较直观,存储数据相对少的数据集
- 当你侧重对于数据灵活处理,并且内存空间足够,将数据集设置为可迭代对象是明确的选择
- 当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,选择将数据设置为迭代器是一个不错的选择
#利用while模拟for循环对可迭代对象进行取值的机制
#将可迭代对象转换成迭代器
li = [1, 2, 3, 4, 5, 6, 7, 8]
obj = iter(li)
while 1:
try:
print(next(obj))
except StopIteration:
break
7.2生成器
python社区,生成器与迭代器看成是一种,生成器的本质就是迭代器,
唯一的区别是:生成器是为自己用python代码构建的数据结构。迭代器都是提供的,或者转换得来的。
获取生成器的三种方式:
- 生成器函数
- 生成器表达式
- python内部提供的一些
#生成器函数
def func():
print(666)
yield 888
#等到第二个next才会执行
yield 999
ret = func()
print(ret) #返回的是生成器对象
print(next(ret)) #888
#一个next对应一个yield
#next使用的次数多于yield会爆出stopIteration
def func():
li = [1, 2, 3, 4]
yield from li
#yield from将这个列表变成迭代器
#提高内层循环的效率
ret = func()
for i in range(4):
print(next(ret)) #每次返回的是列表中的元素,不是列表
return:函数只存在一个return结束函数,并且给函数的执行者返回值
yield:只要函数中有yield,它就是生成器函数,生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next
7.3列表推导式和生成器表达式
列表推导式补充
l1 = ['apple', 'pen', 'banana']
print([ i for i in l1 if len(i) > 3]) #选出长度大于三的字符串
#将含有列表中含有a的字符留下
res = [i for i in li for ch in i if ch.count('a') > 0]
#拓展字典推导式
l2 = [1, 2, 3]
adic = [l1[i]:l2[i] for i in range(len(l1))] #以键值对的形式
生成器表达式与列表推导式几乎一样
也有筛选模式,循环模式,多层循环构建,写法上只有一个不同
obj = (i for i in range(0, 100))
for i in obj:
print(i) #打印从0-99
总结
列表推导式:只能构建比较有规律且复杂的列表,超过三层循环才能构建成功的,不建议使用,查找错误不行,优点就是简单,装逼
#构建一个列表: [2, 3, 4, 5, 6, 7, 8, 'j', 'q', 'l']
li = [i for i in range(2,10)] + list('jql')
列表推导式与生成器表达式区别:相当于可迭代对象和迭代器的区别,写法上有区别
7.4内置函数
7.4.1 eval
去除字符串的外衣并进行计算,有返回值
s1 = '1 + 3'
print(eval(s1)) #4
s = '{"apple":666}'
print(eval(s)) #{'apple':666}
最好不要使用
网络传输的str input输入的时候,sql注入的时候不能使用
exec与eval几乎一样,但是exec处理代码流
msg = """
for i in range(10):
print(i)
"""
exec(msg) #打印从0-9
7,4.2 ord和chr
Ord:输入字符找到该字符编码的位置
Chr:输入数字找出其对应的字符
如果字符是ascii,则返回的位置是ascii
如果超出ascii的范围,使用的是unicode编码
补充:
repr:返回一个对象的string形式(原形毕露)
#可以召回打印的引号,或者格式化输出之前的字符
s1 = 'apple'
print(repr(s1)) # 'apple'
msg = 'www%s' %(s1)
print(msg) # wwwapple
msg = 'www%r' %(s1)
print(msg) # www'apple'
7.4.3 zip
l1 = [1, 2, 3]
tu1 = ('a', 'b', 'c')
s1 = 'jkl'
obj = zip(l1, tu1, s1)
print(list(obj)) #[(1, 'a', 'j'), (2, 'b', 'k'), (3, 'c', 'l')]
7.4.4 max min sorted
l1 = [33, 1, 5, 8, 10, -6, -2]
def abss(a):
return abs(a)
print(min(a, key = abss))
#凡是可以加key的,他会自动将可迭代对象中的每一个元素按照顺序传入key对应的函数中,并以返回值比较大小
#求出最小的键值对
dic = {'a':3, 'b':2, 'c':1}
print(min(dic)) # a
#min会默认按照字典的键去比较大小
def func(a):
return dic[a]
print(min(dic, key = func))
l2 = [('apple', 88), ('pen', 66), ('banana', 55)]
print(sorted(l2, key = lambda x: x[1])) #按照元组中的数字进行排序 默认从低到高
print(sorted(l2, key = lambda x: x[1], reverse = True)) #从高到低
7.4.5 filter map
#列表的推导式的筛选模式
l1 = [1, 3, 5, 7, 9]
ret = filter(lambda x: x > 3, l1)
print(ret) # <filter object at ....> 返回的是一个迭代器
print(list(ret)) # [5, 7, 9]
#map 列表推导式的循环模式
l1 = [1, 4, 9, 16, 25]
print([i ** 2 for i in range(1, 6)])
ret = map(lambda x: x ** 2, range(1,6))
print(ret) # <map object at ...>
print(list(ret)) # [1, 4, 9, 16, 25]
#输出是什么?
list(map(str, [1, 2, 3, 4, 5, 6, 7])) #['1', '2', '3', ...]
7.4.6补充
callable:函数用于检查一个对象是否是可调用的,如果返回true仍可能调用失败,但是返回false一定不会成功
complex:创建一个复数
bin:将十进制转换成二进制并返回
oct:将二进制转换成八进制并返回
hex:将十进制转换成十六进制并返回
round:保留小数的位数
pow:求x的多少次幂,第三个参数是求幂的结果对第三个参数取余
reduce:
from functools import reduce
def func(x, y):
return x + y
l = reduce(func, [1, 2, 3, 4])
print(l)
#第一次 取到前面两个数x = 1 y = 2 x + y = 3 记录下来
#第二次 取到y = 3 x + y = 6 记录下来
#第三次 取到y = 4 x + y = 10记录下来 返回给l
7.5匿名函数
#一句话:比较简单的函数
def func(a, b):
return a + b
func1 = lambda a, b: a + b #一般只用位置参数即可
print(func1(1, 2))
7.6闭包
判断是否是闭包
- 闭包只能存在嵌套 函数中
- 内层函数对外层函数非全局变量的引用,就会成为闭包
闭包的定义:内层函数对外层函数非全局变量的引用,就会称为闭包
被引用的非全局遍历那个也称为自由变量,这个自由变量会与内层函数产生一个绑定关系
自由变量不会在内存中消失
闭包的作用:保证数据安全
def make_averager():
li = []
def averager(new_value):
li.append(new_value)
total = sum(l1)
return total / len(l1)
avg = make_averager()
print(avg(100))
print(avg(150))
#写一个函数完成三次登陆功能:
#用户的用户名密码从一个文件register中取出
#register文件包含多个用户名,密码,用户名密码通过|隔开,每一个人的用户名密码占用文件中的一行
#完成三次验证,三次验证不成功则登陆失败,登陆失败返回false
#登陆成功返回true
def login():
count = 0
while count < 3:
usr = input("username:").strip()
pwd = input("password:").strip()
with open('register.txt', mode = 'r', encoding = 'utf-8') as f:
for line in f:
user_name = line.strip().split('|')[0]
password = line.strip().split('|')[1]
if user_name == usr and password == pwd:
return True
return False
count += 1
def get_user_pwd():
user_dict = {}
with open('register', encoding = 'utf-8') as f:
for line in f:
line_list = line.strip.split('|')
user_dict[line_list[0].strip()] = line_list[1].strip(0)
return user_dict
7.7装饰器
器:工具
开放封闭原则:
开放:对代码的扩展是开放的
封闭:对源码的修改是封闭的
装饰器:在不改变原函数和代码以及调用的前提,为其增加新的功能
#不改变原函数的调用方式
import time
def index():
"""
...
"""
time.sleep(2) #模拟网络延迟或者 代码效率
print('欢迎登陆')
def timmer(f): #f = index
def inner():
strat_time = time.time()
f() #执行index()
end_time = time.time()
print(f'测试本函数的执行效率为{start_time - end_time}')
return inner
index = timmer(index) #index其实保存的是inner的地址
index()
#python中做了一个优化:提出一个语法糖的概念 (最标准的装饰器)
#装饰器要放在所有函数的上面
def timmer(f):
def inner():
#...
f()
#...
return inner
@timmer # index = timmer(index)
def index():
pass
#加上装饰器不应该改变原函数的返回值
#加上装饰器同时需要传递参数
def timmer(f):
def inner(*args, **kwargs):
#...
r = f(*args, **kwargs) #函数的定义:*聚合 args = ('xxx', 'xx') 函数的执行: *打散 :f(*args) -> f('xxx', 'xx')
#...
return r
return inner
@timmer # index = timmer(index)
def index():
return 666
ret = index()
print(ret) #666
#666应该返回给装饰器
#但是ret接收的是inner函数的返回值,而666返回给的是装饰器里面的
#f()也就是r,我们现在要解决的问题就是将r给inner的返回值,这样借助inner返回
#装饰器标准模版
def wrapper(func):
def inner(*args, **kwargs):
"""
此处可添加额外的功能,执行被装饰函数之后的功能
"""
ret = func(*args, **kwargs)
return ret
return inner
@wrapper #login = wrapper(login)
def login():
print('login')
应用 日志写入时间和文件访问
import time
def wrapper(f):
def inner(*args, **kwargs):
struct_time = time.localtime()
with open('log', mode = 'a', encoding = 'utf-8') as file:
file.write(f'北京时间:{time.strftime(%Y-%m-%d %H:%M:%S, struct_time)}执行了{f.__name__}函数')
ret = f(*args, **kwargs)
return ret
return inner
@wrapper
def func():
print('func')

浙公网安备 33010602011771号