成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

說說Python的元編程

開發 后端
提到元這個字,你也許會想到元數據,元數據就是描述數據本身的數據,元類就是類的類,相應的元編程就是描述代碼本身的代碼,元編程就是關于創建操作源代碼(比如修改、生成或包裝原來的代碼)的函數和類。

[[423992]]

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

裝飾器

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

  1. from functools import wraps 
  2.  
  3. def timeit(logger=None): 
  4.     ""
  5.     耗時統計裝飾器,單位是秒,保留 4 位小數 
  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) 注解是很重要的, 它能保留原始函數的元數據) 只需要在原來的函數上面加上 @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() 

裝飾器的執行順序

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

假如有這樣的代碼,請問是先打印 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 

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

  1. add = decorator1(decorator2(add)) 

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

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

  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 

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

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

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

元類

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

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

  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__ 方法會在定義 Spam 的期間被執行,而 __call__方法會在實例化 Spam 的時候執行。

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

descriptor 類(描述符類)

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

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

  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 

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

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

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

  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 驗證值是否為 int 或 float。根據可選參數,它還可以驗證值在給定的最小值或最大值之間。

  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, (intfloat)): 
  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 驗證值是否為 str。根據可選參數,它可以驗證給定的最小或最大長度。它還可以驗證用戶定義的 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.             ) 

實際應用時這樣寫:

  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 

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

  1. >>> Component('Widget''metal', 5)      # Blocked: 'Widget' is not all uppercase 
  2. Traceback (most recent call last): 
  3.     ... 
  4. ValueError: Expected <method 'isupper' of 'str' objects> 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 

最后的話

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

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

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

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

 

責任編輯:武曉燕 來源: Python七號
相關推薦

2024-07-30 14:46:55

2024-11-14 09:00:00

Python編程元編程

2021-03-09 23:12:51

Python集合項目

2011-07-05 17:19:47

元編程

2024-12-12 08:05:14

元類Python控制類

2016-11-01 09:18:33

Python閉包

2011-09-05 17:18:28

2011-05-26 13:43:30

MongoDB

2021-05-31 20:06:57

網元協議網關

2023-10-27 08:33:40

Go語言元編程

2020-05-06 16:47:08

線程安全Python數據安全

2020-05-07 10:05:52

Python數據安全

2021-01-06 10:09:38

MySQL

2011-08-30 10:22:14

MongoDB

2020-12-22 09:32:36

JavaScripMixin mixins

2021-07-16 10:32:33

前端元編程代碼

2016-10-25 14:27:32

metaclasspython

2013-12-20 11:02:08

池建強

2018-03-14 08:33:33

C++元編程云成本

2011-08-18 14:09:35

NoSQL
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩激情一区 | 亚洲码欧美码一区二区三区 | 亚洲精品一区在线 | 日韩精品成人 | 最新中文字幕在线播放 | 亚洲精品久久久一区二区三区 | 欧美高清成人 | 日韩在线中文 | 一区二区三区免费 | 国产精品亚洲视频 | 亚洲在线日韩 | 欧美日韩国产一区二区 | 不卡在线视频 | 天堂一区二区三区四区 | www.亚洲视频.com | 亚洲精品日韩在线 | 一级片在线视频 | 国产sm主人调教女m视频 | 欧美视频网 | 日韩欧美在线观看视频 | 噜啊噜在线 | 91免费在线播放 | 狠狠入ady亚洲精品经典电影 | 欧美福利视频 | 在线观看久草 | 成人福利视频网站 | 亚洲一区二区三区免费在线 | 欧洲一区二区在线 | 日本五月婷婷 | 日韩成人精品在线 | 久久久久国产精品一区 | 久久综合成人精品亚洲另类欧美 | 欧美成人a∨高清免费观看 色999日韩 | 蜜桃精品视频在线 | 一级毛片视频在线 | 欧美性极品xxxx做受 | 国产成人99av超碰超爽 | 一区二区精品在线 | 黄在线免费观看 | 5060网一级毛片| 国产一区二区三区在线看 |