一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - 淺談Python的元編程

淺談Python的元編程

2022-01-06 00:30somenzz Python

提到元這個字,你也許會想到元數據,元數據就是描述數據本身的數據,元類就是類的類,本文的主要目的是向大家介紹這些元編程技術,并且給出實例來演示它們是怎樣定制化源代碼的行為。剛興趣的朋友可以參考一下

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

 

一、裝飾器

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from functools import wraps
 
def timeit(logger=None):
    """
    耗時統計裝飾器,單位是秒,保留 4 位小數
    """
 
    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) 注解是很重要的, 它能保留原始函數的元數據) 只需要在原來的函數上面加上 @timeit() 即可為其增加新的功能:

?
1
2
3
4
5
6
@timeit()
def test_timeit():
    time.sleep(1)
 
test_timeit()
#test_timeit cost 1.0026 seconds

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

?
1
2
test_timeit = timeit(test_timeit)
test_timeit()

 

二、裝飾器的執行順序

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

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

?
1
add = decorator1(decorator2(add))

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

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

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

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

 

三、元類

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 的期間被執行,而 __call__方法會在實例化 Spam 的時候執行。

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

 

四、descriptor 類(描述符類)

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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() 方法以根據需要測試各種約束。

這是三個實用的數據驗證工具:

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

?
1
2
3
4
5
6
7
8
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。根據可選參數,它還可以驗證值在給定的最小值或最大值之間。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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。根據可選參數,它可以驗證給定的最小或最大長度。它還可以驗證用戶定義的 predicate

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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}'
            )

實際應用時這樣寫:

?
1
2
3
4
5
6
7
8
9
10
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

描述器阻止無效實例的創建:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> 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 的元編程,總結如下:

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

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

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

到此這篇關于淺談Python的元編程的文章就介紹到這了,更多相關Python的元編程內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.tuicool.com/articles/MVbiQv2

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 男生的j桶女人屁免费视频 男生操男生 | 明星乱淫 | 精品久久久久久亚洲精品 | 经典三级四虎在线观看 | 欧美人禽杂交狂配无删完整 | 午夜精品网 | 禁漫H天堂免费A漫 | 91精品国产综合久久消防器材 | 亚洲欧美国产另类 | 午夜视频网站 | 国产真实一区二区三区 | 91极品在线观看 | 91精品国产9l久久久久 | 好大用力深一点女公交车 | 免费高清资源黄网站在线观看 | 欧美x×x | 欧美日韩高清完整版在线观看免费 | 亚洲国产情侣一区二区三区 | 国产不卡视频一区二区在线观看 | 456成人免费高清视频 | 男人疯狂进女人下部视频动漫 | 国产欧美在线播放 | 美女撒尿部位无遮挡 | 亚洲国产在线视频中文字 | 亚洲精品6久久久久中文字幕 | 国产精品高清视亚洲一区二区 | poren黑人| 第一福利在线视频 | 亚洲欧美日韩中文字幕网址 | a毛片免费全部在线播放毛 a级在线看 | 扒开胸流出吃奶 | 红杏劫 | 农村老少伦小说 | 男人捅女人的鸡鸡 | 1313午夜精品久久午夜片 | 91av最新地址 | poren黑人| 好逼365| 调教老师肉色丝袜的故事 | 网友偷自拍原创区 | 久久受www免费人成_看片中文 |