#9生活小妙招:python基础知识补充(详解装饰器及可能你没注意到的一些知识点)

#装饰器就是一个帽子,一个背包,有了他你就有了新功能,你的代码运行之前会先在装饰器里走一圈;

先大致了解一下相关概念:

'''
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:跳出并结束当前整个循环,执行循环后的语句;可以与whilefor搭配使用;
continue:结束当次循环,继续执行后续剩余循环;
pass 该语句没有实际的作用,一个空语句,为了结构完整性添加;

''

有时候你觉得不重要的知识,不是因为不重要,而是你现在的项目还不需要用到;还有就是知识不要一知半解,容易丢脸的;不断提升自己吧,安慰自己一下,只要坚持,终会遇见。
持续更新,,,

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值