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')
posted @ 2021-03-19 21:43  wrrr  阅读(83)  评论(0)    收藏  举报