1. 程式人生 > 遊戲資訊 >【遊戲王】效果無效和攻守變化類效果的一個細節

【遊戲王】效果無效和攻守變化類效果的一個細節

裝飾器

裝飾器是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 時觸發的行為