【遊戲王】效果無效和攻守變化類效果的一個細節
裝飾器
裝飾器是Python中的一個重要概念,多用於在不修改原函式的基礎上,為函式增加額外的功能。
基礎裝飾器
例如小李給女朋友買了一款iPhone12作為生日禮物,手機原封未拆封。
def gift():
print('iPhone12')
gift() # 執行顯示禮物資訊
但還是覺得禮物太單薄,於是又買了一盒德芙巧克力,一支dior的口紅,並找了個精美的禮品盒包裝了一下,盒子裡放滿了泡沫球。
def gift(): print('iPhone12') def box(gift): print('='*5 + '禮物盒' + '='*5) print('一盒泡沫球') print('好多巧克力') print('一支dior口紅') return gift gift = box(gift) # 將禮物包裝後作為禮物 gift() # 顯示禮物資訊
執行後顯示如下:
=====禮物盒=====
一盒泡沫球
好多巧克力
一支dior口紅
iPhone12
這個box便是一個裝飾器,它的引數是一個函式物件,同數字、字串、列表、字典等資料型別一樣,函式和類也可以作為函式的引數使用,畢竟在Python里人人平等,一切皆物件。
box在使用時依然返回了原來的gift,只是在拿到這個gift之前增加了兩個額外的驚喜,然後我們把box作為gift使用即可。
裝飾器本質上就是以函式作為引數,對函式做一些處理,並替換原函式的一種高階函式。
上例中,使用裝飾器表示為如下。
def box(gift): # 以函式為引數的裝飾器 print('='*5 + '禮物盒' + '='*5) print('一盒泡沫球') print('好多巧克力') print('一支dior口紅') return gift @box # 掛載裝飾器,會自動替換原函式 def gift(): print('iPhone12') gift() # 這裡面得到的gift實際上是裝飾後的gift即`box(gift)`
執行後顯示和上例相同。
處理函式引數
小李突然想到,買哪個顏色應該徵詢下女友的意見,也就是原來的gift應支援一個可供選擇的顏色引數。
def gift(color):
print(f'iPhone12{color}版')
作為一個細心的boyfriend,小李需要根據對應的手機顏色選擇同樣顏色的泡沫球,也就是需要能獲取到,被裝飾的gift函式的引數。
這時候我們需要在盒子內部(box裝飾器),重新準備一個新的禮物,根據顏色引數做不同的處理,然後根據顏色拿到指定的iPhone12禮物。
def box(gift): print('='*5 + '禮物盒' + '='*5) def new_gift(color): # 準備一個新的禮物,引數和原gift引數一致 print(f'一盒{color}泡沫球') # 根據顏色準備泡沫球 print('好多巧克力') print('一支dior口紅') return gift(color) # 根據顏色拿到指定的iPhone12 return new_gift # 返回新禮物,新禮物呼叫時,增加一些驚喜,並返回原有禮物gift(color)的結果。 @box def gift(color): print(f'iPhone12{color}版') gift('紅色') # 實際上這裡的gift是被box裝飾後狸貓換太子的new_gift函式,而new_gift('紅色'),返回原gift('紅色')的結果。
在box內部為了根據引數做對應處理,我們新建了一個函式,函式內部也可以定義內部函式,內部函式new_gift可以獲取並使用外部box函式的引數,如gift。
為了能獲取到原有函式gift的引數,我們需要建立一個傀儡函式new_gift,這個函式和原函式gift的引數一致、返回結果一致,即new_gift('紅色')返回的就是gift('紅色')。
然後狸貓換太子,不再返回原來的gift函式物件,而是返回替換的new_gift函式物件。
執行後顯示
=====禮物盒=====
一盒紅色泡沫球
好多巧克力
一支dior口紅
iPhone12紅色版
注意:在裝飾器box裡,要返回一個函式物件,如上例中的
return gift
或本例中的return new_gift
。而在傀儡函式new_gift中,為了和原函式gift結果一致,要返回原函式的呼叫結果即gift(color)。
從普遍意義上講,作為商家,為了裝飾器box可以包裝任何形式的禮物,無論禮物有什麼引數都可以滿足,這就要求我們的傀儡函式new_gift支援任意型別的引數即def new_gift(*args, **kwargs)
。
然後把無論什麼引數*args, **kwargs
交由原函式gift(*args, **kwargs)
處理即可。
修改後,我們便得到一個通用的裝飾器,可以包裝任何禮物。
def box(gift):
print('='*5 + '禮物盒' + '='*5)
def new_gift(*args, **kwargs): # 接受任意數量的引數
if args and len(args) > 0: # 由於引數不確定了,我們假設萬一有引數,第一個引數是color引數
color = args[0]
print(f'一盒{color}泡沫球')
else:
print(f'一盒泡沫球')
print('好多巧克力')
print('一支dior口紅')
result = gift(*args, **kwargs) # 如果我們需要對原函式的結果做出處理,可以先獲取到結果
# print(f'原函式結果{result}') 由於原函式gift沒有return,這是其實是None
return result # 返回原函式結果
return new_gift
@box
def gift(color, pro=False): # 新的禮物函式,兩個引數,預設買12,萬一女友要Pro,也可以
if pro is True:
print(f'iPhone12 Pro{color}版')
else:
print(f'iPhone12{color}版')
gift('海藍色', pro=True)
這樣,無論被裝飾的函式有幾個引數,box裝飾器都可以正常處理。
執行後顯示如下。
=====禮物盒=====
一盒海藍色泡沫球
好多巧克力
一支dior口紅
iPhone12 Pro海藍色版
帶參裝飾器
信心滿滿的小李覺得,在盒子上還可以做些文章,要根據女友的喜好選擇不同形狀的箱子,因此我們需要根據引數來定製我們的裝飾器box,在盒子外面再加一層定製函式。
def custom_box(shape): # 根據引數定製裝飾器
def box(gift): # 裝飾器函式
print('='*5 + f'{shape}禮物盒' + '='*5) # 根據形狀定製
# ...
return box # 返回裝飾器函式
此時我們得到一個可以根據引數進行定製的裝飾器函式custom_box,這個裝飾器接收到引數後,傳遞給真實裝飾器box,並返回定製後box裝飾器函式。
完整程式碼如下。
def custom_box(shape): # 根據引數定製裝飾器 =====================
def box(gift): # 實際的裝飾器函式 ---------------------------
print('='*5 + f'{shape}禮物盒' + '='*5)
def new_gift(*args, **kwargs): # 傀儡函式 ..............
if args and len(args) > 0:
color = args[0]
print(f'一盒{color}泡沫球')
else:
print(f'一盒泡沫球')
print('好多巧克力')
print('一支dior口紅')
result = gift(*args, **kwargs)
return result # 返回原函式結果 ......................
return new_gift # 返回傀儡函式 ---------------------------
return box # 返回定製的裝飾器 ===============================
@custom_box('心形') # 使用可定製的裝飾器
def gift(color, pro=False):
if pro is True:
print(f'iPhone12 Pro{color}版')
else:
print(f'iPhone12{color}版')
gift('海藍色', pro=True)
注意:裝飾器在匯入模組時立即計算的,即沒呼叫
gift('海藍色', pro=True)
之前就已經執行生成定製後的box。
執行後,結果如下。
=====心形禮物盒=====
一盒海藍色泡沫球
好多巧克力
一支dior口紅
iPhone12 Pro海藍色版
生成器和迭代器
可迭代物件
實現了__iter__方法, __iter__方法返回一個迭代器
迭代器
按標準的迭代協議實現__iter__和__next__方法,StopIteration結束
class A:
start = 0
def __iter__(self):
return self
def __next__(self):
if self.start > 10:
raise StopIteration
self.start += 1
return self.start
生成器
內部實現了迭代器的一種函式,通過yield記錄當前位置並返回一次迭代結果
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
列表推導式
推倒式:當我們對一批可迭代的資料(如列表或字典)進行提取或處理,最後要得到一個新的列表或字典時,推導式是一種非常簡潔的表達方式。
比如,有一批資料
data = [
{'name': '張三', 'gender': 'male', 'age': 12},
{'name': '李四', 'gender': 'female', 'age': 10},
{'name': '王五', 'gender': 'male', 'age': 20},
{'name': '趙六', 'gender': 'male', 'age': 11},
{'name': '周七', 'gender': 'female', 'age': 16},
{'name': '孫八', 'gender': 'male', 'age': 13},
]
我們想要把資料中的name都提取出來形成一個新的列表,一般的操作是這樣的。
names = [] # 定義一個空列表
for item in data: # 遍歷資料
name = item['name'] # 提取每行中的name
names.append(name) # 追加到列表中
如果用推導式的話,形式如下。
names = [item['name'] for item in data] # 遍歷data,提取每項中的name生成一個新列表
資料處理
在提取資料時,我們還可以對每一項資料進行,處理,假設我們需要每個名稱前加上'姓名: '這個字串,可以這樣。
names = ['姓名: '+item['name'] for item in data]
'姓名: '+item['name']
就是每一項的資料
資料篩選
同樣我們還可以對資料進行篩選,比如我們只要年齡大於12歲,後面可以使用if進行過濾
names = [item['name'] for item in data if item['age']>12]
多重迴圈
推導式還支援多重迴圈,比如
for x in range(1,5)
if x > 2
for y in range(1,4)
if y < 3
x*y
使用推導式表示如下
[x*y for x in range(1,5) if x > 2 for y in range(1,4) if y < 3]
批量執行操作
由於推導式就是一種迴圈操作,我們也可以使用推導式來批量執行一些相似操作,比如:
def step1(driver):
print('步驟1)
def step2(driver):
print('步驟2)
def step3(driver):
print('步驟3)
我們可以將函式名放到一個列表裡,然後使用推導式迴圈執行
steps = [step1, step2, step3] # 函式名列表
[step(driver) for step in steps] # 不需要變數接收,我們只需要它迴圈執行
字典推導式
當我們需要遍歷一批資料最後得到一個字典時,同樣可以使用字典推導式,如:
data = [
{'name': '張三', 'gender': 'male', 'age': 12},
{'name': '李四', 'gender': 'female', 'age': 10},
{'name': '王五', 'gender': 'male', 'age': 20},
{'name': '趙六', 'gender': 'male', 'age': 11},
{'name': '周七', 'gender': 'female', 'age': 16},
{'name': '孫八', 'gender': 'male', 'age': 13},
]
假設我們想得到一個{'張三': 12, '李四': 10, ....}
這樣的一個字典,使用字典推導式方式如下:
persons = {item['name']: item['age'] for item in data}
字典推導式同樣支援if篩選等操作。
生成器
生成器實際上是一種包含初始資料和推導法則的物件,比如我們可以輕鬆的寫出1w以內所有的奇數,原因是因為我只需要記住從1開始每次加2即可。
生成器便是這樣。對應大量的資料或者CSV/Excel檔案中的資料,生成器可以大量的節省記憶體,比如csv.Reader(f)就是一個生成器,只存了當前位置和讀取下一行資料的方法。
當你需要遍歷時,它再每次給你讀取一行資料給你。
如列表推導式的例子,
data = [
{'name': '張三', 'gender': 'male', 'age': 12},
{'name': '李四', 'gender': 'female', 'age': 10},
{'name': '王五', 'gender': 'male', 'age': 20},
{'name': '趙六', 'gender': 'male', 'age': 11},
{'name': '周七', 'gender': 'female', 'age': 16},
{'name': '孫八', 'gender': 'male', 'age': 13},
]
names = [item['name'] for item in data]
我們把列表的中括號改為小括號就得到一個生成器
names2 = (item['name'] for item in data)
注意:生成器和推導式不同,其中的迴圈不是立即執行的,只用你遍歷這個生成器時才會執行
for name in names: # 遍歷列表推導式生成的新列表
print(name)
for name in names2: # 遍歷一個生成器
print(name)
兩個列印結果是一樣的,生成器更節省記憶體,只有遍歷時才執行。
魔術方法
魔術方法 | 描述 |
---|---|
__new__ |
建立類並返回這個類的例項 |
__init__ |
可理解為“建構函式”,在物件初始化的時候呼叫,使用傳入的引數初始化該例項 |
__del__ |
可理解為“解構函式”,當一個物件進行垃圾回收時呼叫 |
__metaclass__ |
定義當前類的元類 |
__class__ |
檢視物件所屬的類 |
__base__ |
獲取當前類的父類 |
__bases__ |
獲取當前類的所有父類 |
__str__ |
定義當前類的例項的文字顯示內容 |
__getattribute__ |
定義屬性被訪問時的行為 |
__getattr__ |
定義試圖訪問一個不存在的屬性時的行為 |
__setattr__ |
定義對屬性進行賦值和修改操作時的行為 |
__delattr__ |
定義刪除屬性時的行為 |
__copy__ |
定義對類的例項呼叫 copy.copy() 獲得物件的一個淺拷貝時所產生的行為 |
__deepcopy__ |
定義對類的例項呼叫 copy.deepcopy() 獲得物件的一個深拷貝時所產生的行為 |
__eq__ |
定義相等符號“==”的行為 |
__ne__ |
定義不等符號“!=”的行為 |
__lt__ |
定義小於符號“<”的行為 |
__gt__ |
定義大於符號“>”的行為 |
__le__ |
定義小於等於符號“<=”的行為 |
__ge__ |
定義大於等於符號“>=”的行為 |
__add__ |
實現操作符“+”表示的加法 |
__sub__ |
實現操作符“-”表示的減法 |
__mul__ |
實現操作符“*”表示的乘法 |
__div__ |
實現操作符“/”表示的除法 |
__mod__ |
實現操作符“%”表示的取模(求餘數) |
__pow__ |
實現操作符“**”表示的指數操作 |
__and__ |
實現按位與操作 |
__or__ |
實現按位或操作 |
__xor__ |
實現按位異或操作 |
__len__ |
用於自定義容器型別,表示容器的長度 |
__getitem__ |
用於自定義容器型別,定義當某一項被訪問時,使用 self[key] 所產生的行為 |
__setitem__ |
用於自定義容器型別,定義執行 self[key]=value 時產生的行為 |
__delitem__ |
用於自定義容器型別,定義一個專案被刪除時的行為 |
__iter__ |
用於自定義容器型別,一個容器迭代器 |
__reversed__ |
用於自定義容器型別,定義當 reversed( ) 被呼叫時的行為 |
__contains__ |
用於自定義容器型別,定義呼叫 in 和 not in 來測試成員是否存在的時候所產生的行為 |
__missing__ |
用於自定義容器型別,定義在容器中找不到 key 時觸發的行為 |