函数进阶四(装饰器、内置函数)
一. 昨日内容回顾
生成器,本质就是迭代器,自己用Python代码构建的迭代器
1.生成器函数(yield)
2.生成器表达式(i for i in range(21))
生成器对象:gentor
next(gentor)
send(gentor)
for 循环
yield 与 return 的区别
send:
给上一个yield发送一个值
send 对应yield取值
next:
对应一个yield取值
yield from——将一个可迭代对象变成一个迭代器返回
列表推导式
循环模式:[变量(加工后的变量) for 变量 in iterable]
筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
三元模式:["*" if a > b else 变量 for 变量 in iterable]
优点:
一行代码搞定
缺点:不能使用debug模式查错
生成器表达式:与列表推导式几乎一样,只需将[]改为()即可
字典推导式、集合推导式
二. 装饰器
# 装饰器 # 假设现在要写一个程序来测试某段代码的运行效率 import time # 这是要测试的代码段,一个函数 def func(): print("测试这个函数的执行效率...") # 因为该函数几乎没什么代码,所以加个延时 time.sleep(0.2) func() # 现在开始先测试程序 def timmer(): start_time = time.time() func() # 注意是函数调用开始计时 end_time = time.time() print("该函数的执行效率%s" % (end_time - start_time)) timmer() # 第一版完成 def timmer(f): start_time = time.time() f() # 注意是函数调用开始计时 end_time = time.time() print("该函数的执行效率%s" % (end_time-start_time)) timmer(func) # 上面这样只能测试一个人的,如果要测试三个人的,又得这样: def func1(): print("测试这个函数的执行效率...") # 因为该函数几乎没什么代码,所以加个延时 time.sleep(0.2) def func2(): print("测试这个函数的执行效率...") # 因为该函数几乎没什么代码,所以加个延时 time.sleep(0.2) def func3(): print("测试这个函数的执行效率...") # 因为该函数几乎没什么代码,所以加个延时 time.sleep(0.2) timmer(func1)timmer(func2)timmer(func3) # 上一个版本中,假如有500个函数的项目,那么项目中可能出现5000次调用 # 如果想要不影响项目正常运作的情况下测试每个函数的执行效率,意味着将项目中5000次这样的调用方式func()变成timmer(func)
# 这样明显不行,要尽量不改变原函数的调用方式,并且能对原函数进行效率测试 # 即原函数既要正常运行,又能测试它的时间 import time def func(): print("测试这个函数的执行效率...") time.sleep(0.2) def timmer(f): start_time = time.time() f() end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) func() f1 = func # 这里表示 f1 和 func 在内存中指向同一个内存地址 func = timmer # 这里开始,func 指向函数 timmer 在内存中的地址,但是 f1 还是指向原先 func 函数的内存地址 func(f1) # 因此相当于timmer(func),没有返回值,相当于做了一个伪装 # 上一个版本虽然做了伪装,执行原函数的调用方式不变,并且测试了执行效率 # 但是这里还有个问题,如果func()里面有参数的话,这样就伪装不彻底了 # 而且 func(f1) 上面还有两行代码,如果要测试500个函数就要多写至少1000行代码
# 装饰器的雏形 import time def func(): print("测试这个函数的执行效率...") time.sleep(0.2) def timmer(f): def inner(): start_time = time.time() f() end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) return inner func = timmer(func) # inner # # 相当于 f = (func函数),但是不执行 inner 函数,因为没有调用它(inner()),只是返回了 inner 这个函数名 # # 也就是(return innner),所以这里相当于 inner 这个函数名赋值给一个变量 func # func() # inner() # 这里就是在调用 inner(),因为 inner() 中又有 f(), 因此还会调用最上面的 func() 函数 # 上面的timmer就是装饰器 # 但是这里还是多了一行代码,比如有另一个 func1(), 那么还得再加一行:func1 = timmer(func1) # Python解释器提供了一个语法糖 @ 的概念 import time def timmer(f): def inner(): start_time = time.time() f() end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) return inner # # 前提是先定义一个 timmer 函数 @timmer # 相当于 func = timmer(func) ,要记住这里的 timmer(func) 是 inner 函数 def func(): print("测试这个函数的执行效率...") time.sleep(0.2) func() @timmer def func1(): print(666) time.sleep(0.2) func1()
# 这还有个问题,被装饰的函数 func、func1 如果有参数,这样写就满足不了了 # 比如下面这样 import time def timmer(f): def inner(): start_time = time.time() f() end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) return inner @timmer def func(x): print("测试一个参数:%s" %x) time.sleep(0.2) func("abc") # 会报错 # 因为这里的 func("abc") 相当于 inner("abc"),而 inner() 是没有参数的 # 因此要在 inner()中传入一个参数 import time def timmer(f): def inner(a): start_time = time.time() f(a) end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) return inner @timmer def func(x): print("测试一个参数:%s" %x) time.sleep(0.2) func("abc") # 这个功能还是不完善,如果有两个参数以上,inner() 及 inner 函数里的 f() 里面就得加入两个参数 # 这时就要想到之前学过的动态参数(万能参数)
import time def timmer(f): def inner(*args, **kwargs): # 函数的定义,* 代表聚合 start_time = time.time() f(*args, **kwargs) # 比如这里 func1 中的两个万能参数为什么可以平均合理分配给 func 函数中 x, y, z 三个参数? # 因为 f(*args, **kwargs)是调用函数,而括号中的 *args **kwargs 就是打散 # 所以这里的 f(*args, **kwargs) 相当于 f("abc", "jjk", "hkas") end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) return inner @timmer def func(x): print("测试一个参数:%s" %x) time.sleep(0.2) func("abc") @timmer def func1(x, y, z="alex"): print("测试两个参数:%s" % x, y) print("测试一个参数:%s" % z) time.sleep(0.2) func("abc", "jjk", "hkas") # 这里还差一点,被装饰函数缺少返回值
# 函数有返回值 return 时 import time def timmer(f): def inner(*args, **kwargs): start_time = time.time() f(*args, **kwargs) end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) return inner @timmer def func(x): print("测试一个参数:%s" %x) time.sleep(0.2) return 666 func("abc") # 运行发现没有返回 666,因为 inner() 中没有返回值 import time def timmer(f): def inner(*args, **kwargs): start_time = time.time() ret = f(*args, **kwargs) # 注意这里 end_time = time.time() print("该函数的执行效率是%s" % (end_time - start_time)) return ret # 借助 inner() 函数设置一个返回值 return inner @timmer def func(x): print("测试一个参数:%s" %x) time.sleep(0.2) return 666 s = func("abc") print(s) # 这里发现去掉 @timmer 也一样能打印出 666
# 标准版装饰器写法 def wrapper(f): def inner(*args, **kwargs): """执行被装饰函数之前的操作""" ret = f(*args, **kwargs) """执行被装饰函数之后的操作""" return ret return inner # 装饰器: # 1. 它的本质是闭包,而且它就是个函数 # 2. 在不改变原函数调用方式上,给原函数增加了一些额外的功能,比如登录验证,写日志,执行效率等 # 开放封闭原则: # 对代码拓展开放——任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。 # 所以我们必须允许代码扩展、添加新功能 # 对代码修改封闭——因为写的函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改, # 很有可能影响其他已经在使用该函数的用户。 # 而装饰器完美的遵循了这个开放封闭原则。 # 比如上一段代码中的 func() 函数如果还要加一个功能 # 不要在这个被装饰函数里改,只需在装饰器 timmer 里的 inner 函数添加即可
三. 内置函数
"""
print() input() len() list() str() max() min()
dir() isinstance() globals() locals() type()
int() id() dict() bool() next() range() set()
open() tuple() hash() iter() enumerate()
"""
作用域相关
*** locals()
*** globals()
# 字符串类型代码的执行 eval, exec, compile 慎用 # eval -- 执行字符串类型的代码,并返回最终结果 # 只能用于一些写死的东西,如果是用户输入, input之类的,就不要用,反之别人入侵 s1 = "1 + 2 + 3" n = 55 print(s1) # 1 + 2 + 3 print(eval(s1)) # 6 print(eval("n + 16")) # 71 s3 = '{"name": "alex"}' print(s3) # {"name": "alex"} 注意这是字符串 print(eval(s3)) # {'name': 'alex'} 注意这是字典 # exec # 执行字符串类型的代码 s4 = ''' for i in range(5): print(i) ''' print(s4) # for i in range(5): # print(i) print(exec(s4)) # 0 # 1 # 2 # 3 # 4 # None
# 设置打印的分隔符 print(1, 2) print(1, 2, sep="|") # 1 2 # 1|2 print(1, 2, 3) print(1, 2, 3) # 1 2 3 # 1 2 3 # 设置结束时的格式 print(1, 2, 3, end="") print(1, 2, 3) # 1 2 31 2 3 f = open("log", "w", encoding="utf-8") print("写入文件", file=f)
# 内存相关的内置函数 hash id # hash: 获取一个对象(可哈希对象: int str bool tuple)的哈希值 # id: 用于获取对象的内存地址 print(hash("asda")) # -9061060272216898141 print(hash("我")) # 8166043114470169461 print(hash("奥斯卡")) # 3330570073468651602 # 唯独数字的哈希还是数字(应该只适用于整数,浮点数不一样) print(hash(10)) # 10 print(hash(0.1)) # 230584300921369408 # 文件操作相关 # open:函数用于打开一个文件,创建一个 file 对象,相关的方法才可以调用它进行读写 # 模块相关 __import__ # __import__:函数用于动态加载类和函数 # 帮助 # help:函数用于查看函数或模块用途的详细说明 # 调用相关 # callable:函数用于检查一个对象是否是可调用的。如果返回True,object仍然可能调用失败;但如果返回False,调用对象ojbect绝对不会成功 callable(0) # False callable("runoob") # False def add(a, b): ... return a + b callable(add) # 函数返回 True # True class A: ... def method(self): ... return 0 callable(A) # 类返回 True a = A() callable(a) # 没有实现 __call__, 返回 False class B: ... def __call__(self): ... return 0 callable(B) # True b = B() callable(b) # 实现 __call__, 返回 True
# 查看内置属性 # # dir: # 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表 # 带参数时,返回参数的属性、方法列表。 # 如果参数包含方法__dir__(),该方法将被调用。 # 如果参数不包含__dir__(),该方法将最大限度地收集参数信息 # int:函数用于将一个字符串或数字转换为整型 print(int()) # 0 print(int(3.6)) # 3 print(int('0100',base=2)) # 将2进制的 0100 转化成十进制。结果为 4 # float:函数用于将整数和字符串转换成浮点数 print(float("3.15")) # 3.15 print(float(11)) # 11.0 # 进制转换: # bin:将十进制转换成二进制并返回 # oct:将十进制转化成八进制字符串并返回 # hex:将十进制转化成十六进制字符串并返回 print(bin(10),type(bin(10))) # 0b1010 <class 'str'> print(oct(10),type(oct(10))) # 0o12 <class 'str'> print(hex(10),type(hex(10))) # 0xa <class 'str'> # 数学运算: # # abs:函数返回数字的绝对值 print(abs(-5)) # divmod:计算除数与被除数的结果,返回一个包含商和余数的元组(a // b, a % b) # 页面分类会用到 print(divmod(100, 9)) # (11, 1) # round:保留浮点数的小数位数,默认保留整数。 print(round(3.14)) # 3 print(round(3.1415926, 3)) # 3.142 # pow:求x**y次幂。(三个参数为x**y的结果对z取余) print(pow(3, 4)) # 81 print(pow(3, 4, 4)) # 1 # sum:对可迭代对象进行求和计算(可设置初始值) # s = [i for i in range(20)] print(sum(s)) # 190 print(sum(s, 10)) # 200
# min:返回可迭代对象的最小值(可加key,key为函数名,通过函数的规则,返回最小值) s = [3, 2, 7, 9, -10, -1] print(min(s, key=abs)) # 元素一个个放到 abs 中,以最小的绝对值返回 # -1 dic = {'a': 3, 'b': 2, 'c': 1} print(min(dic)) # 默认以键的ASCII码的大小来排 # a dic = {'a': 3, 'b': 2, 'c': 1} def func(x): return dic[x] print(min(dic, key=func)) print(min(dic, key=lambda x: dic[x])) # 这一步相当于上面的三步 print(dic[min(dic, key=lambda x: dic[x])]) # 将 dic 中的每一个键传入函数中以函数的返回值(即字典的值)比较得出最小值 # 第一次传入 "a", 返回它对应的值 3, 以此类推 l1 = [("a", 3), ("b", 2), ("c", 1)] print(min(l1)) # ("a", 3) 按照元组内的第一个元素的ASCII来排序 print(min(l1, key=lambda x: x[1])) # x 是一个元组,要想到这个是重点 # max:返回可迭代对象的最大值(可加key,key为函数名,通过函数的规则,返回最大值) # 跟 min 用法一样 # 列表和元祖 # list:将一个可迭代对象转化成列表(如果是字典,默认将key作为列表的元素) print(list({"a": 1, "b": "asd"})) # ['a', 'b'] # tuple:将一个可迭代对象转化成元祖(如果是字典,默认将key作为元祖的元素) print(tuple({"a": 1, "b": "asd"}))
# reversed:将一个序列翻转,并返回此翻转序列的迭代器 # 记住返回的是迭代器 # 补上 # slice:构造一个切片对象,用于列表的切片 li = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] l1 = ['d', 'e', 'f', 'g'] sli_obj = slice(3) print(li[sli_obj]) # ['a', 'b', 'c'] print(l1[sli_obj]) # ['d', 'e', 'f'] sli_obj = slice(100) print(li[sli_obj]) # ['a', 'b', 'c', 'd', 'e', 'f', 'g'] print(l1[sli_obj]) # ['d', 'e', 'f', 'g'] # format:与具体数据相关,用于计算各种小数,精算等 print(format('test', '<20')) print(format('test', '>20')) print(format('test', '^20')) # test # test # test
# bytes # 编解码的第一种方式 s1 = "alex" b1 = s1.encode("utf-8") s2 = b1.decode("utf-8") print(b1, s2) # b'alex' alex # 编解码的第二种方式 s1 = "alex" b1 = bytes(s1, encoding="utf-8") # 编码 unicode ---> utf-8 s2 = str(b1, encoding="utf-8") # 解码 utf-8 ---> unicode print(b1) # b'alex' print(s2) # alex
# ord 输入字符找该字符编码的位置 unicode print(ord('a')) # 97 print(ord('中')) # 20013 # chr:输入位置数字找出其对应的字符 print(chr(97)) # a print(chr(20013)) # 中 # ascii:是ascii码中的返回该值,不是就返回/u... print(ascii('a')) # 'a' print(ascii('中')) # '\u4e2d'
# repr:返回一个对象的string形式(原形毕露) s1 = "太白" print(s1) # 太白 print(repr(s1)) # '太白' msg = "我是%s" % "李白" print(msg) # 我是李白 print(repr(msg)) # '我是李白' # all:可迭代对象中,全都是True才是True l1 = [1, 2, "alex", ""] print(all(l1)) # False # any:可迭代对象中,有一个True 就是True l2 = [1, {}, False, ""] print(any(l2)) # True
# min max 可以加 key # sorted: 对所有可迭代的对象进行排序操作,形成一个新的列表 l1 = [1, 6, 5, 3, 10] l2 = sorted(l1) print(l2) # [1, 3, 5, 6, 10] l1 = [("阿亮", 25), ("二师兄", 21), ("相师", 73), ('三包春药', 30)] print(sorted(l1)) # [('三包春药', 30), ('二师兄', 21), ('相师', 73), ('阿亮', 25)] print(min(l1, key=lambda x: x[1])) # ('二师兄', 21) l1 = [("a阿亮", 25), ("c二师兄", 21), ("e相师", 73), ('d三包春药', 30)] print(sorted(l1)) # [('a阿亮', 25), ('c二师兄', 21), ('d三包春药', 30), ('e相师', 73)]
# zip:函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组 # 然后返回由这些元组组成的列表。 # 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同 # 拉链方法 l1 = [1, 2, 3, ] l2 = ['a', 'b', 'c', 5] l3 = ('*', '**', (1, 2, 3)) iter1 = zip(l1, l2, l3) print(iter1) # <zip object at 0x000001D114D240C8> for i in iter1: print(i) # (1, 'a', '*') # (2, 'b', '**') # (3, 'c', (1, 2, 3))
# filter: 过滤,类似于列表推导式的筛选模式 # filter 返回的也是一个迭代器 l1 = [2, 4, 5, 6, 7, 8, 10] iter2 = filter(lambda x: x % 2 == 0, l1) print(iter2) # <filter object at 0x0000025498638320> for i in iter2: print(i) # 2 # 4 # 6 # 8 # 10
# map: 返回一个迭代器,类似于列表推导式的循环模式 l1 = [1, 2, 3, 4, 5] iter3 = map(lambda x: x**2, l1) print(list(iter3)) # [1, 4, 9, 16, 25] # reduce (只能加减乘除) from functools import reduce l1 = [1, 2, 3, 4, 5] iter4 = reduce(lambda x,y: x + y, l1) print(iter4) # 15 # 一开始传进去1和2,相加为3,把3存起来, # 再把列表中元素3传进来,与这个3相加,得到6 # 又把6存起来,以此类推 from functools import reduce l1 = ["alex", "wusir", "taibai"] iter5 = reduce(lambda x,y: x + y, l1) print(iter5) # alexwusirtaibai from functools import reduce l1 = [i for i in range(1, 11)] iter6 = reduce(lambda x,y,z: x * y + z, l1) print(iter6) # 报错,位置参数只能是两个参数
# 装饰器进阶 # 两个以上的装饰器装饰一个函数时的运行顺序 def set_fun1(func1): print("set_func1") def call_fun1(): print("call_func1") func1() return call_fun1 def set_fun2(func2): print("set_func2") def call_func2(): print("call_func2") func2() return call_func2 @set_fun1 @set_fun2 def test(): print("test") test() # 运行结果: # set_func2 # set_func1 # call_func1 # call_func2 # test # 首先,装饰器set_fun2在最下面,先运行它 # 先执行 set_fun2 函数中 set_fun2() 与 call_fun2() 之间的内容,即 "set_func2"(最外层函数) # 然后,再跳到 set_fun1 函数中,执行 set_fun1 与 call_fun1 之间的内容,即 "set_fun1"(最外层函数) # 接着,运行 call_fun1与 func1 之间的内容,即 "call_fun1" # 再次跳到 装饰器 set_fun2中,运行 call_fun2与 func2 之间的内容,即 "call_fun2" # 最后运行 f() 函数,即 "test" # # 注意:函数与装饰器之间调用到引用先由内至外,再由外到内调用。先 def dest>@set_fun2>@set_fun1,然后逆方向回调引用,执行@set fun1>@set_fun2>def test # 当多个装饰器装饰一个函数时,执行时的顺序是: # 最先装饰的装饰器,最后一个执行。 # 它遵循了先进后出这样一个规则 # 可以类比圣诞树的装饰品: # 最先贴上去的饰品总是在最底层, # 而如果从树上往下取饰品的话(即程序里的执行) # 则需要从最后贴上的饰品开始一层一层地往下取。