日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
說說Python的元編程

提到元這個(gè)字,你也許會(huì)想到元數(shù)據(jù),元數(shù)據(jù)就是描述數(shù)據(jù)本身的數(shù)據(jù),元類就是類的類,相應(yīng)的元編程就是描述代碼本身的代碼,元編程就是關(guān)于創(chuàng)建操作源代碼(比如修改、生成或包裝原來的代碼)的函數(shù)和類。主要技術(shù)是使用裝飾器、元類、描述符類。本文的主要目的是向大家介紹這些元編程技術(shù),并且給出實(shí)例來演示它們是怎樣定制化源代碼的行為。

創(chuàng)新互聯(lián)公司主要從事成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)平橋,十年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220

裝飾器

裝飾器就是函數(shù)的函數(shù),它接受一個(gè)函數(shù)作為參數(shù)并返回一個(gè)新的函數(shù),在不改變?cè)瓉砗瘮?shù)代碼的情況下為其增加新的功能,比如最常用的計(jì)時(shí)裝飾器:

 
 
 
  1. from functools import wraps 
  2.  
  3. def timeit(logger=None): 
  4.     """ 
  5.     耗時(shí)統(tǒng)計(jì)裝飾器,單位是秒,保留 4 位小數(shù) 
  6.     """ 
  7.  
  8.     def decorator(func): 
  9.         @wraps(func) 
  10.         def wrapper(*args, **kwargs): 
  11.             start = time.time() 
  12.             result = func(*args, **kwargs) 
  13.             end = time.time() 
  14.             if logger: 
  15.                 logger.info(f"{func.__name__} cost {end - start :.4f} seconds") 
  16.             else: 
  17.                 print(f"{func.__name__} cost {end - start :.4f} seconds") 
  18.             return result 
  19.  
  20.         return wrapper 
  21.  
  22.     return decorator 

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

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

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

 
 
 
  1. test_timeit = timeit(test_timeit) 
  2. test_timeit() 

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

當(dāng)有多個(gè)裝飾器的時(shí)候,他們的調(diào)用順序是怎么樣的?

假如有這樣的代碼,請(qǐng)問是先打印 Decorator1 還是 Decorator2 ?

 
 
 
  1. from functools import wraps 
  2.  
  3. def decorator1(func): 
  4.     @wraps(func) 
  5.     def wrapper(*args, **kwargs): 
  6.         print('Decorator 1') 
  7.         return func(*args, **kwargs) 
  8.     return wrapper 
  9.  
  10. def decorator2(func): 
  11.     @wraps(func) 
  12.     def wrapper(*args, **kwargs): 
  13.         print('Decorator 2') 
  14.         return func(*args, **kwargs) 
  15.     return wrapper 
  16.  
  17. @decorator1 
  18. @decorator2 
  19. def add(x, y): 
  20.     return x + y 
  21.  
  22. add(1,2) 
  23.  
  24. # Decorator 1 
  25. # Decorator 2 

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

 
 
 
  1. add = decorator1(decorator2(add)) 

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

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

 
 
 
  1. def decorator1(func): 
  2.     @wraps(func) 
  3.     def wrapper(*args, **kwargs): 
  4.         result = func(*args, **kwargs) 
  5.         print('Decorator 1') 
  6.         return result 
  7.     return wrapper 
  8.  
  9. def decorator2(func): 
  10.     @wraps(func) 
  11.     def wrapper(*args, **kwargs): 
  12.         result = func(*args, **kwargs) 
  13.         print('Decorator 2') 
  14.         return result 
  15.     return wrapper 

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

關(guān)于裝飾器的其他用法,可以參考前文:

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

元類

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

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

 
 
 
  1. class Singleton(type): 
  2.     def __init__(self, *args, **kwargs): 
  3.         self._instance = None 
  4.         super().__init__(*args, **kwargs) 
  5.  
  6.     def __call__(self, *args, **kwargs): 
  7.         if self._instance is None: 
  8.             self._instance = super().__call__(*args, **kwargs) 
  9.             return self._instance 
  10.         else: 
  11.             return self._instance 
  12.  
  13.  
  14. class Spam(metaclass=Singleton): 
  15.     def __init__(self): 
  16.         print("Spam!!!") 

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

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

descriptor 類(描述符類)

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

定義驗(yàn)證器類,它是一個(gè)描述符類,同時(shí)還是一個(gè)抽象類:

 
 
 
  1. from abc import ABC, abstractmethod 
  2.  
  3. class Validator(ABC): 
  4.  
  5.     def __set_name__(self, owner, name): 
  6.         self.private_name = '_' + name 
  7.  
  8.     def __get__(self, obj, objtype=None): 
  9.         return getattr(obj, self.private_name) 
  10.  
  11.     def __set__(self, obj, value): 
  12.         self.validate(value) 
  13.         setattr(obj, self.private_name, value) 
  14.  
  15.     @abstractmethod 
  16.     def validate(self, value): 
  17.         pass 

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

這是三個(gè)實(shí)用的數(shù)據(jù)驗(yàn)證工具:

OneOf 驗(yàn)證值是一組受約束的選項(xiàng)之一。

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

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

 
 
 
  1. class Number(Validator): 
  2.  
  3.     def __init__(self, minvalue=None, maxvalue=None): 
  4.         self.minvalue = minvalue 
  5.         self.maxvalue = maxvalue 
  6.  
  7.     def validate(self, value): 
  8.         if not isinstance(value, (int, float)): 
  9.             raise TypeError(f'Expected {value!r} to be an int or float') 
  10.         if self.minvalue is not None and value < self.minvalue: 
  11.             raise ValueError( 
  12.                 f'Expected {value!r} to be at least {self.minvalue!r}' 
  13.             ) 
  14.         if self.maxvalue is not None and value > self.maxvalue: 
  15.             raise ValueError( 
  16.                 f'Expected {value!r} to be no more than {self.maxvalue!r}' 
  17.             ) 

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

 
 
 
  1. class String(Validator): 
  2.  
  3.     def __init__(self, minsize=None, maxsize=None, predicate=None): 
  4.         self.minsize = minsize 
  5.         self.maxsize = maxsize 
  6.         self.predicate = predicate 
  7.  
  8.     def validate(self, value): 
  9.         if not isinstance(value, str): 
  10.             raise TypeError(f'Expected {value!r} to be an str') 
  11.         if self.minsize is not None and len(value) < self.minsize: 
  12.             raise ValueError( 
  13.                 f'Expected {value!r} to be no smaller than {self.minsize!r}' 
  14.             ) 
  15.         if self.maxsize is not None and len(value) > self.maxsize: 
  16.             raise ValueError( 
  17.                 f'Expected {value!r} to be no bigger than {self.maxsize!r}' 
  18.             ) 
  19.         if self.predicate is not None and not self.predicate(value): 
  20.             raise ValueError( 
  21.                 f'Expected {self.predicate} to be true for {value!r}' 
  22.             ) 

實(shí)際應(yīng)用時(shí)這樣寫:

 
 
 
  1. class Component: 
  2.  
  3.     name = String(minsize=3, maxsize=10, predicate=str.isupper) 
  4.     kind = OneOf('wood', 'metal', 'plastic') 
  5.     quantity = Number(minvalue=0) 
  6.  
  7.     def __init__(self, name, kind, quantity): 
  8.         self.name = name 
  9.         self.kind = kind 
  10.         self.quantity = quantity 

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

 
 
 
  1. >>> Component('Widget', 'metal', 5)      # Blocked: 'Widget' is not all uppercase 
  2. Traceback (most recent call last): 
  3.     ... 
  4. ValueError: Expected  to be true for 'Widget' 
  5.  
  6. >>> Component('WIDGET', 'metle', 5)      # Blocked: 'metle' is misspelled 
  7. Traceback (most recent call last): 
  8.     ... 
  9. ValueError: Expected 'metle' to be one of {'metal', 'plastic', 'wood'} 
  10.  
  11. >>> Component('WIDGET', 'metal', -5)     # Blocked: -5 is negative 
  12. Traceback (most recent call last): 
  13.     ... 
  14. ValueError: Expected -5 to be at least 0 
  15. >>> Component('WIDGET', 'metal', 'V')    # Blocked: 'V' isn't a number 
  16. Traceback (most recent call last): 
  17.     ... 
  18. TypeError: Expected 'V' to be an int or float 
  19.  
  20. >>> c = Component('WIDGET', 'metal', 5)  # Allowed:  The inputs are valid 

最后的話

關(guān)于 Python 的元編程,總結(jié)如下:

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

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

如果希望實(shí)例的屬性擁有某些共同的特點(diǎn),就可以自定義一個(gè)描述符類。


文章標(biāo)題:說說Python的元編程
URL網(wǎng)址:http://www.dlmjj.cn/article/codciji.html