百万年薪python之路 -- 装饰器

装饰器

1.1 开放封闭原则

开放封闭原则具体定义是这样:

1.对扩展是开放的

我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

2.对修改是封闭的

因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对函数内部进行修改,或者修改了函数的调用方式,很有可能影响其他已经在使用该函数的用户。

定义:在不改变原被装饰的函数的源代码以及调用方式下,为其添加额外的功能。

实现真正的开放封闭原则:装饰器。

运用闭包原理:

import time
def index():
    time.sleep(2)  # 模拟一下网络延迟以及代码的效率
    print('这是一个主页')

def access_index(name):
    time.sleep(3)  # 模拟一下网络延迟以及代码的效率
    print(f'欢迎访问{name}主页')

套一层最外面的函数timer,然后将里面的inner函数名作为最外面的函数的返回值

def timer(func):  # func = index
    def inner():
        start_time = time.time()
        func()
        end_time = time.time()
        print(f'此函数的执行效率为{end_time-start_time}')
    return inner
# f = timer(index)
# f()

我们分析一下代码,代码执行到这一行:f = timer(index) 先执行谁?看见一个等号先要执行等号右边, timer(index) 执行timer函数将index函数名传给了func形参。内层函数inner执行么?不执行,inner函数返回 给f变量。所以我们执行f() 就相当于执行inner闭包函数。 f(),这样既测试效率又执行了原函数,有没有问题?当然有啦!!我们要解决原函数执行方式不改变的问题,怎么做? 所以你可以把 f 换成 index变量就完美了! index = timer(index) 特别要注意 函数外面的index实际是inner函数的内存地址而不是index函数。这个timer就是最简单版本装饰器,在不改变原index函数的源码以及调用方式前提下,为其增加了额外的功能,测试执行效率。

1.2标准版装饰器

def wrapper(func):
    def inner(*args,**kwargs):
        '''执行被装饰函数之前的操作'''
        ret = func(*args,**kwargs)
        '''执行被装饰函数之后的操作'''
        return ret
    return inner
import time
def wrapper(f):
    def inner(*args,**kwargs):
        start = time.time()     #调用函数之前的操作
        f(*args,**kwargs)
        end = time.time()
        print(end - start)  #调用函数之后的操作
    return inner

def func(*args,**kwargs):
    time.sleep(0.5)
    print(f"{args[0]}暴打{args[1]}页面!")

func = wrapper(func)
func("章超印","煞笔周道镕")

语法糖:@xxx

import time
def wrapper(f):
    def inner(*args,**kwargs):
        start = time.time()     #调用函数之前的操作
        f(*args,**kwargs)
        end = time.time()
        print(end - start)  #调用函数之后的操作
    return inner

@wapper     # 等于 func = wapper(func)
def func(*args,**kwargs):
    time.sleep(0.5)
    print(f"{args[0]}暴打{args[1]}页面!")

# func = wapper(func)
func("章超印","煞笔周道镕")

带参数的装饰器:

在装饰器的基础上再套一层

你现在要完成的就是你的装饰器要分情况去判断账号和密码,不同的函数用的账号和密码来源不同。 但是你之前写的装饰器只能接受一个参数就是函数名,所以你写一个可以接受参数的装饰器。

dic = {
    "flag":False,
    "username":None

}
msg ="""
QQ
微信
抖音
邮箱
>>>
"""
choose = input(msg).upper()
def auth(argv):
    def wrapper(func):
        def inner(*args, **kwargs):
            if dic["flag"]:
                foo(*args, **kwargs)
            else:
                if argv == "QQ":
                    print("欢迎来到QQ!")
                    user = input("username:")
                    pwd = input("password:")
                    if user == "zcy1" and pwd == "123":
                        dic["flag"] = True
                        dic["username"] = user
                elif argv == "微信":
                    print("欢迎来到微信!")
                    user = input("username:")
                    pwd = input("password:")
                    if user == "zcy2" and pwd == "1234":
                        dic["flag"] = True
                        dic["username"] = user
                elif argv == "抖音":
                    print("欢迎来到抖音!")
                    user = input("username:")
                    pwd = input("password:")
                    if user == "zcy3" and pwd == "12345":
                        dic["flag"] = True
                        dic["username"] = user
                else:
                    print("欢迎来到邮箱!")
                    user = input("username:")
                    pwd = input("password:")
                    if user == "zcy4" and pwd == "123456":
                        dic["flag"] = True
                        dic["username"] = user
            ret = func(*args, **kwargs)
            return ret
        return inner
    return wrapper

@auth(choose)
# wrapper = auth(choose)
# foo = wrapper()
def foo():
    return "我不管,章超印超帅!"

# @auth(choose)  等于下面的两行
# wrapper = auth(choose)
# foo = wrapper()
print(foo())

@auth(‘choose‘) :分两步:

第一步先执行auth(‘choose‘)函数,得到返回值wrapper

第二步@与wrapper结合,形成装饰器@wrapper然后在依次执行。

这样就是带参数的装饰器,参数可以传入多个

2.多个装饰器装饰一个函数

被装饰的函数正上方有多个装饰器,先执行离被装饰函数最近的装饰器

我们现在知道标准装饰器和带参数的装饰器,我们来看看多个装饰器装饰一个函数:

def wrapper1(func):
    def inner1(*args,**kwargs):
        print("这是装饰器一开始")
        func(*args,**kwargs)
        print("这是装饰器一结束")
    return inner1

def wrapper2(func):
    def inner2(*args,**kwargs):
        print("这是装饰器二开始")
        func(*args,**kwargs)
        print("这是装饰器二结束")
    return inner2

@wrapper1
@wrapper2
def func():
    print("这是被装饰的函数")

func()

大家来推断一下,这个的打印结果

这是装饰器一开始
这是装饰器二开始
这是被装饰的函数
这是装饰器二结束
这是装饰器一结束
 先执行离被装饰的函数最近的语法糖
# 小技巧:进入装饰器从上往下,走到最会一个装饰器执行被装饰的函数,退出装饰器从下往上走

原文地址:https://www.cnblogs.com/zhangchaoyin/p/11378797.html

时间: 2024-10-05 04:58:47

百万年薪python之路 -- 装饰器的相关文章

Python之路-装饰器

一.装饰器前置知识要点 1.名称空间 存放名字与变量绑定关系的地方 名称空间的分类: 内置名称空间:Python解释器启动时,存放自带的名字的地方.(比如内置函数) 全局名称空间:在执行文件时,文件级别名字存放的地方 局部名称空间:在执行文件过程中,如果调用函数,用来存放函数内定名称的地方,调用时生效,调用结束后失效 加载顺序:内置-------->全局---------->局部 查找顺序:局部-------->全局---------->内置 2.作用域 作用域:作用的范围 (1)

python之路——装饰器

作为一个会写函数的python开发,我们从今天开始要去公司上班了.写了一个函数,就交给其他开发用了. def func1(): print('in func1') 季度末,公司的领导要给大家发绩效奖金了,就提议对这段日子所有人开发的成果进行审核,审核的标准是什么呢?就是统计每个函数的执行时间. 这个时候你要怎么做呀? 你一想,这好办,把函数一改: import time def func1(): start = time.time() print('in func1') print(time.t

python之路--装饰器函数

(一)装饰器的形成过程 装饰器的本质是闭包函数的一种应用,装饰器是用来装饰函数的,在不修改原函数及其调用方式的情况下对原函数功能进行拓展. #简单的无参函数装饰器 import time def timmer(func): def inner(): start = time.time() func() end = time.time() print(end -start) return inner @timmer#语法糖 def hahaha(): time.sleep(2)#停止时间的一种用法

百万年薪python之路 -- 闭包

2.闭包 闭包的定义: 闭包是嵌套在函数中的函数. 闭包必须是内层函数对外层函数的变量(非全局变量)的引用. 一句话定义就是:在嵌套函数内,对非全局变量 (且不是本层的变量)的引用 如何判断判断闭包?举例让同学回答: # 例一: def wrapper(): a = 1 def inner(): print(a) return inner ret = wrapper() #是 print(ret.__code__.co_freevars) # 例二: a = 2 def wrapper(): d

百万年薪python之路 -- 模块

1.自定义模块 1.1.1 模块是什么? 模块就是文件,存放一堆常用的函数和变量的程序文件(.py)文件 1.1.2 为什么要使用模块? 1.避免写重复代码,从文件级别组织程序,更方便管理 2.可以多次利用,我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用 3.拿来主义,提升开发效率 同样的原理,我们也可以下载别人写好的模块然后导入到自己的项目中使用,这种拿来主义,可以极大地提升我们的开发效率,避免重复造轮子. 1.1.3 模块的分类 Pyt

百万年薪python之路 -- 模块二

1. 序列化模块 什么是序列化呢? 序列化的本质就是将一种数据结构(如字典.列表)等转换成一个特殊的序列(字符串或者bytes)的过程就叫做序列化. 为什么要有序列化模块? 如果你写入文件中的字符串是一个序列化后的特殊的字符串,那么当你从文件中读取出来,是可以转化回原数据结构的. 作用及用途 序列化模块就是将一个常见的数据结构转化成一个特殊的序列,并且这个特殊的序列还可以反解回去.它的主要用途:文件读写数据,网络传输数据. 1.1 json序列化(很重要) 不同语言都遵循json数据转化格式,即

百万年薪python之路 -- 并发编程之 多线程 二

1. 死锁现象与递归锁 进程也有死锁与递归锁,进程的死锁和递归锁与线程的死锁递归锁同理. 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因为争夺资源而造成的一种互相等待的现象,在无外力的作用下,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在相互等待的进程称为死锁进程 # 多个线程多个锁可能会产生死锁 from threading import Thread from threading import Lock import time lock_A = Lock

百万年薪python之路 -- MySQL数据库之 MySQL行(记录)的操作(一)

MySQL的行(记录)的操作(一) 1. 增(insert) insert into 表名 value((字段1,字段2...); # 只能增加一行记录 insert into 表名 values(字段1,字段2...); insert into 表名(id,name) values(字段1,字段2),(xx1,xx2); id,name,age 插入查询结果 语法: INSERT INTO 表名(字段1,字段2,字段3-字段n) SELECT (字段1,字段2,字段3-字段n) FROM 表2

百万年薪python之路 -- JS基础介绍及数据类型

JS代码的引入 方式1: <script> alert('兽人永不为奴!') </script> 方式2:外部文件引入 src属性值为js文件路径 <script src="test.js"></script> 变量声明 变量名是区分大小写的. 推荐使用驼峰式命名规则.首字母大写 保留字不能用做变量名. 声明前要加var关键字. var a = 1; 加var定义的变量是全局变量,在函数里定义会是局部变量 不加定义的变量不管在哪,都是全局