Day 12 闭包函数 + 装饰器 (如懂*-*)

chsun12 / 2024-12-19 / 原文

目录
  • 0 昨日复习
    • 0.1 函数对象
      • 0.1.1 引用
      • 0.1.2 当作函数的返回值
      • 0.1.3 当作函数的参数
      • 0.1.4 当作容器的元素
    • 0.2 函数的嵌套
    • 0.3 空间名称与作用域
  • 1 闭包函数
    • 1.1 何为闭包?
    • 1.2 代码展示(这就是闭包!)
    • 1.3 闭包的应用
  • 2 装饰器
    • 2.1 装饰器是什么?
    • 2.2 为什么要用装饰器?(如懂,
    • 2.3 怎么用装饰器?
      • 2.3.1 改变源代码
      • 2.3.2 编写重复代码
      • 2.3.3 第一种传参方式:改变调用方式
      • 2.3.4 第二种传参方式:报给函数(外包)
    • 2.4 完善装饰器
    • 2.5 装饰器模板
    • 2.6 装饰器语法糖 +

0 昨日复习

0.1 函数对象

0.1.1 引用

def f1():
  print(147)
f = f1

0.1.2 当作函数的返回值

def f2():
	return f1
f3 = f2()  #f1
f3()#  f1()

0.1.3 当作函数的参数

def f4(func):
  func()
f4(f1)   # f1()

0.1.4 当作容器的元素

#容器:列表,元组and so on
l = [1,2,3,l1]
l[-1]()   #f1()

0.2 函数的嵌套

from math import pi
def circle(radius,action='area'):
  if action == 'perimeter':
    def perimeter():
      return 2*pi*radius
    res = perimeter()
  else:
    def area():
      return pi*radius**2
    res = area()
  return res
res_area = circle(1)
res_perimeter = circle(1,action = 'perimeter')
print(f'area:{res_area}')
print(f'perimeter:{res_perimeter}')

0.3 空间名称与作用域

  • 内置名称空间

  • 全局名称空间

  • 局部名称空间

  • 查找顺序:内置名称空间 ---> 全局名称空间 ---> 局部名称空间

  • 执行顺序:从当前名称空间开始查找,然后逐步网上,如果当前为局部,则顺序为 局部--->全局--->内置

  • 作用域:函数内部的变量和全局的变量,可能命名相同,但不是一个东西

  • global:局部变量声明编程全局变量 (不推荐使用)

  • nonlocal:从内层函数的内部变量声明到外部变量(外层函数的内部变量)(不推荐使用)

1 闭包函数

1.1 何为闭包?

**闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。 ** (不懂先放着

1.2 代码展示(这就是闭包!)

# this be written by myself ,my understand
def f1(x): 
  def f2(y):
    res = x+y
    return res
  return f2
f3 = f1(2)
res = f3(3)
print(res)

1.3 闭包的应用

爬虫! 等学完再另外专门学爬虫

2 装饰器

2.1 装饰器是什么?

装饰的东西,工具,装饰的工具

本质也是一个函数,只不过这个函数具有装饰的功能

2.2 为什么要用装饰器?(如懂,

如果我们已经上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。

装饰器的实现必须遵循两大原则:

  • 不修改被装饰对象的源代码

  • 不修改被装饰对象的调用方式

如懂:后面想起来了再说

2.3 怎么用装饰器?

2.3.1 改变源代码

import time
def time_count():
  print("welcome to time_count")
  start_time = time.time()
  time.sleep(3)
  end_time = time.time()
  print(f'休眠时间:{end_time - start_time}')
time_count()
time_count()

2.3.2 编写重复代码

import time
def index():
  print('welcome to index')
  time.sleep(2)
def f2():
  print('welcome to f2')
  time.sleep(2)
  
start = time.time()
index()
end = time.time()
print(f'index run time is {start - end}')

start = time.time()
f2()
end = time.time()
print(f'f2 run time is {start - end}')

2.3.3 第一种传参方式:改变调用方式

import time
def index():
  print('welcome to index')
  time.sleep(2)
  
def time_count(func):
  start = time.time()
  func()
  end = time.time()
  print(f'{func} time is {end - start}')

time_count(index)

2.3.4 第二种传参方式:报给函数(外包)

import time
def index():
  print('welcome to index')
  time.sleep(2)
  
def time_count(func):
  def wrapper():
    start = time.time()
    func()
    end = time.time()
    print(f'{func} time is {end - start}')
  return wrapper

f = time_count(index)
f()

声明:上述过程我都懂,但目前以我垃圾的认知,真不知道好用到哪了*-*

2.4 完善装饰器

如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。

import time

def index():
    print('welcome to index')
    time.sleep(1)
    return 123

def home(name):
    print(f"welcome {name} to home page")
    time.sleep(1)

    return name

def time_count(func):
    # func = 最原始的index
    def wrapper(*args, **kwargs):  # 这里给wrapper 加了这俩玩意,因此能够接受任意数据的参数作为形参
        start = time.time()
        res = func(*args, **kwargs)    # 这里就要求time_count函数接收的函数必须为有参函数,即home,不能为index
        end = time.time()
        print(f"{func} time is {start-end}")

        return res  # 即此时函数wrapper(*args, **kwargs) 会得到返回值 res,即func(*args,**kwargs),
										# 即wrapper(*args, **kwargs) = func(*args,**kwargs)
    
    return wrapper # 即此时函数执行time_count(func) = wrapper = func 

home = time_count(home)  # 即home = time_count(home) = wrapper = home

res = home('egon')   # 此时home 变了,被wrapper取代了
print(f"res: {res}")

in short:这就是装饰器的常见用法。用于在不修改原始函数代码的情况下,给函数添加额外的功能。

说实话,本来就是在稀里糊涂的跟着水导的博客一个接着一个代码敲,边敲边理解,但到理解到最后有一个疑问,好像原来的home函数不在了,被取代了,但又不确实对不对,去ai了一下,果然是的被wrapper取代了,接着最后一句话瞬间明白了,有一种醍醐灌顶的感觉,^-^

image-20241025165217761

2.5 装饰器模板

def deco(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

2.6 装饰器语法糖 +

在被装饰函数正上方,并且是单独一行写上@装饰器名

userinfo_dict = {'sch':147}
is_login_list = False  #定义成可变类型或着global使其可以修改
def login(func):
    def wrapper(*args,**kwargs):
        global is_login_list
        if not is_login_list:
            username = input("请输入你的用户名>>>")
            pwd = int(input("请输入你的密码>>>"))
            if username in userinfo_dict and pwd == userinfo_dict.get(username):
                print('登陆成功')
                is_login_list = True
                res = func(*args,**kwargs)
                return res
            print("傻逼来老子这里吃霸王餐!")
        else:
            res = func(*args,**kwargs)
            return res
    return wrapper
@login
def shopping():
    print('with shopping')

def pay():
    print('with pay')