亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

在Python 3中實現(xiàn)類型檢查器的簡單方法

系統(tǒng) 1741 0

示例函數(shù)

為了開發(fā)類型檢查器,我們需要一個簡單的函數(shù)對其進行實驗。歐幾里得算法就是一個完美的例子:
?

            
def gcd(a, b):
  
'''Return the greatest common divisor of a and b.'''
  a = abs(a)
  b = abs(b)
  if a < b:
    a, b = b, a
  while b != 0:
    a, b = b, a % b
  return a

          

在上面的示例中,參數(shù) a 和 b 以及返回值應該是 int 類型的。預期的類型將會以函數(shù)注解的形式來表達,函數(shù)注解是 Python 3 的一個新特性。接下來,類型檢查機制將會以一個裝飾器的形式實現(xiàn),注解版本的第一行代碼是:
?

            
def gcd(a: int, b: int) -> int:

          

使用“gcd.__annotations__”可以獲得一個包含注解的字典:
?

            
>>> gcd.__annotations__
{'return': 
            
              , 'b': 
              
                , 'a': 
                
                  }
>>> gcd.__annotations__['a']

                  
                  
                
              
            
          

需要注意的是,返回值的注解存儲在鍵“return”下。這是有可能的,因為“return”是一個關鍵字,所以不能用作一個有效的參數(shù)名。
檢查返回值類型

返回值注解存儲在字典“__annotations__”中的“return”鍵下。我們將使用這個值來檢查返回值(假設注解存在)。我們將參數(shù)傳遞給原始函數(shù),如果存在注解,我們將通過注解中的值來驗證其類型:
?

            
def typecheck(f):
  def wrapper(*args, **kwargs):
    result = f(*args, **kwargs)
    return_type = f.__annotations__.get('return', None)
    if return_type and not isinstance(result, return_type):
      raise RuntimeError("{} should return {}".format(f.__name__, return_type.__name__))
    return result
  return wrapper
          

我們可以用“a”替換函數(shù)gcd的返回值來測試上面的代碼:

            
 
Traceback (most recent call last):
 File "typechecker.py", line 9, in 
            
              
  gcd(1, 2)
 File "typechecker.py", line 5, in wrapper
  raise RuntimeError("{} should return {}".format(f.__name__, return_type.__name__))
RuntimeError: gcd should return int

            
          

由上面的結果可知,確實檢查了返回值的類型。
檢查參數(shù)類型

函數(shù)的參數(shù)存在于關聯(lián)代碼對象的“co_varnames”屬性中,在我們的例子中是“gcd.__code__.co_varnames”。元組包含了所有局部變量的名稱,并且該元組以參數(shù)開始,參數(shù)數(shù)量存儲在“co_nlocals”中。我們需要遍歷包括索引在內的所有變量,并從參數(shù)“args”中獲取參數(shù)值,最后對其進行類型檢查。

得到了下面的代碼:
?

            
def typecheck(f):
  def wrapper(*args, **kwargs):
    for i, arg in enumerate(args[:f.__code__.co_nlocals]):
      name = f.__code__.co_varnames[i]
      expected_type = f.__annotations__.get(name, None)
      if expected_type and not isinstance(arg, expected_type):
        raise RuntimeError("{} should be of type {}; {} specified".format(name, expected_type.__name__, type(arg).__name__))
    result = f(*args, **kwargs)
    return_type = f.__annotations__.get('return', None)
    if return_type and not isinstance(result, return_type):
      raise RuntimeError("{} should return {}".format(f.__name__, return_type.__name__))
    return result
  return wrapper

          

在上面的循環(huán)中,i是數(shù)組args中參數(shù)的以0起始的索引,arg是包含其值的字符串。可以利用“f.__code__.co_varnames[i]”讀取到參數(shù)的名稱。類型檢查代碼與返回值類型檢查完全一樣(包括錯誤消息的異常)。

為了對關鍵字參數(shù)進行類型檢查,我們需要遍歷參數(shù)kwargs。此時的類型檢查幾乎與第一個循環(huán)中相同:
?

            
for name, arg in kwargs.items():
  expected_type = f.__annotations__.get(name, None)
  if expected_type and not isinstance(arg, expected_type):
    raise RuntimeError("{} should be of type {}; {} specified".format(name, expected_type.__name__, type(arg).__name__))

          

得到的裝飾器代碼如下:
?

            
def typecheck(f):
  def wrapper(*args, **kwargs):
    for i, arg in enumerate(args[:f.__code__.co_nlocals]):
      name = f.__code__.co_varnames[i]
      expected_type = f.__annotations__.get(name, None)
      if expected_type and not isinstance(arg, expected_type):
        raise RuntimeError("{} should be of type {}; {} specified".format(name, expected_type.__name__, type(arg).__name__))
    for name, arg in kwargs.items():
      expected_type = f.__annotations__.get(name, None)
      if expected_type and not isinstance(arg, expected_type):
        raise RuntimeError("{} should be of type {}; {} specified".format(name, expected_type.__name__, type(arg).__name__))
    result = f(*args, **kwargs)
    return_type = f.__annotations__.get('return', None)
    if return_type and not isinstance(result, return_type):
      raise RuntimeError("{} should return {}".format(f.__name__, return_type.__name__))
    return result
  return wrapper

          

將類型檢查代碼寫成一個函數(shù)將會使代碼更加清晰。為了簡化代碼,我們修改錯誤信息,而當返回值是無效的類型時,將會使用到這些錯誤信息。我們也可以利用 functools 模塊中的 wraps 方法,將包裝函數(shù)的一些屬性復制到 wrapper 中(這使得 wrapper 看起來更像原來的函數(shù)):
?

            
def typecheck(f):
  def do_typecheck(name, arg):
    expected_type = f.__annotations__.get(name, None)
    if expected_type and not isinstance(arg, expected_type):
      raise RuntimeError("{} should be of type {} instead of {}".format(name, expected_type.__name__, type(arg).__name__))
 
  @functools.wraps(f)
  def wrapper(*args, **kwargs):
    for i, arg in enumerate(args[:f.__code__.co_nlocals]):
      do_typecheck(f.__code__.co_varnames[i], arg)
    for name, arg in kwargs.items():
      do_typecheck(name, arg)
 
    result = f(*args, **kwargs)
 
    do_typecheck('return', result)
    return result
  return wrapper

          

結論

注解是 Python 3 中的一個新元素,本文例子中的使用方法很普通,你也可以想象很多特定領域的應用。雖然上面的實現(xiàn)代碼并不能滿足實際產品要求,但它的目的本來就是用作概念驗證。可以對其進行以下改善:

  • ??? 處理額外的參數(shù)( args 中意想不到的項目)
  • ??? 默認值類型檢查
  • ??? 支持多個類型
  • ??? 支持模板類型(例如,int 型列表)


更多文章、技術交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 成年女人毛片免费视频 | 欧美成人一区亚洲一区 | 久久99国产精品视频 | 东京干手机福利视频 | 国产香蕉在线视频 | 五月天婷婷网址 | 日韩毛片 | 日韩欧美在线观看成人 | 久久精品国产麻豆不卡 | 欧美国产日韩911在线观看 | 中国一级特黄真人毛片免 | 深夜在线观看 | 国产最新在线视频 | 国产精品网站 夜色 | 国产精品香蕉在线一区 | 羞羞网站视频 | 欧美精品影院 | 成人在线免费观看视频 | 久久午夜国产片 | 亚洲国产婷婷香蕉久久久久久 | 亚洲国产成人久久精品动漫 | 天天射影院 | 中文字幕免费视频 | 久久久99精品久久久 | 婷婷在线网 | 亚洲免费国产 | 夜夜撸日日干 | 国产精品久久国产三级国电话系列 | 亚洲精品欧美精品一区二区 | 福利视频免费 | 成人四虎影院 | 久操中文在线 | 九九久久久久久久爱 | 久久国产精品伦理 | 日本一级一片免在线观看 | 天天狠操| 国产精品真实对白精彩久久 | 国产精品一区在线免费观看 | 日本在线观看永久免费网站 | 久久国产精品自在自线 | 亚洲伊人精品综合在合线 |