【漫漫转码路】Python Day 19

一、闭包

1、闭包的条件

1、函数嵌套
2、内部函数引用外部函数的参数或变量
3、外部函数返回内部函数的引用或调用

2、闭包特点

一般情况下,函数在return之后,会结束函数,返回值,同时释放内存(因为无引用)
一个函数的闭包函数,在外部函数结束之后,内部函数引用的外部函数的变量,会被存储下来,存储到内部函数的__closure__属性中。

# 例如
def A(a):
    c = 999
    def B(b):
        d = a + b
    return B
test = A(1) #指向B的内存地址
print(test)
print(test.__closure__)  # 用test调用__closure__可以看到存在内存的变量,以元组的形式存在,由cell对象组成
print(test.__closure__[0].cell_contents)  # 根据索引,cell_contents属性就是外部函数保存的变量
# 终端显示
<function A.<locals>.B at 0x00000233724AC430>  
(<cell at 0x00000233723CF040: int object at 0x00007FFF24312730>,)
1
# 例如
def outer():
    list1 = []
    for k in range(3):
        def inner():
            return k * k
        list1.append(inner)
    return inner


print(outer())  # outer()即指向inner的内存地址
print(outer()())  # 即等价于inner(),返回k * k
print(outer().__closure__)  # 当外部函数结束之后,查看内部函数内存里存了几个变量,可以看到是一个
print(outer().__closure__[0].cell_contents)  # 查看该变量
# 终端显示
<function outer.<locals>.inner at 0x0000024041DCC550>
4
(<cell at 0x0000024041CEF040: int object at 0x00007FFF22982750>,)
2
# 例如
def outer():
    list1 = []
    for k in range(3):
        def inner():
            return k * k
        list1.append(inner)
    return list1

print(outer()) # 因为返回是list1,所以此处是一个列表,列表里面的元素是inner
print(outer().__closure__)  # 注意:list1不是inner,只是list1里面的元素是inner,所以list1不能调用__closure__,此处会报错
print(outer()[0].__closure__[0].cell_contents)  # outer()[0]表示取列表list1的第一个元素,即第一个inner,.__closure__[0]表示取内部的第一个变量,因为知道元组里面只有一个变量,.cell_contents表示查看该变量内容
print(outer()[1].__closure__[0].cell_contents)
print(outer()[2].__closure__[0].cell_contents)
# 终端显示
[<function outer.<locals>.inner at 0x000001156B0BC430>, <function outer.<locals>.inner at 0x000001156B0BC4C0>, <function outer.<locals>.inner at 0x000001156B0BC550>]
AttributeError: 'list' object has no attribute '__closure__'  # 报错
2
2
2

二、装饰器

装饰器:在原来的函数上起装饰作用,不改变原来函数作用,在原来函数基础上增加一些额外功能;
装饰器不是编码必须性

1、函数装饰器

(1)不带参数的装饰器

洗衣机工作时长计时
不使用装饰器

# 例如
import time


def outer(func):
    def timer():
        t1 = time.time()
        func()
        t2 = time.time()
        print(f'洗衣机工作用时{t2 - t1}秒')
    return timer

def washer():
    print('洗衣机开始工作')
    time.sleep(3)
    print('洗衣机工作完成')


washer = outer(washer)
washer()
# 终端显示
洗衣机开始工作
洗衣机工作完成
洗衣机工作用时3.007417678833008

使用装饰器

# 例如
import time


def outer(func):
    def timer():
        t1 = time.time()
        func()
        t2 = time.time()
        print(f'洗衣机工作用时{t2 - t1}秒')
    return timer

@outer  # 将outer作为装饰器修饰washer,此处outer不带参数
def washer():
    print('洗衣机开始工作')
    time.sleep(3)
    print('洗衣机工作完成')


washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
洗衣机工作用时3.0132269859313965

装饰器原理,被装饰的函数定义完成之后,将该函数作为参数,传给装饰器,把它作为装饰器的实参来调用,然后装饰器返回的函数重新命名为与被装饰函数一样的函数名 (装饰器只起装饰作用,不改变原函数功能,所以装饰前后的函数名要一致)

(2)带参数的装饰器

# 例如
import time


def outer(delay):
    def timer(func):
        t1 = time.time()
        func(delay)
        t2 = time.time()
        print(f'洗衣机工作用时{t2 - t1}秒')
    return timer

@outer(3) # 带参数
def washer(delay):
    print('洗衣机开始工作')
    time.sleep(delay)
    print('洗衣机工作完成')


washer()
# 终端显示
洗衣机开始工作
洗衣机工作完成
洗衣机工作用时3.00116229057312# 前面表示正常工作
Traceback (most recent call last):  # 后面是因为执行washer()的时候,washer接收得到的是none,不可被调用,如果将wahser()注释掉,程序自动运行
  File "d:/shenlanclass/PythonFiles/day19/day1901.py", line 61, in <module>
    washer()
TypeError: 'NoneType' object is not callable
PS D:\shenlanclass\PythonFiles> 

增加功能,增加name

# 例如
import time


def outer(name):
    def inner(func):
        def timer(delay):
            t1 = time.time()
            func(delay)
            t2 = time.time()
            print(f'{name}你的衣服洗好了,洗衣机工作用时{t2 - t1}秒,快去拿!')
        return timer
    return inner

@outer('张三')
def washer(delay):
    print('洗衣机开始工作')
    time.sleep(delay)
    print('洗衣机工作完成')


washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
张三你的衣服洗好了,洗衣机工作用时3.0018467903137207,快去拿!

注意:
装饰器函数是多个嵌套的时候(并非多个装饰器),最外一层装饰器的返回值要将被装饰函数的函数名作为实参,所以在从外往里数第二层的时候,函数的要以func(也就是函数)作为参数上传,否则会报错

2、类装饰器

(1)不带参数的类装饰器

# 例如
import time

class f1:
    def __init__(self, func):
        self.func = func


    def __call__(self, delay):
        t1 = time.time()
        self.func(delay)
        t2 = time.time()
        print(f'你的衣服洗好了,洗衣机工作用时{t2 - t1}秒,快去拿!')
    
@f1
def washer(delay):
    print('洗衣机开始工作')
    time.sleep(delay)
    print('洗衣机工作完成')
washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
你的衣服洗好了,洗衣机工作用时3.002544641494751,快去拿!

注意:
不带参数,类装饰器,在被装饰函数定义完之后,将被装饰函数的函数名放到类装饰器里当做实参去执行,因此,此时需要类装饰器在__init__的时候,传入参数func(实参传入被装饰函数)

(2)带参数的类装饰器

# 例如
import time

class f1:
    def __init__(self, name):
        self.name = name


    def __call__(self, func):
        def inner(delay):
            t1 = time.time()
            func(delay)
            t2 = time.time()
            print(f'{self.name}你的衣服洗好了,洗衣机工作用时{t2 - t1}秒,快去拿!')
        return inner

@f1('张三')
def washer(delay):
    print('洗衣机开始工作')
    time.sleep(delay)
    print('洗衣机工作完成')


washer(3)
# 终端显示
洗衣机开始工作
洗衣机工作完成
张三你的衣服洗好了,洗衣机工作用时3.0059919357299805,快去拿!

注意:
带参数,类装饰器,将装饰器后面的参数传入装饰器进行初始化__init__,在被装饰函数定义完之后,将被装饰函数的函数名传入类装饰器,调用__call__,此时,__call__需要接收func(被装饰函数的函数名)作为参数

3、多个装饰器

当使用多个装饰器的时候,被定义参数定义完成之后,作为参数上传到最近的装饰器,第一个装饰器返回值给第二个参数

# 例如
import time  # 一
def deco(func):  # 二
    def wrapper1(*args):  # 九
        res = func(*args)  # 十二
        return res  # 二十
    return wrapper1  # 十
def timer(func):  # 三
    def wrapper2(*args):  # 七
        start_time = time.time()  # 十三
        res = func(*args)  # 十四
        end_time = time.time()  # 十七
        print("函数耗时:{}".format(end_time-start_time))  # 十八
        return res  # 十九
    return wrapper2  # 八
@deco  # 四
@timer  # 五
def add(*args):  # 六
    time.sleep(2)  # 十五
    return sum(args)  # 十六
    
print(add(3, 4, 5))  # 十一  # 二十一
# 十一,此时add = wrapper1,add(3, 4, 5)等价于wrapper1(3, 4, 5),对wrapper1(3, 4, 5)中的3,4,5封包成元组,跳到十二
# 十二,调用func,此时func = wrapper2,同时func接收到的参数是元组(3, 4, 5),*args对元组进行解包,成为不定长参数3, 4,5,跳到十三
# 十三,正常运行,但是在跳到十三的时候,调用wrapper2对接收到的不定长参数再次封包成为元组
# 十四,此时func = add,func接收不定长参数,对接收到的参数解包成为数字3, 4,5跳到十五
# 十五,add对接收到的不定长参数3, 4,5进行封包,执行睡眠
# 十六,执行求和函数,并返回,12,第十四步,res = 12,
# 十七,正常执行 
# 十八,正常执行
# 十九,正常执行,返回res = 12
# 二十,十二部的函数返回值是12,即十二步,res = 12,执行return res,返回12
# 二十一,add(3, 4, 5)的返回值是12
# 终端显示
函数耗时:2.006530284881592
12

4、内置装饰器

(1)@classmethod

类装饰器

(2)@staticmethod

静态装饰器

(3)@property

将类中的方法装饰为只读属性,不可修改
不能传参数
对象只能是实例属性,不能是类属性

# 例如
class Person:

    def __init__(self, name, age):
        self.name = name
        self.age = age


    @property  # 只读属性
    def threshold(self):
        return 18
   

    def is_adult(self):
        if self.age >= 18:
            print(f'恭喜{self.name},成年了')
        else:
            print(f'{self.name}还没有成年')

p1 = Person('张三', 18)
p2 = Person('李四', 16)
p1.is_adult()
p2.is_adult()
print(p1.threshold)
p1.threshold = 16 #此处报错
print(p1.threshold)
# 终端显示
恭喜张三,成年了
李四还没有成年
18  # 查看属性
Traceback (most recent call last):
  File "d:/shenlanclass/PythonFiles/day19/day1901.py", line 205, in <module>
    p1.threshold = 16
AttributeError: can't set attribute  #不能修改

属性返回函数

# 例如
def a(b): # 定义函数a
    return b
class Person:

    def __init__(self, name, age):
        self.name = name
        self.age = age


    @property
    def threshold(self):
        return a  # 返回函数a
   

    def is_adult(self):
        if self.age >= 18:
            print(f'恭喜{self.name},成年了')
        else:
            print(f'{self.name}还没有成年')

p1 = Person('张三', 18)
p2 = Person('李四', 16)
p1.is_adult()
p2.is_adult()
# print(p1.threshold)
print(p1.threshold(1))
# 终端显示
恭喜张三,成年了
李四还没有成年
1

你可能感兴趣的:(转码,python,开发语言,改行学it,深度学习,人工智能)