欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Python的元編程

 更新時間:2021年09月15日 10:16:24   作者:somenzz  
提到元這個字,你也許會想到元數(shù)據(jù),元數(shù)據(jù)就是描述數(shù)據(jù)本身的數(shù)據(jù),元類就是類的類,本文的主要目的是向大家介紹這些元編程技術,并且給出實例來演示它們是怎樣定制化源代碼的行為。剛興趣的朋友可以參考一下

相應的元編程就是描述代碼本身的代碼,元編程就是關于創(chuàng)建操作源代碼(比如修改、生成或包裝原來的代碼)的函數(shù)和類。主要技術是使用裝飾器、元類、描述符類。

一、裝飾器

裝飾器就是函數(shù)的函數(shù),它接受一個函數(shù)作為參數(shù)并返回一個新的函數(shù),在不改變原來函數(shù)代碼的情況下為其增加新的功能,比如最常用的計時裝飾器:

from functools import wraps 
 
def timeit(logger=None): 
    """ 
    耗時統(tǒng)計裝飾器,單位是秒,保留 4 位小數(shù) 
    """ 
 
    def decorator(func): 
        @wraps(func) 
        def wrapper(*args, **kwargs): 
            start = time.time() 
            result = func(*args, **kwargs) 
            end = time.time() 
            if logger: 
                logger.info(f"{func.__name__} cost {end - start :.4f} seconds") 
            else: 
                print(f"{func.__name__} cost {end - start :.4f} seconds") 
            return result 
 
        return wrapper 
 
    return decorator 


(注:比如上面使用 @wraps(func) 注解是很重要的, 它能保留原始函數(shù)的元數(shù)據(jù)) 只需要在原來的函數(shù)上面加上 @timeit() 即可為其增加新的功能:

@timeit() 
def test_timeit(): 
    time.sleep(1) 
 
test_timeit() 
#test_timeit cost 1.0026 seconds 


上面的代碼跟下面這樣寫的效果是一樣的:

test_timeit = timeit(test_timeit) 
test_timeit() 

二、裝飾器的執(zhí)行順序

當有多個裝飾器的時候,他們的調(diào)用順序是怎么樣的?

假如有這樣的代碼,請問是先打印 Decorator1 還是 Decorator2 ?

from functools import wraps 
 
def decorator1(func): 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
        print('Decorator 1') 
        return func(*args, **kwargs) 
    return wrapper 
 
def decorator2(func): 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
        print('Decorator 2') 
        return func(*args, **kwargs) 
    return wrapper 
 
@decorator1 
@decorator2 
def add(x, y): 
    return x + y 
 
add(1,2) 
 
# Decorator 1 
# Decorator 2 


回答這個問題之前,我先給你打個形象的比喻,裝飾器就像函數(shù)在穿衣服,離它最近的最先穿,離得遠的最后穿,上例中 decorator1 是外套,decorator2 是內(nèi)衣。

add = decorator1(decorator2(add)) 


在調(diào)用函數(shù)的時候,就像脫衣服,先解除最外面的 decorator1,也就是先打印 Decorator1,執(zhí)行到 return func(*args, **kwargs) 的時候會去解除 decorator2,然后打印 Decorator2,再次執(zhí)行到 return func(*args, **kwargs) 時會真正執(zhí)行 add() 函數(shù)。

需要注意的是打印的位置,如果打印字符串的代碼位于調(diào)用函數(shù)之后,像下面這樣,那輸出的結果正好相反:

def decorator1(func): 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
        result = func(*args, **kwargs) 
        print('Decorator 1') 
        return result 
    return wrapper 
 
def decorator2(func): 
    @wraps(func) 
    def wrapper(*args, **kwargs): 
        result = func(*args, **kwargs) 
        print('Decorator 2') 
        return result 
    return wrapper 


裝飾器不僅可以定義為函數(shù),也可以定義為類,只要你確保它實現(xiàn)了__call__() __get__() 方法。

關于裝飾器的其他用法,可以參考前文:

  • 我是裝飾器
  • 再談裝飾器

三、元類

Python 中所有類(object)的元類,就是 type 類,也就是說 Python 類的創(chuàng)建行為由默認的 type 類控制,打個比喻,type 類是所有類的祖先。我們可以通過編程的方式來實現(xiàn)自定義的一些對象創(chuàng)建行為。

定一個類繼承 type 類 A,然后讓其他類的元類指向 A,就可以控制 A 的創(chuàng)建行為。典型的就是使用元類實現(xiàn)一個單例:

class Singleton(type): 
    def __init__(self, *args, **kwargs): 
        self._instance = None 
        super().__init__(*args, **kwargs) 
 
    def __call__(self, *args, **kwargs): 
        if self._instance is None: 
            self._instance = super().__call__(*args, **kwargs) 
            return self._instance 
        else: 
            return self._instance 
 
 
class Spam(metaclass=Singleton): 
    def __init__(self): 
        print("Spam!!!") 


元類 Singleton __init____new__ 方法會在定義 Spam 的期間被執(zhí)行,而 __call__方法會在實例化 Spam 的時候執(zhí)行。

如果想更好的理解元類,可以閱讀Python黑魔法之metaclass詳情

四、descriptor 類(描述符類)

descriptor 就是任何一個定義了 __get__(),__set__() __delete__()的對象,描述器讓對象能夠自定義屬性查找、存儲和刪除的操作。這里舉官方文檔[1]一個自定義驗證器的例子。

定義驗證器類,它是一個描述符類,同時還是一個抽象類:

from abc import ABC, abstractmethod 
 
class Validator(ABC): 
 
    def __set_name__(self, owner, name): 
        self.private_name = '_' + name 
 
    def __get__(self, obj, objtype=None): 
        return getattr(obj, self.private_name) 
 
    def __set__(self, obj, value): 
        self.validate(value) 
        setattr(obj, self.private_name, value) 
 
    @abstractmethod 
    def validate(self, value): 
        pass 


自定義驗證器需要從 Validator 繼承,并且必須提供 validate() 方法以根據(jù)需要測試各種約束。

這是三個實用的數(shù)據(jù)驗證工具:

OneOf 驗證值是一組受約束的選項之一

class OneOf(Validator): 
 
    def __init__(self, *options): 
        self.options = set(options) 
 
    def validate(self, value): 
        if value not in self.options: 
            raise ValueError(f'Expected {value!r} to be one of {self.options!r}') 


Number 驗證值是否為 int 或 float。根據(jù)可選參數(shù),它還可以驗證值在給定的最小值或最大值之間。

class Number(Validator): 
 
    def __init__(self, minvalue=None, maxvalue=None): 
        self.minvalue = minvalue 
        self.maxvalue = maxvalue 
 
    def validate(self, value): 
        if not isinstance(value, (int, float)): 
            raise TypeError(f'Expected {value!r} to be an int or float') 
        if self.minvalue is not None and value < self.minvalue: 
            raise ValueError( 
                f'Expected {value!r} to be at least {self.minvalue!r}' 
            ) 
        if self.maxvalue is not None and value > self.maxvalue: 
            raise ValueError( 
                f'Expected {value!r} to be no more than {self.maxvalue!r}' 
            ) 


String 驗證值是否為 str。根據(jù)可選參數(shù),它可以驗證給定的最小或最大長度。它還可以驗證用戶定義的 predicate。

class String(Validator): 
 
    def __init__(self, minsize=None, maxsize=None, predicate=None): 
        self.minsize = minsize 
        self.maxsize = maxsize 
        self.predicate = predicate 
 
    def validate(self, value): 
        if not isinstance(value, str): 
            raise TypeError(f'Expected {value!r} to be an str') 
        if self.minsize is not None and len(value) < self.minsize: 
            raise ValueError( 
                f'Expected {value!r} to be no smaller than {self.minsize!r}' 
            ) 
        if self.maxsize is not None and len(value) > self.maxsize: 
            raise ValueError( 
                f'Expected {value!r} to be no bigger than {self.maxsize!r}' 
            ) 
        if self.predicate is not None and not self.predicate(value): 
            raise ValueError( 
                f'Expected {self.predicate} to be true for {value!r}' 
            ) 


實際應用時這樣寫:

class Component: 
 
    name = String(minsize=3, maxsize=10, predicate=str.isupper) 
    kind = OneOf('wood', 'metal', 'plastic') 
    quantity = Number(minvalue=0) 
 
    def __init__(self, name, kind, quantity): 
        self.name = name 
        self.kind = kind 
        self.quantity = quantity 


描述器阻止無效實例的創(chuàng)建:

>>> Component('Widget', 'metal', 5)      # Blocked: 'Widget' is not all uppercase 
Traceback (most recent call last): 
    ... 
ValueError: Expected <method 'isupper' of 'str' objects> to be true for 'Widget' 
 
>>> Component('WIDGET', 'metle', 5)      # Blocked: 'metle' is misspelled 
Traceback (most recent call last): 
    ... 
ValueError: Expected 'metle' to be one of {'metal', 'plastic', 'wood'} 
 
>>> Component('WIDGET', 'metal', -5)     # Blocked: -5 is negative 
Traceback (most recent call last): 
    ... 
ValueError: Expected -5 to be at least 0 
>>> Component('WIDGET', 'metal', 'V')    # Blocked: 'V' isn't a number 
Traceback (most recent call last): 
    ... 
TypeError: Expected 'V' to be an int or float 
 
>>> c = Component('WIDGET', 'metal', 5)  # Allowed:  The inputs are valid 

五、總結

關于 Python 的元編程,總結如下:

如果希望某些函數(shù)擁有相同的功能,希望不改變原有的調(diào)用方式、不寫重復代碼、易維護,可以使用裝飾器來實現(xiàn)。

如果希望某一些類擁有某些相同的特性,或者在類定義實現(xiàn)對其的控制,我們可以自定義一個元類,然后讓它類的元類指向該類。

如果希望實例的屬性擁有某些共同的特點,就可以自定義一個描述符類。

到此這篇關于淺談Python的元編程的文章就介紹到這了,更多相關Python的元編程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • python實現(xiàn)兩個經(jīng)緯度點之間的距離和方位角的方法

    python實現(xiàn)兩個經(jīng)緯度點之間的距離和方位角的方法

    今天小編就為大家分享一篇python實現(xiàn)兩個經(jīng)緯度點之間的距離和方位角的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • Pandas格式化DataFrame的浮點數(shù)列的實現(xiàn)

    Pandas格式化DataFrame的浮點數(shù)列的實現(xiàn)

    本文主要介紹了Pandas格式化DataFrame的浮點數(shù)列的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-05-05
  • Python?查看數(shù)據(jù)類型與格式

    Python?查看數(shù)據(jù)類型與格式

    這篇文章主要介紹了Python?查看數(shù)據(jù)類型與格式方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • Python編程中用close()方法關閉文件的教程

    Python編程中用close()方法關閉文件的教程

    這篇文章主要介紹了Python編程中用close()方法關閉文件的教程,是Python編程入門中的基礎知識,需要的朋友可以參考下
    2015-05-05
  • python繪圖坐橫坐標軸顯示方式

    python繪圖坐橫坐標軸顯示方式

    這篇文章主要介紹了python繪圖坐橫坐標軸顯示方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Python+OpenCV實現(xiàn)車牌字符分割和識別

    Python+OpenCV實現(xiàn)車牌字符分割和識別

    這篇文章主要為大家詳細介紹了Python+OpenCV實現(xiàn)車牌字符分割和識別,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • python使用turtle庫繪制時鐘

    python使用turtle庫繪制時鐘

    這篇文章主要為大家詳細介紹了python使用turtle庫繪制時鐘,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • PyTorch中torch.tensor與torch.Tensor的區(qū)別詳解

    PyTorch中torch.tensor與torch.Tensor的區(qū)別詳解

    這篇文章主要介紹了PyTorch中torch.tensor與torch.Tensor的區(qū)別詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • python常用數(shù)據(jù)重復項處理方法

    python常用數(shù)據(jù)重復項處理方法

    在本篇文章里小編給大家整理的是關于python常用數(shù)據(jù)重復項處理方法,需要的朋友們參考下。
    2019-11-11
  • python pytest進階之conftest.py詳解

    python pytest進階之conftest.py詳解

    這篇文章主要介紹了python pytest進階之conftest.py詳解,如果我們在編寫測試用的時候,每一個測試文件里面的用例都需要先登錄后才能完成后面的操作,那么們該如何實現(xiàn)呢?這就需要我們掌握conftest.py文件的使用了,需要的朋友可以參考下
    2019-06-06

最新評論