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
5.單例模式
定義:單例模式,是一種常用的軟體設計模式,在它的核心結構中只包含一個被稱為單例的特殊類,通過單例模式可以保證系統中,應用該模式的類一個類只有一個實體,即一個類無論實體化多少次都只有一個物件實體,
白話定義:一個類,實體化多少次出來的物件都是一個,就像賦值一樣,一個實體化物件,但是可以有多個名字,但是他們(這些名字)都指一個實體化物件.
實體化物件干了三件事:
-
類名加()自動執行object類中的__new__方法,產生并回傳一個物件空間
-
執行__init__方法,將物件空間傳給self.
-
在__init__方法中給物件封裝基礎屬性
應用場景:Django的admin.py和settings檔案
①admin.py:每個應用都有一個admin.py.但是修改每一個admin的內容后,所有的應用的admin內容都會加載,出來,證明他們都用的一個admin.py
②settings.py:是因為原始碼中把settings檔案內容都寫到一個類中,實體化物件,再呼叫物件,from xxx import settings檔案第一次會把settings檔案的內容加載到記憶體中,后面無論from xxx import settings多少次都是直接在記憶體中直接拿,而不是再次實體化物件,隨意每次用的
settings檔案的內容都是一個物件出來的內容.
正規版單例模式:(單例模式的類最好都用singleton做為類名,singleton就是單例模式的意思)
#正規版單例模式(new+鎖+執行緒)import threading #類名都用singleton,是單例的意思 class Singleton(object): instance = None lock = threading.RLock() #鎖放類里面當屬性 def __new__(cls, *arg, **kwargs): if cls.instance: return cls.instance with cls.lock: #用類去呼叫鎖 if not cls.instance: cls.instance = object.__new__(cls) return cls.instance def task(): obj = Singleton() print(obj) for i in range(10): t = threading.Thread(target=task) t.start()
面試版單例模式:
#面試時可以去掉執行緒(new+鎖)鎖一定要有 import threading class Singleton(object): instance = None lock = threading.RLock() def __new__(cls, *args, **kwargs): if cls.instance: return cls.instance with cls.lock: if not cls.instance: cls.instance = object.__new__(cls)#這里object中傳的引數cls就是Singleton類,如果把cls換成Singleton也是可以的 return cls.instance obj1 = Singleton() obj2 = Singleton()
檔案版單例模式:
#呼叫檔案時也是單例模式: #在xx.py檔案中: class Singleton(object): def __init__(self): self.names = [] def fun(self): pass site = Singleton() 在另一個與其相鄰的www.py檔案中: import xx #第一次import會從檔案中讀取,放到記憶體中 print(xx.site) import xx #第二次import會直接從記憶體中拿,而不再是用import讀檔案,所以就是單例模式,無論用多少次import都是第一次讀的內容 print(xx.site)
錯誤版單例模式:
class Singleton(object): instance = None def __init__(self): self.name = None def __new__(cls, *arg, **kawrgs): if not cls.instance: cls.instance = object.__new__(cls) return cls.instance 這個單例模式有問題,每次實體化物件,都會在init把上一個物件的內容全部清空,再寫內容,所以,這個單例模式不能給里面傳引數,只能實體化物件,沒有其他屬性添加進去.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/151082.html
標籤:Python
