python---基础知识回顾(二)(闭包函数和装饰器)

一.闭包函数:

闭包函数:

1.在一个外函数中定义了一个内函数

2.内函数里运用了外函数的临时变量,而不是全局变量

3.并且外函数的返回值是内函数的引用。(函数名,内存块地址,函数名指针..)

正确形式:

def outer():
    name = "dasfa"
    def inner():
        print("inner func exec,get outer func name",name)
    return inner

func = outer()
func()    #执行内联中的函数"inner func exec,get outer func name dasfa"

案例一:内联函数inner使用的是外部outer函数中的数据

>>> def outer():
...     age = 10
...     print(id(age))
...     def inner():
...             print("age",age)
...             print(id(age))
...     return inner
...
>>> func = outer()
2012959808
>>> func()
age 10
2012959808

案例二:是否可以修改outer中的数据:

>>> def outer():
...     age = 10
...     print(id(age))
...     def inner():
...             age = 11
...             print(id(age))
...     return inner
...
>>> func = outer()
2012959808
>>> func()
2012959840

就同python---基础知识回顾(一)所说的传参一样,要想修改,那就需要将其中的变量设置为可变类型(列表等)

使用闭包的好处:含有自己的变量,不与全局变量相冲突。可以不用传参。

闭包的__closure__变量

闭包都有__closure__属性,会返回外部作用域中的变量信息。

>>> def outer():
...     age = 10
...     print(id(age))
...     def inner():
...             print(id(age))
...             print(age)
...     return inner
...
>>> func = outer()
2012959808
>>> print(func.__closure__)
(<cell at 0x0000000000CF8DC8: int object at 0x0000000077FB5440>,)  #返回元组,若是有多个变量,都会显示在这里面,我们都可以通过索引获取
>>> print(func.__closure__[0])    #获取地址
<cell at 0x0000000000CF8DC8: int object at 0x0000000077FB5440>
>>> print(func.__closure__[0].cell_contents)  #获取值
10

注意:若是引用的是全局变量,获取inner函数中将外部outer函数中的变量进行覆盖后,那么func.__closure__会为空

闭包的简单使用:

from urllib.request import urlopen

def outer(url):
    def get():
        return urlopen(url).read()
    return get

info = outer("https://www.baidu.com")
print(info())


b'<html>
<head>
	<script>
		location.replace(location.href.replace("
https://","http://"));
	</script>
</head>
<body>
	<noscript><meta
http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
</body
>
</html>'

二.装饰器函数:

python装饰器:(开放封闭原则)

python 装饰器

就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

开放封闭原则:虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改

(封闭:已实现的功能代码块,开放:对扩展开发)

装饰器简单实现:

def check_login(func):
    def inner():
        #验证1
        #验证2
        #...
        return func()
    return inner


@check_login
def f1():
    print("f1")

@check_login
def f2():
    print("f2")

@check_login
def f3():
    print("f3")

装饰器:外部函数参数为:被装饰函数名,内部函数返回装饰函数名。

特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码。

其执行顺序:@函数名是python中的一种语法形式,其内部会执行下面操作:

1.执行check_login函数,并且将@check_login下面的函数作为参数,传递进去。等价于check_login(f1)
在内部会执行
    def inner():
        #验证1
        #验证2
        #...
        return func()   #func是参数,相当于传入的函数名
    return inner     #返回的inner,inner代表的是函数名,不会去执行,会将这个函数传递到另外一个函数中
2.将执行完的check_login函数返回值给@check_login下面修饰的函数的函数名:相当于对该函数进行了重新赋值(这个新的函数是经过修改验证后的函数)
新f1 = inner #inner中添加了验证代码,然后执行了原来f1的逻辑代码
3.执行新f1。即完成了验证功能,有执行了原来F1的内容。并将其原来函数的返回值返回给业务调用

补充:多个装饰器同时修饰时的调用顺序:

>>> def check_1(func):
...     def inner():
...             print("check_1")
...             func()
...     return inner
...
>>> def check_2(func):
...     def inner():
...             print("check_2")
...             func()
...     return inner
...
>>> @check_1    index = check_1(check_2(index))  #后
... @check_2    index = check_2(index)  #先,然后将这个新的函数作为参数传递
... def index():
...     print("进入首页")
...
>>> index()
check_1
check_2
进入首页
... @check_2    index = check_2(index)  #先,然后将这个新的函数作为参数传递
... def index():
...     print("进入首页")


index = def inner():
                print("check_2")
                print("进入首页")
@check_2
@check_1

最新的index函数是上面被check_2和check_1修饰过的函数.

check_1
check_2
进入首页
执行结果

有参装饰器:

外部函数是用来接收被装饰函数的地址

内联函数是用来接收参数(一般是与被修饰函数一致,因为inner函数中调用了被修饰函数,需要对其进行传参)

def check_login(func):
    def inner(*args,**kwargs):
        return func()
    return inner
与被修饰函数不一致情况
接收一个参数
def check_func(func):
    def inner(arg1,arg2):
        #验证
        if 正确:
            return func(arg1,arg2)
    return inner

@check_login
def index(arg1,arg2):
    print("欢迎来到首页")
接收两个参数
def check_func(func):
    def inner(*args,**kwargs):
        #验证
        if 正确:
            return func(*args,**kwargs)
    return inner

@check_login
def index(*args,**kwargs):
    print("欢迎来到首页")
接收多个参数

更强大装饰器:

def Before(request, kargs):
    print('before')


def After(request, kargs):
    print('after')


def Filter(before_func, after_func):
    def outer(main_func):
        def wrapper(request, kargs):
            before_result = before_func(request, kargs)
            if (before_result != None):
                return before_result

            main_result = main_func(request, kargs)
            if (main_result != None):
                return main_result

            after_result = after_func(request, kargs)
            if (after_result != None):
                return after_result
        return wrapper
    return outer


@Filter(Before, After)  #注意,这里是先执行该外层函数然后返回outer函数名给@符号,进行处理。
def Index(request, kargs):
    print('index')

Index("aa","bb")

类装饰器:

装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('class decorator runing')
        self._func()
        print('class decorator ending')


@Foo  # bar = Foo(bar) 传入到__init__函数中 , 返回时使用__call__
def bar():
    print('bar')


bar()  # Foo(bar)()

# 结果
# class decorator runing
# bar
# class decorator ending

functools.wraps

虽然使用装饰器极大的复用了代码,但是他有一个缺点,就是原函数的元信息不见了,比如__doc__,__name__,参数列表:

def check_login(func):
    def inner():
        '''验证信息'''
        print("验证")
        func()
    return inner

@check_login
def f1():
    '''首页信息'''
    print("f1 exec")

f1()
print(f1.__doc__)    #“验证信息”,若是inner中也没有__doc__信息,那么这里为空
print(f1.__name__)    #“inner”

这可不是我们想要的信息。解决方案是:functool.wraps

functool.wraps本身也是一个装饰器,他将原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

from functools import wraps

def check_login(func):
    @wraps(func)
    def inner():
        '''验证信息'''
        print("验证")
        func()
    return inner

@check_login
def f1():
    '''首页信息'''
    print("f1 exec")

f1()
print(f1.__doc__)  #"首页信息"
print(f1.__name__) #"f1"

常用的内置装饰器:

@staticmathod,@classmethod,@property

原文地址:https://www.cnblogs.com/ssyfj/p/8859172.html