python:闭包、装饰器、深浅拷贝(顺便分享赋值和变量传递)

一、闭包:局部变量往往随着函数开始调用而产生,执行结束而消亡。
闭包可以保存函数内的变量,不会随着调用完函数而被销毁。
闭包构成条件:1.有嵌套;2.有引用;3.有返回值。
注意:1.函数名本身记录的是内存地址;2.如果内部函数要改变外部变量,需要nonlocal声明,但注意重新运行函数时还是最初的函数值,除非使用了内部函数,才会重新改变。

闭包:
def outer(c):
    a=int(input('value:'))
    b=2
    def inner(num):   #有嵌套
        num=num+a+b+c    #有引用
        print(num)
    return inner     #有返回
f=outer(3)
f(1)
f(2)
f(3)
声明nonlocal,改变外部变量b
def outer():
    b=2
    print(b)
    def inner(num):  #有嵌套
        nonlocal b
        b=10
        num=num+b    #有引用
        print(num)
    return inner     #有返回
f=outer()
f(1)
outer()

二、装饰器:用来给原有函数在不改变自身情况下,增加额外功能。
装饰器有两种调用方式:1.原生方法调用;2.语法糖形式。另外,可以同时调用多个装饰器,当调用多个装饰器时,距离函数近的先装饰,然后把装饰好的结果传递给下一个装饰器,但打印是从外到内执行(反着来)。
注意:1.装饰器需要能够接收原有函数形参,定义装饰器时常常用*args和**kwargs作形参;2.装饰器每次只能传入一个参数,如果有多个参数,不然再嵌套函数(参看下面例子:装饰器工厂),不然可用原生方法(原生方法一次可以传入多个参数)。

定义装饰器&原生方法调用:
def outer(func):
    def inner():
         print('外部函数启动')  #额外功能
         func()         #调用原有函数
    return inner   #此处返回的是原有函数
def comment():     #定义原有函数
    print('内部函数启动:开始评论')
comment=outer(comment)  #调用装饰器的原生方法
comment()        #此处调用的是内部函数
定义装饰器&语法糖形式,运用了*args和**kwargs。
def outer(func):
     def inner(*args,**kwargs):  #*代表的是解包
          print('外部函数启动')  #额外功能
          func(*args,**kwargs)         #调用原有函数
     return inner   #此处返回的是原有函数
 @outer        #调用装饰器
 def comment(name):     #定义原有函数
     print(f'内部函数启动:开始评论{name}')
 comment('bob')        #此处调用的是内部函数
同时定义和调用多个装饰器
def outer1(func):
    def inner(*args,**kwards):
        print('第一个装饰器')
        func(*args,**kwards)
    return inner
def outer2(func):
    def inner(*args,**kwards):
        print('第二个装饰器')
        func(*args,**kwards)
    return inner
@outer1    #后装饰,先打印,打印结果为“第一个装饰器”
@outer2    #先装饰,后打印,打印结果:“第二个装饰器”
def  get_sum(*args,**kwards):
    sum=0
    for i in args:
        sum+=i
    for i in kwards.values():
        sum+=i
    print(sum)
    return sum
    get_sum(1,2,3,A=2,B=1,C=3)   #最后打印结果
附加分享:装饰器工厂
def ooouter(name):
    def oouter(flag):
        def outer(func):
            def inner():
                if flag=="*":
                    print(f'{name}正在走路')
                elif flag=="#":
                    print(f'{name}正在骑车')
                elif flag=="@":
                    print(f'{name}正在开车')
                func()
            return inner
        return outer
    return oouter
dec=ooouter("小红")("#")
@dec
def play1():
    print('要去学校')
dec=ooouter('小明')('@')
@dec
def play2():
    print('要去上班')
play1()
play2()

三、赋值、变量传递、浅拷贝和深拷贝
赋值:直接将值赋给变量,如:a=1;
变量传递:将a变量的值赋给b变量,如:b=a;
浅拷贝:拷贝了最外围的对象本身,内部元素仅仅拷贝了一个引用,表现形式:copy(a);
深拷贝:拷贝出来一个全新的对象,不再与原来对象有任何关联,表现形式:deepcopy(a);
数据分为可变类型和不可变类型,对于不可变类型,再赋值给新变量名、变量传递、浅拷贝和深拷贝都只是引用地址传递,不开辟新的空间;
对于可变类型来说:1.除了变量传递不会开辟新的空间,只是引用地址传递外,再赋值给新变量名、浅拷贝和深拷贝都会开辟新的空间;2.浅拷贝和深拷贝的不同主要体现在:对于含有多层的可变类型数据,浅拷贝只拷贝了最外层,第二层及以上的值照样是引用地址传递,所以当第二层及以上的值发生变化时,浅拷贝结果也会发生相应变化(藕断丝连),而深拷贝是完全脱离了原来数据变量,绝不变化(恩断义绝)。
注意:在面对只有一层的可变类型数据时,浅拷贝和深拷贝没有差别:都开辟了新的空间,并都不受原来变量影响。

针对可变类型数据,赋值与变量传递的不同
a=[1,2,3]       #赋值
b=[1,2,3]       #再赋值给新变量名
c=a               #变量传递
print(id(a),id(b),id(c))       #c和a的id一样,但都和b的id不一样(c只是引用地址传递,b开辟了新空间)
对于不可变类型:数值、字符串、元组;
无论再赋值给新变量名、还是变量引用、浅拷贝和深拷贝,都只是引用地址传递。

from copy import copy,deepcopy       #先导入函数


#数值型
a1=1                  #赋值
b1=copy(a1)      #浅拷贝
c1=deepcopy(a1)   #深拷贝
d1=a1                #变量传递
e1=1                 #再赋值给新变量名
print(a1,b1,c1,d1,e1)              #值都一样
print(id(a1),id(b1),id(c1),id(e1))     #id都一样


#字符串型
a2='123'
b2=copy(a2)
c2=deepcopy(a2)
d2=a2
e2='123'
print(a2,b2,c2,d2,e2)        #值都一样
print(id(a2),id(b2),id(c2),id(e2)    #id都一样


#元组型
a3=(1,2,3)
b3=copy(a3)
c3=deepcopy(a3)
d3=a3
e3=(1,2,3)
print(a3,b3,c3,d3)        #值都一样
print(id(a3),id(b3),id(c3))   #id都一样
不可变类型数据:列表、集合、字典
#列表型
a4=[1,2,3]          #赋值
b4=copy(a4)     #浅拷贝
c4=deepcopy(a4)   #深拷贝
d4=a4       #变量传递
e4=[1,2,3]     #再赋值给新变量名
print(a4,b4,c4,d4,e4)   #值都一样
print(id(a4),id(b4),id(c4),id(e4))      #除了变量传递的d4的id与a4一样,其它都不一样


#集合型(同列表情况)
a5={1,2,3}
b5=copy(a5)
c5=deepcopy(a5)
d5=a5
e5={1,2,3}
print(a5,b5,c5,d5,e5)
print(id(a5),id(b5),id(c5),id(e5))    


#字典型(同列表情况)
a6={1:'a',2:'b',3:'c'}
b6=copy(a6)
c6=deepcopy(a6)
d6=a6
e6={1:'a',2:'b',3:'c'}
print(a6,b6,c6,d6,e6)
print(id(a6),id(b6),id(c6),id(d6),id(e6))
**#深浅拷贝的不同点:以列表为例**
a=[1,2,[3,4,['tom','linda']]]            #赋值于a
b=copy(a)                             #浅拷贝,赋值于b
c=deepcopy(a)                   #深拷贝,赋值于c
print(a,b,c)                       #a,b,c此时值都为[1,2,[3,4,['tom','linda']]] 
print(id(a),id(b),id(c))        #三者id各不相同
#改变第二层
a[2][0]=5                        #将第二层的3改为5
print(a,b,c)                  # 此时b受到影响,变得和a值一样,c不受影响 
print(id(a),id(b),id(c))
#改变第三层
a[2][2][0]='july'              #将第三层'tom'改为'july'
print(a,b,c)                  #此时b受到影响,变得和a值一样,c照常不受影响
print(id(a),id(b),id(c))


输出结果
#最初结果:
[1, 2, [3, 4, ['tom', 'linda']]] [1, 2, [3, 4, ['tom', 'linda']]] [1, 2, [3, 4, ['tom', 'linda']]]
2911905060168     2911905061064       2911904581384
#改变第二层后的结果
[1, 2, [5, 4, ['tom', 'linda']]] [1, 2, [5, 4, ['tom', 'linda']]] [1, 2, [3, 4, ['tom', 'linda']]]
2911905060168      2911905061064        2911904581384
#改变第三层后的结果
[1, 2, [5, 4, ['july', 'linda']]] [1, 2, [5, 4, ['july', 'linda']]] [1, 2, [3, 4, ['tom', 'linda']]]
2911905060168      2911905061064          2911904581384
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值