首页 > temp > 简明python教程 >
-
Python基础
1.数据类型:
# 可变: list ,dict ,set(集合) # 不可变: int bool str tuple # 有序: list,tuple,str,int,bool # 无序: dict(python3.6以后可以是有序的了),set # 取值方式: # 索引: str list tuple # 直接: set ,int ,bool # 键: dict
注意:'is'和'=='
'=='只比较值是否一样
'is'不仅比较值一样还要比较内存地址是否一样
2.深浅拷贝:
import copy lst1=[1,2,3] lst2=copy.copy(lst1)#浅拷贝 lst3=copy.deepcopy(lst1)#深拷贝 lst4=lst1 #赋值
定义:
浅拷贝 :
浅拷贝只拷贝第一层(如果有列表嵌套,他会复制那一个索引位置的列表的内存地址,而那个列表内的数据不会复制,说白了就是共用内部的list,最外层的list是自己新建的)
深拷贝:
可变类型拷贝,不可变类型共用(列表嵌套时,内层的列表地址不会复制,因为list是可变的,他是自己创个内部的list,然后共用list内的不可变元素的地址)
其实深拷贝不可变元素时也应该重新创建内存,来访对应的不可变元素,因为小数据池的关系所以就没新开辟地址.
注:
如果发生了拷贝,内存地址一定发生变化,说白了拷贝就是自己新建地址,放复制过来的内容.
如下图:深浅拷贝如果只拷贝一个单一的列表(列表内不包含字典列表等可变元素)的情况下,深浅拷贝效果都一样:
再看代码:
#如果只是单一列表,深浅拷贝都一样 import copy lst1=[1,2,3] lst2=copy.copy(lst1)#浅拷贝 lst3=copy.deepcopy(lst1)#深拷贝 print(id(lst1),id(lst2),id(lst3))#列表地址新建,全都不一样 # 44954968 44957408 44990712 print(id(lst1[1]) , id(lst2[1]) , id(lst3[1]))#内部不可变元素全都共用 # 496100128 496100128 496100128
重点(这里要着重理解):
列表内部加了列表后(深浅拷贝就不一样了):
看代码:
#如果列表套列表,深浅拷贝大不一样了 import copy lst1=[1,2,3,[4,5,6,[7,8,9]]] lst2=copy.copy(lst1)#浅拷贝 lst3=copy.deepcopy(lst1)#深拷贝 print(id(lst1),id(lst2),id(lst3))#地址新建,全都不一样 # 44954968 44957408 44990712 print(id(lst1[1]) , id(lst2[1]) , id(lst3[1]))#内部不可变元素全都共用 # 496100128 496100128 496100128 # 看内部列表 print(id(lst1[3]) , id(lst2[3]) , id(lst3[3]))#深拷贝是自己建的所以不一样,浅拷贝是共用的 # 52295000 52295000 52330984 # 看内部列表的内部元素 print(id(lst1[3][0]) , id(lst2[3][0]) , id(lst3[3][0]))#4是不可变元素,共用 # 496296768 496296768 496296768 # 看内部列表再套的列表 print(id(lst1[3][3]) , id(lst2[3][3]) , id(lst3[3][3]))#还是只有深拷贝自己建(因为列表不管在哪都是可变元素),浅拷贝共用 # 44758360 44758360 44796584 print(id(lst1[3][3][1]) , id(lst2[3][3][1]) , id(lst3[3][3][1]))#里面的不可变元素8还是共用的 # 493675392 493675392 493675392
特殊情况(元组套列表):
如果单一元组深浅拷贝就都一样:
import copy v1=(1,2,3) v2=copy.copy(v1)#浅拷贝 v3=copy.deepcopy(v1)#深拷贝 print(id(v1[1]),id(v2[1]),id(v3[1]))#内部不可变元素都是一样的 # 501474080 501474080 501474080 print(id(v1),id(v2),id(v3))#浅拷贝只是单一元组,深浅拷贝都一样,因为里面都是不可变元素,元组也不可变 # 47061912 47061912 47061912 copy.copy(v)=copy.deepcopy(v) #单一元组时深浅拷贝都一样
如果元组里有列表:
import copy v1=(1,2,3,[4,5,6]) v2=copy.copy(v1)#浅拷贝 v3=copy.deepcopy(v1)#深拷贝 print(id(v1),id(v2),id(v3))#浅拷贝不变,深拷贝会变,因为里面多了不可变元素,深拷贝要变的 # 18215728 18215728 48191024 print(id(v1[1]),id(v2[1]),id(v3[1]))#内部不可变元素都是一样的 # 501474080 501474080 501474080 print(id(v1[3]),id(v2[3]),id(v3[3]))#内部列表已经不同地址了所以元组也变了 # 51377496 51377496 51413360 元组套列表(元组里面都是不可变元素,都不变,怎么拷贝都一样,如果元组里面有可变元素,例如列表,此时深拷贝会变,浅拷贝没事,和原来一样) 浅拷贝不变 深拷贝变了,因为深拷贝中不可变元素不会共用
3.闭包:
# 闭包,为函数创建一块区域(开辟一个新的内存地址), # 并且能保护这个函数内部的变量或者是这个函数内部创建的新函数不被销毁, # 并能一直为这个函数的执行提供这些被保护的数据 name ='oldboy'#这是全局变量,自己函数内部使用变量时,没有的话先找父级,父级没有才会找全局 def bar(name): #name='alex' 这样定义一个变量和你把'alex'当参数传进来是一个意思 def inner(): print(name)#使用变量名时,先找自己,自己没有就找父级,父级没有就找全局 return inner #注意这里返回的只是函数名,而不是函数名+括号 v1=bar('alex') #{name='alex',inner}当函数执行时会自动开辟一块空间(内存地址)来执行这个函数, # 执行之后就会销毁这个执行函数的内存,而闭包不会销毁,这块执行函数的内存一直存在,这里的name变量和inner函数就被保护了起来,一直存在. 到了这里相当于是v1=inner,这个函数内部的变量一直在被调用,所以不会销毁.除非v1执行了(inner函数被执行后)就销毁了 v2=bar('eric') #{name='eric',inner} #v1和v2开辟的是不同的内存地址,并不会共用,所以他俩互相不影响 v1()#输出'alex' 此时v1的bar函数新开辟的执行内存就销毁了 v2()#输出'eric' 此时v2的bar函数新开辟的执行内存也被销毁了
闭包练习:
#第一题: info=[] def func(): print(item) for item in range(10): #循环不创建作用域,所以他还是在全局范围做循环,直接循环到9就停止,并返回给info里面10个func函数名和全局变量item=9 info.append(func) info[0]() #输出:9 #第二题:
info=[] def func(i): def inner(): print(i) return inner for item in range(10): #先循环,每次循环的结果都被添加到info里面去,0,1,2...9 info.append(func(item)) #这执行了func函数,并把inner返回给了info列表,而每次的item都不一样 info[0]() #输出0 info[1]() #输出1
info[6]() #输出6
高阶函数:
①对函数名进行赋值 v=func
②把函数名当作参数传递 def xx(ww): xx(func)
③把函数名当做返回值返回 return func
4.装饰器:
普通装饰器:
定义:
在不改变原函数内部代码的基础上,在函数执行之前和函数执行之后添加某些功能
#装饰器,习惯上将装饰器函数命名为wrapper,这里用的是func,内部函数习惯命名为inner def func(arg): def inner(): print('before') res=arg() print('after') return res return inner #被装饰器装饰的函数@func是简写,原来是 # v=func(index) # index=v # 或者直接写成:index=func(index) @func def index(): print('123') return '666' print(index) # 此时输出为:<function func.<locals>.inner at 0x03A51150>,表示index现在 # 是func函数中的一个内部函数inner,index不再是以前的index函数了,原来的index函数已经传进去赋值给了arg了 print(index())#在执行原来的index函数之前会输出before,结束后会输出after, # 而index的返回值一定是在after之后再输出,注意看代码的执行顺序 # 输出: # before # 123 # after # 666 # 装饰器的功能: # 第一步:执行func函数(就是装饰器函数),并且将被装饰的函数的函数名当做参数传递到装饰器中,相当于:func(index) # 第二步:将func函数的返回值inner赋值给被装饰器装饰的函数的函数名(此时这个函数名已经不能调用原来被装饰的函数了,而是inner函数了).这一步操作相当于 index=func(index)
格式:
装饰器的编写格式: def 外层函数(参数): def 内层函数(*args,**kwargs): return 参数(*args,**kwargs) return 内层函数 装饰器应用格式: @外层函数 def index(): pass index() 内层函数用*args,**kwargs接收参数: 这样你的装饰器可以接收没有参数的函数,有一个参数或者多个参数的装饰器都行.
带参数的装饰器:
带参数的装饰器本质就是在原来的装饰器外边再加个函数.
因为原来的装饰器中@只能写一个参数,就是装饰器的函数名,所以用带参数的装饰器来增加你需要的参数,就是在执行装饰器之前,先执行一个函数,用这个函数把参数传到装饰其中(例如在装饰其中执行n次被装饰的函数,这个n就得是带参数的装饰器传进去的n)
#执行n次被装饰的函数 def x(n): print('x函数') def wrapper(arg): print('wrapper函数') def inner(*args,**kwargs): print('inner函数') res='' for i in range(n): res = arg() print('这是执行第(',i+1,')次啦!') return res return inner return wrapper @x(9)#传的参数就是9 def index(): print('123') return '666' print(index()) #只返回第n次执行的返回值 def x(n): print('x函数') def wrapper(arg): print('wrapper函数') def inner(*args,**kwargs): print('inner函数') for i in range(n): res = arg() return res return inner return wrapper @x(9) def index(): print('123') return '666' print(index())
多个装饰器装饰一个函数:
这里常考的其实就是执行顺序.把执行顺序看明白了就好了.(顺序就是一条龙,跟考试贴座位号是一个道理)
一个函数正上方有多个装饰器时,先执行离被装饰函数最近的装饰器(如下图,执行顺序一定是3,2,1)
但是执行的位置要注意
①外层函数(wrapper内的代码)顺序分别是3-2-1,此时一条龙走到了1
②内层函数(inner函数内,到被装饰函数f之前的代码),1-2-3,此时一条龙走到了3
③执行被装饰的函数f,此时还在3
④执行inner函数中被装饰函数下面的代码,3-2-1
def wrapper1(f): print("我是wrapper1") def inner1(): print("我是wrapper1的开始") f() print("我是wrapper1的结束") return inner1 def wrapper2(f): # f=func() print("我是wrapper2") def inner2(): print("我是wrapper2的开始") f() print("我是wrapper2的结束") return inner2 def wrapper3(f): print("我是wrapper3") def inner3(): print("我是wrapper3的开始") f() print("我是wrapper3的结束") return inner3 @wrapper1 @wrapper2 @wrapper3 def func(): print('我是被装饰的函数!') func() 输出: 我是wrapper3 我是wrapper2 我是wrapper1 我是wrapper1的开始 我是wrapper2的开始 我是wrapper3的开始 我是被装饰的函数! 我是wrapper3的结束 我是wrapper2的结束 我是wrapper1的结束 @wrapper1拆开:func=wrapper1(func) wrapper1装饰的是前两个wrapper装饰过的inner
@wrapper2拆开:func=wrapper2(func) wrapper2装饰的其实是wrapper3装饰过的inner
@wrapper3拆开:func=wrapper3(func) wrapper3装饰的其实就是内层的inner