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

說說Python的元編程

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

[[423992]]

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

裝飾器

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

  1. from functools import wraps 
  2.  
  3. def timeit(logger=None): 
  4.     ""
  5.     耗時統(tǒng)計裝飾器,單位是秒,保留 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í)行順序

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

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

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

  1. add = decorator1(decorator2(add)) 

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

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

  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ù),也可以定義為類,只要你確保它實現(xiàn)了__call__() 和 __get__() 方法。

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

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

元類

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

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

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

如果想更好的理解元類,可以閱讀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() 方法以根據(jù)需要測試各種約束。

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

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。根據(jù)可選參數(shù),它還可以驗證值在給定的最小值或最大值之間。

  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。根據(jù)可選參數(shù),它可以驗證給定的最小或最大長度。它還可以驗證用戶定義的 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 

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

  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 的元編程,總結如下:

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

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

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

 

責任編輯:武曉燕 來源: 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

網(wǎng)元協(xié)議網(wǎng)關

2023-10-27 08:33:40

Go語言元編程

2020-05-06 16:47:08

線程安全Python數(shù)據(jù)安全

2020-05-07 10:05:52

Python數(shù)據(jù)安全

2021-01-06 10:09:38

MySQL

2011-08-30 10:22:14

MongoDB

2021-07-16 10:32:33

前端元編程代碼

2020-12-22 09:32:36

JavaScripMixin mixins

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技術棧公眾號

主站蜘蛛池模板: 亚洲国产电影 | 奇米超碰| 成年人在线 | 青青草网 | 无码一区二区三区视频 | 青青草网 | 超碰97免费观看 | 精品日韩欧美一区二区 | 两性午夜视频 | 秋霞国产 | 九一视频在线播放 | 黄视频网站在线 | 91视频大全| 找个黄色片| 国产精品永久免费视频 | 亚洲精品一区二区三区四区高清 | 国产精品久久久爽爽爽麻豆色哟哟 | 国产第一页在线播放 | 超碰97人人人人人蜜桃 | 美女天天干 | 欧美第一页 | 欧美日韩国产精品 | 色免费看 | 91精品一区二区三区久久久久 | 久久小视频 | 国产乱码精品一区二区三区中文 | 日韩福利在线 | 九九国产| 欧洲av在线 | 日韩三级在线 | 欧美一级一| 欧美三级三级三级爽爽爽 | 中文字幕日韩欧美一区二区三区 | 牛牛热在线视频 | 成人av一区二区三区 | 亚洲成人三级 | 欧美视频1区 | 国产欧美综合在线 | www.色综合 | 久久新 | 欧美一区二区三区,视频 |