详解2

阅读: 评论:0

详解2

详解2

文章目录

    • 0、函数嵌套
    • 1、闭包
      • 1.1、概念
      • 1.2、条件
      • 1.3、闭包陷阱
    • 2、装饰器
      • 2.1、带参
      • 2.2、带返回值
      • 2.3、装饰器带参
      • 2.4、多个装饰器
      • 2.4、小结

0、函数嵌套

变量名解析:LEGB原则

变量名查找:

  1. 首先从本地(L)查找;

  2. 本地没有找到,从上一层机构中的def或者lambda的本地作用域(E);

  3. 从全局作用域(G)中查找;

  4. 从内置的模块(B)中查找,第一个出现的地方查找;

nonlocal关键字:内部函数想要改变外部函数的变量,需要加nonlocal关键字,示例如下

def outer():a = 100def inner():nonlocal ab = 200a += bprint('我是内部函数', a)inner()print(a)outer()

1、闭包

1.1、概念

首先看一下维基上对闭包的解释:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

1.2、条件

形成闭包的三个条件:

  1. 嵌套函数
  2. 内部函数引用外部函数的变量
  3. 返回内部函数
  • 简单示例如下

    '''
    求直线上的对应x的y值,方程式:y = a*x+b
    '''def outer(a, b):def inner(x):return a * x + breturn innerline = outer(2, 1)
    print(line(1))
    

简单分析:通过断点查看

  • 此时line值为:<function outer..inner at 0x00000000031758B0>

1.3、闭包陷阱

先看一段代码:自己思考结果

def my_func(*args):fs = []for i in range(3):def func():return i * ifs.append(func)return fsfs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())

结果会是0 1 4 吗?并不是 结果为 4 4 4,为什么呢?

  • 因为在函数my_func返回前其内部定义的函数并不是闭包函数,只是一个内部定义的函数。当然这个内部函数引用的父函数中定义的变量也不是自由变量,而只是当前block中的一个local variable。
  • 在内部定义的函数func实际执行前,对局部变量j的任何改变均会影响到函数func的运行结果

正确的写法:

def my_func(*args):fs = []for i in range(3):func = lambda _i=i: _i * _ifs.append(func)return fsfs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())
  • 总结:返回闭包中不要引用任何循环变量,或者后续会发生变化的变量。

  • 参考文章:

    • 理解Python闭包概念
    • [Python小记] 通俗的理解闭包 闭包能帮我们做什么?

2、装饰器

因为还没有学习面向对象,这里关于类装饰器相关的内容暂时不介绍,只讲解光宇函数装饰器内容。

下面我们通过模拟新房装修来认识下装饰器,你买了一座新房子(毛坯房),现在我们要对毛坯房进行装修。

开闭原则是编程中最基础、最重要的设计原则。

基本介绍:

  1. 一个软件实体如类,模块和函数应该对扩展开放(对于提供方来说),对修改关闭(对于使用方来说)。用抽象构建框架,用实现扩展细节。
  2. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
  3. 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

示例如下:

def decorator(func):def wrapper():func()print('刷漆')print('铺地板')print('买家具')print('精装修房,可以入住啦~~~')return wrapper@decorator
def house():print('毛坯房。。。')house()

那么执行顺序是怎么样的呢?

def decorator(func):print('decorator start ...')def wrapper():print('wrapper start ...')func()print('wrapper end ...')print('decorator end ...')return wrapper@decorator
def func():print('函数执行...')# func()

执行结果:

decorator start …
decorator end …

执行顺序:加载decorator -> 加载原始func->执行decorator->func此时指向wrapper

  • 执行decorator时,把原始func作为实参传给decorator的形参

2.1、带参

还是以上面装修房子为例,我们是装修队的,需要知道房屋地址。

def decorator(func):def wrapper(address):func(address)print('刷漆')print('铺地板')print('买家具')print('精装修房,可以入住啦~~~')return wrapper@decorator
def house(address):print('房子在: {}是一座毛坯房。。。'.format(address))house('杭州西湖')

随着业务拓展,我们不仅要装修房子,还要装修工厂,装修工厂的时候,我们还要知道厂房的面积。装修房子和装修工厂过程相似,只是接收的参数不同,那么我们能不能用相同的装饰器呢?

def decorator(func):def wrapper(*args):func(*args)print('刷漆')print('铺地板')print('买家具')print('精装修房,可以入住啦~~~')return wrapper@decorator
def house(address):print('房子在: {} 是一座毛坯房。。。'.format(address))@decorator
def factory(address, area):print('工厂在: {} 是一座毛坯房,建筑面积: {}'.format(address, area))house('杭州西湖')
factory('杭州西湖', 100)

同理,如果参数有默认值参数,那么形参需要加**kwargs:

def decorator(f):passdef wrapper(*args, **kwargs):passf(*args, **kwargs)passpassreturn wrapper

2.2、带返回值

继续以装修房子为例,我们现在要对装修房子做一个预算,看看毛坯房+装修需要花费多少。

def decorator(f):def wrapper(*args, **kwargs):ret = f(*args, **kwargs)print('刷漆')print('铺地板')print('买家具')print('精装修房,可以入住啦~~~')ret += 10000return retreturn wrapper@decorator
def house(address, area):print('房子在: {}是一座毛坯房, 面积:{}'.format(address, area))return 50000cost = house('杭州西湖', 100)
print('预计花费:{}'.format(cost))

2.3、装饰器带参

简单示例:

def decrator(*dargs, **dkargs):def wrapper(func):def _wrapper(*args, **kargs):print("装饰器参数:", dargs, dkargs)print("函数参数:", args, kargs)return func(*args, **kargs)return _wrapperreturn wrapper@decrator(1, 2, a=1, b=2)
def f():print('函数执行')f()

执行顺序:加载decorator->加载f->执行decorator->返回wrapper->执行wrapper->放回_wrapper,f指向_wrapper

  • 解析
    • 返回的wrapper直接执行了

一般装饰器参数很少使用。

2.4、多个装饰器

def decorator_1(f):print('decorator_1 start')def wrapper(*args, **kwargs):print('wrapper_1 start')ret = f(*args, **kwargs)print('wrapper_1 end')return retprint('decorator_1 end')return wrapperdef decorator_2(f):print('decorator_2 start')def wrapper(*args, **kwargs):print('wrapper_2 start')ret = f(*args, **kwargs)print('wrapper_2 end')return retprint('decorator_2 end')return wrapper@decorator_2
@decorator_1
def hello():print('hello python')hello()

可以看到,当多个装饰器装饰同一个函数时,会是一个嵌套的装饰结果,也就是说,先执行完离函数近的一个装饰器,然后再用离函数远的装饰器来装饰执行结果。

2.4、小结

装饰器功能:

  • 引入日志
  • 统计执行时间
  • 执行函数前的预处理
  • 执行函数后的清理功能
  • 权限校验等场景
  • 缓存

通用函数装饰器格式:

def decorator(f):passdef wrapper(*args, **args):passret = f(*args, **args)passreturn retpassreturn wrapper

本文发布于:2024-02-02 14:46:23,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170685638444503.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:详解
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23