#装饰器就是一个帽子,一个背包,有了他你就有了新功能,你的代码运行之前会先在装饰器里走一圈;
先大致了解一下相关概念:
'''
python装饰器的本质是函数闭包代码更规范的一种表现形式;装饰器的作用就是为已经存在的函数或对象添加额外的功能。
什么是闭包函数:函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包(或者这么理解:一个函数,其参数和返回值都是函数:用于增强函数的功能,面向切面编程(AOP))。
语法糖:指的是计算机语言中添加某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用;
重点:装饰器在第一次调用被装饰函数时进行增强;
增强时机:在第一次调用之前;
增强次数:只增强一次;
'''
1.什么叫做装饰器(性质先看一下,不懂关系不大,实例看完后再回头瞅瞅就会有感觉了):python内置的装饰器
我的理解就是一些通用函数功能的复用,例如:我们需要计算函数运行时间,每次都去调用时间计算函数太笨重了,我们加入计算时间的装饰器后,运行我们主函数后就会自动计算时间了,增加了代码的健壮也增加了代码的美观;
装饰器本质上就是一个python函数,它可以让其它函数在不需要做任何代码改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景中,比如-- >插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同的代码并且可以重复使用。
重点:
装饰器在第一次调用被装饰函数时进行增强;
增强时机:在第一次调用之前;
增强次数:只增强一次;
2.跟着实例继续理解:实例讲解
a.闭包函数(一个函数内部嵌套一个函数,返回值也是一个函数):count_time_wrapper2函数就是一个闭包函数,可以将print_odds2函数功能进行加强,多了一个时间计算的功能:
#b.闭包函数:做上面的事情(传入函数,返回函数):
'''
通过闭包增强,功能函数print_odds,给他增加一个统计时间的功能;
'''
from time import process_time #返回进程运行时间
from time import perf_counter #返回系统运行时间
#功能函数:
def print_odds2():
'''
输出0-100之间所有奇数,并统计函数执行时间;
'''
for i in range(100):
if i%2 == 1:
print(i)
#闭包增强函数:
def count_time_wrapper2(func):
'''
闭包,用于增强函数func:给函数func增加时间统计功能;
闭包函数的返回值是对传入的函数增强后的结果
传入的是函数,返回的也是函数;
'''
def improved_func2():
start_time = perf_counter() #起始时间
func()
end_time = perf_counter()
print("it takes {}s to find all the olds".format(end_time-start_time))
return improved_func2()
if __name__ == "__main__":
# print_odds2()
print_odds2 = count_time_wrapper2(print_odds2)
b.装饰器实现上面函数相同的功能:写法:@装饰函数
#c.装饰器:装饰器实现相同功能(无传参):
'''
通过装饰器进行函数增强,只是一种语法糖,本质上跟上个程序完全一致;
'''
from time import process_time #返回进程运行时间
from time import perf_counter #返回系统运行时间
#装饰器函数:
def count_time_wrapper3(func):
'''
闭包,用于增强函数func:给函数func增加时间统计功能;
闭包函数的返回值是对传入的函数增强后的结果
传入的是函数,返回的也是函数;
装饰器在第一次调用被装饰函数时候进行增强(增强的意思就是加入了新的功能);
增强次数:只增强一次;
'''
def improved_func3():
start_time = perf_counter() #起始时间
func()
end_time = perf_counter()
print("it takes {}s to find all the olds".format(end_time-start_time))
return improved_func3()
@count_time_wrapper3
def print_odds3(): #功能函数
'''
输出0-100之间所有奇数,并统计函数执行时间;
'''
for i in range(100):
if i%2 == 1:
print(i)
if __name__ == "__main__":
print_odds3() #连辅助函数名字都不用写了,直接就是调用功能函数加强
c.装饰器实现上面函数相同的功能:写法:@装饰函数(传参)
#d.装饰器:装饰器实现相同功能(原函数参数可变,传参):
'''
通过装饰器进行函数增强(增强函数里面传参);
'''
from time import process_time #返回进程运行时间
from time import perf_counter #返回系统运行时间
def count_time_wrapper4(func):
'''
增强函数的返回值就是原函数的返回值;
增强函数应该把收到的所有参数传给原函数;
'''
def improved_func4(*args,**kwargs):
start_time = perf_counter() #起始时间
result = func(*args,**kwargs)
end_time = perf_counter()
print("it takes {}s to find all the olds".format(end_time-start_time))
return result
return improved_func4
@count_time_wrapper4
def print_odds4(lim):
'''
输出0-100之间所有奇数,并统计函数执行时间;
'''
result = 0
for i in range(lim):
if i%2 == 1:
result +=1
return result
if __name__ == "__main__":
print_odds4(lim=1000000)
d.两层装饰器如何运行:
#e.装饰器:多个装饰器的闭包运行:
'''
通过装饰器进行函数增强(多个装饰器装饰);
'''
from time import process_time #返回进程运行时间
from time import perf_counter #返回系统运行时间
import time
def count_time_wrapper5(func):
'''
增强函数的返回值就是原函数的返回值;
增强函数应该把收到的所有参数传给原函数;
'''
print("run count_time_wrapper5")
def improved_func5():
start_time = perf_counter() #起始时间
func()
end_time = perf_counter()
print("it takes {}s to find all the olds".format(end_time-start_time))
return improved_func5
def log_wrapper5(func):
print("run log_wrapper5")
def improve_func():
start_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
func()
end_time = time.strftime("%Y-%m-%d %H:%M:S",time.localtime(time.time()))
print("Logging:func{} runs from{}to{}".format(func.__name__,start_time,end_time))
return improve_func
@count_time_wrapper5
@log_wrapper5
def print_odds5():
'''
输出0-100之间所有奇数,并统计函数执行时间;
'''
result = 0
for i in range(100):
if i%2 == 1:
result +=1
return result
if __name__ == "__main__":
print_odds5()
#等价于闭包函数:
# a = count_time_wrapper5(log_wrapper5(print_odds5))
# a()
看明白怎么运行的了吧:先执行@log_wrapper5,在执行@count_time_wrapper5;无论多少层都是这样的;不懂得时候自己试试;
用闭包函数更明显的展示一下:
#e.装饰器:多个装饰器的闭包运行:
'''
通过装饰器进行函数增强(多个装饰器装饰);
'''
from time import process_time #返回进程运行时间
from time import perf_counter #返回系统运行时间
import time
def count_time_wrapper5(func):
'''
增强函数的返回值就是原函数的返回值;
增强函数应该把收到的所有参数传给原函数;
'''
print("run count_time_wrapper5")
def improved_func5():
start_time = perf_counter() #起始时间
func()
end_time = perf_counter()
print("it takes {}s to find all the olds".format(end_time-start_time))
return improved_func5
def log_wrapper5(func):
print("run log_wrapper5")
def improve_func():
start_time = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
func()
end_time = time.strftime("%Y-%m-%d %H:%M:S",time.localtime(time.time()))
print("Logging:func{} runs from{}to{}".format(func.__name__,start_time,end_time))
return improve_func
# @count_time_wrapper5
# @log_wrapper5
def print_odds5():
'''
输出0-100之间所有奇数,并统计函数执行时间;
'''
result = 0
for i in range(100):
if i%2 == 1:
result +=1
return result
if __name__ == "__main__":
# print_odds5()
#等价于闭包函数:
a = count_time_wrapper5(log_wrapper5(print_odds5))
a()
从图中可以看到,执行加强函数的时候只输出外围打印:
当我们执行增强函数的时候,才会执行内部增强后的功能函数,也就是装饰器的执行等于,闭包函数的增强加上执行:
e.装饰器传参1:
def logging(level):
print("1")
def wrapper(func):
def inner_wrapper(*args, **kwargs):
print("[{level}]: enter function {func}()".format(
level=level,
func=func.__name__))
return func(*args, **kwargs)
return inner_wrapper
return wrapper
@logging(level='INFO')
def say(something):
print("say {}!".format(something))
@logging(level='DEBUG')
def do(something):
print("do {}...".format(something))
'''
# 如果没有使用@语法,等同于
# say = logging(level='INFO')(say)
# say([1,2])
'''
if __name__ == '__main__':
say('hello')
do("my work")
f.装饰器传参2:
#g.如何创建带参数的装饰器:
'''
闭包本身就是一个二层函数,可以通过增加层数来给装饰器附加更多的功能:
'''
from inspect import signature
def check_type(*args, **kwargs):
def out_wrapper(func):
#使用标准库的signature方法,获取函数签名对象;获取函数形参:name, age, height
sig = signature(func)
#OrderedDict是按照有序插入顺序存储的有序字典;
# bind_partial()方法对提供的类型与参数名称进行部分绑定;arguments生成有序插入顺序存储的有序字典;
bind_types = sig.bind_partial(*args, **kwargs).arguments
print(bind_types)
def wrapper(*args, **kwargs):
# Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
# 给执行函数中具体的实参进行和形参进行绑定,arguments.items()生成可遍历的(键, 值) 元组数组
func_type = sig.bind(*args, **kwargs).arguments.items()
print(func_type)
# 循环形参和实参字典的items()形式
for name, obj in func_type:
if name in bind_types:
'''
isinstance() 与 type() 区别:type() 不会认为子类是一种父类类型,不考虑继承关系。
isinstance() 会认为子类是一种父类类型,考虑继承关系。如果要判断两个类型是否相同推荐使用 isinstance()。
'''
if not isinstance(obj, bind_types[name]):
raise TypeError('%s must be %s' % (name, bind_types[name]))
func(*args, **kwargs)
return wrapper
return out_wrapper
# 通过装饰器实现对函数参数进行类型检查
@check_type(str, int, float)
def func(name, age, height):
print(name, age, height)
if __name__ == '__main__':
func('bei_men', 18, 1.75)
g.基于类的传参装饰器:
'''
带参数的类装饰器
如果需要通过类形式实现带参数的装饰器,那么会比前面的例子稍微复杂一点。
那么在构造函数里接受的就不是一个函数,而是传入的参数。通过类把这些参数保存起来。
然后在重载__call__方法是就需要接受一个函数并返回一个函数。
'''
class logging(object):
def __init__(self, level='INFO'):
self.level = level
def __call__(self, func): # 接受函数
def wrapper(*args, **kwargs):
print("[{level}]: enter function {func}()".format(
level=self.level,
func=func.__name__))
func(*args, **kwargs)
return wrapper # 返回函数
@logging(level='INFO')
def say(something):
print("say {}!".format(something))
if __name__ == "__main__":
say("123")
h.装饰器返回值:返回函数名字的错误及正确示范:
#输出函数名字示范正确错误:
import time
def logging(func):
def wrapper(*args, **kwargs):
"""print log before a function."""
print("[DEBUG] {}: enter {}()".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), func.__name__))
return func(*args, **kwargs)
return wrapper
@logging
def say1(something):
"""say something"""
print("say {}!".format(something))
print(say1.__name__ ) # wrapper)
#正确的方式:引用wraps
from functools import wraps
def logging(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""print log before a function."""
print("[DEBUG] {}: enter {}()".format(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), func.__name__))
return func(*args, **kwargs)
return wrapper
@logging
def say2(something):
"""say something"""
print("say {}!".format(something))
print(say2.__name__ ) # say
print(say2.__doc__ )# say something
if __name__ == "__main__":
say1("1")
say2("2")
2.面试可能遇见但是平时你不一定注意的知识:
#2.附送知识点:
#a.
'''
python的可变与不可变对象:可变:列表,字典,集合;
不可变:int float number 数组 字符串
'''
#b.
'''
浅度拷贝:copy() 和 深度拷贝:deepcopy 之间的关系;
浅度拷贝就是原数据和拷贝数据公用一个内存地址,原数据对二层数据修改拷贝数据也跟着改变(拷贝的是引用),对一层数据修改无影响;
(数据半共享(拷贝第一层的数据,具有自己单独的内存地址;嵌套层即第二层或其他层不拷贝,指向原有的内存地址))
深度拷贝是拷贝数据自己开辟一个地址,原数据改变,但是拷贝数据不会跟着改变(拷贝的是对象);
(数据完全不共享(复制其数据完完全全放独立的一个内存,完全拷贝,数据不共享))
'''
import copy
a = [1,2,3,["x","n"]]
b = a
c = copy.copy(a)
d = copy.deepcopy(a)
a.append(4)
a[3].append("c")
print("原始列表:",a)
print("直接赋值列表",b) #就是原始数据的一个别名;
print("浅拷贝列表",c) #对二层索引的值进行性修改会有影响,对一层修改没有影响;
print("深拷贝列表",d) #原始数据的任何操作不会影响到深拷贝列表,完全赋值对象,开辟新空间;
#c:
'''
去重也是python里面会使用的技术:比如:unique();set();
datafrane:对单属性列去重:df.drop_duplicates(["id"])
datafrane:对多属性列去重:df.drop_duplicates(["id","age"])
datafrane:对多属性列去重:df.drop_duplicates(["id","age"],keep = "first")
用unique()就挺好,下面是一些实例;
return_index=True: 返回新列表中的每个元素在原列表中第一次出现的索引值,因此元素个数与新列表中元素个数一样
return_inverse=True:返回原列表中的每个元素在新列表中出现的索引值,因此元素个数与原列表中元素个数一样
'''
# 一、元素为数值型数据
import numpy as np
A = [1, 2, 5, 3, 4, 3]
print("原列表:", A)
print("--------------------------")
# 返回任意的一个参数值
a = np.unique(A)
print("新列表:", a)
print("--------------------------")
# 返回任意的两个参数值
a, s = np.unique(A, return_index=True)
print("新列表:", a)
print("return_index:", s)
print("--------------------------")
# 返回全部三个参数值
a, s, p = np.unique(A, return_index=True, return_inverse=True)
print("新列表:", a)
print("return_index", s)
# 二、元素为字符型数据
A = ['fgfh', 'asd', 'fgfh', 'asdfds', 'wrh']
print("原列表:", A)
print("-------------------------------------------------")
a, s, p = np.unique(A, return_index=True, return_inverse=True)
print("新列表:", a)
print("return_index", s)
print("return_inverse", p)
#三、set进行字符串去重:
str = "asdfgsdecrtdft"
li = ["1",2,3,"3",1,2,"1"]
se1 = set(str)
se2 = set(li)
print(se1,se2)
#d:
'''
python内置模块:time() 时间戳:timetamp,元组:struct_time;random:random.random();random.randint();
os模块:os.listdir();os.mkdir();set内置模块;json模块:json.load();json.dump();
shelve模块:shelve.open("shelve") ;xlm模块:不同语言或程序之间数据交换的协议;
re模块:正则表达式模块;logging模块:日志操作;
'''
#e:全局变量:
'''
除了在函数内部定义变量,Python 还允许在所有函数的外部定义变量,这样的变量称为全局变量(Global Variable)。
和局部变量不同,全局变量的默认作用域是整个程序,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。
对于全局变量的修改,如果全局变量是int或者str,那么如果想要在函数中对函数变量进行修改,则需要先在函数内,声明其为global,再进行修改
'''
#对数值操作:
a = 1
def fun1():
a = 2
print("In fun1 a is:{}".format(a))
def fun2():
global a
a = 2
print("In fun2 a is:{}".format(a))
if __name__ == '__main__':
print("Before fun1 a is:{}".format(a))
fun1()
print("Now a is:{}".format(a))
fun2()
print("After fun2 a is:{}".format(a))
#对列表操作:
a = [1, 2, 3]
def fun1():
a[1] = 1
print("In fun1 a is:{}".format(a))
def fun2():
global a
a[1] = 3
print("In fun2 a is:{}".format(a))
def fun3():
a = [1, 2]
print("In fun1 a is:{}".format(a))
if __name__ == '__main__':
print("Before fun1 a is:{}".format(a))
fun1()
print("Now a is:{}".format(a))
fun2()
print("After fun2 a is:{}".format(a))
fun3()
print("After fun3 a is:{}".format(a))
'''
结论:
想要引用但不修改全局变量,不需要加global标签;
想要修改全局变量时,需要加global标签;
例外:当变量是列表或元组时,若只修改元素的值,不需要加global标签就可直接修改
'''
#f:
'''
break , continue , pass区别;
braek:跳出并结束当前整个循环,执行循环后的语句;可以与while和for搭配使用;
continue:结束当次循环,继续执行后续剩余循环;
pass 该语句没有实际的作用,一个空语句,为了结构完整性添加;
''
有时候你觉得不重要的知识,不是因为不重要,而是你现在的项目还不需要用到;还有就是知识不要一知半解,容易丢脸的;不断提升自己吧,安慰自己一下,只要坚持,终会遇见。
持续更新,,,