[英]How to use type hints in python 3.6?
我注意到 Python 3.5 和 Python 3.6 添加了很多關於 static 類型檢查的功能,所以我嘗試使用以下代碼(在 python 3.6,穩定版中)。
from typing import List
a: List[str] = []
a.append('a')
a.append(1)
print(a)
令我驚訝的是,Python 沒有給我錯誤或警告,盡管1
被附加到一個應該只包含字符串的list
中。 Pycharm
檢測到類型錯誤並給我警告,但不明顯,output 控制台中沒有顯示,我擔心有時會錯過。 我想要以下效果:
那可能嗎? 也許mypy
可以做到,但我更願意使用 Python-3.6 風格的類型檢查(如a: List[str]
)而不是mypy
中使用的注釋風格(如# type List[str]
)。 而且很好奇native python 3.6有沒有switch來實現我上面說的兩點。
類型提示完全被 Python 運行時忽略,並且只能由 3rd 方工具檢查,如 mypy 和 Pycharm 的集成檢查器。 還有各種鮮為人知的 3rd 方工具,它們使用類型注釋在編譯時或運行時進行類型檢查,但大多數人使用 mypy 或 Pycharm 的集成檢查器 AFAIK。
事實上,我實際上懷疑在可預見的未來,類型檢查是否會被集成到 Python 中——參見PEP 484 (引入類型注釋)和PEP 526 (引入變量注釋)的“非目標”部分,以及正如 Guido 在這里的評論。
我個人很高興類型檢查與 Python 更緊密地集成在一起,但似乎整個 Python 社區都沒有准備好或願意進行這樣的改變。
最新版本的 mypy 應該理解 Python 3.6 變量注釋語法和注釋樣式語法。 事實上,變量注釋基本上是 Guido 的想法(Guido 目前是 mypy 團隊的一部分)——基本上,在 mypy 和 Python 中對類型注釋的支持幾乎是同時開發的。
那可能嗎? 也許 mypy 可以做到,但我更喜歡使用 Python-3.6 樣式的類型檢查(如
a: List[str]
)而不是 mypy 中使用的注釋樣式(如# type: List[str]
)。 我很好奇原生python 3.6中是否有一個開關來實現我上面所說的兩點。
Python 不可能為你做這件事。 您可以使用mypy
進行類型檢查(PyCharms 內置檢查器也應該這樣做)。 除此之外, mypy
也不限制您只能輸入注釋# type List[str]
,您可以像在 Python 3.6 中那樣使用變量注釋,因此a: List[str]
工作得同樣好。
使用mypy
原樣,因為版本是最新的,您需要安裝typed_ast
並使用--fast-parser
和--python-version 3.6
執行mypy
, 如 mypy 的文檔中所述。 這可能很快就會改變,但現在你需要它們來讓它順利運行
更新:現在--fast-parser
和--python-version 3.6
。
執行此操作后,mypy 檢測到a: List[str]
上第二個操作的不兼容性就好了。 假設您的文件名為tp_check.py
並帶有以下語句:
from typing import List
a: List[str] = []
a.append('a')
a.append(1)
print(a)
使用上述參數運行mypy
(您必須先pip install -U typed_ast
):
python -m mypy --fast-parser --python-version 3.6 tp_check.py
捕獲錯誤:
tp_check.py:5: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
正如在使用 Python 進行類型提示的許多其他答案中所指出的, mypy
和PyCharm
的類型檢查器是執行驗證的,而不是 Python 本身。 Python 當前不使用此信息,它僅將其存儲為元數據並在執行期間忽略它。
Python 中的類型注釋並不意味着強制類型。 任何涉及運行時靜態類型依賴的事情都意味着變化如此根本,以至於繼續將生成的語言稱為“Python”甚至沒有任何意義。
請注意,Python 的動態特性確實允許使用純 Python 代碼構建外部工具來執行運行時類型檢查。 它會使程序運行(非常)緩慢,但也許它適用於某些測試類別。
可以肯定的是 - Python 語言的基礎之一是一切都是對象,並且您可以嘗試在運行時對對象執行任何操作。 如果對象沒有符合嘗試操作的接口,它將在運行時失敗。
本質上靜態類型的語言以不同的方式工作:當在運行時嘗試時,操作只需要在對象上可用。 在編譯步驟中,編譯器在各處為適當的對象創建空間和槽 - 並且,在不符合標准的類型中,會中斷編譯。
Python 的類型檢查允許任意數量的工具做到這一點:在實際運行應用程序之前的一個步驟中中斷和警告(但獨立於編譯本身)。 但是語言的本質不能改變為實際要求對象在運行時遵守——並且在編譯步驟本身進行打字和中斷將是人為的。
盡管如此,人們可以預期 Python 的未來版本可能會在 Python 運行時本身上合並編譯時類型檢查——最有可能通過可選的命令行開關。 (我認為它永遠不會是默認的 - 至少不會破壞構建 - 也許它可以被設置為默認發出警告)
因此,Python 不需要在運行時進行靜態類型檢查,因為它將不再是 Python。 但是至少存在一種同時使用動態對象和靜態類型的語言——Cython 語言,它在實踐中用作 Python 超集。 人們應該期望Cython 很快將新的類型提示語法合並為實際的類型聲明。 (目前它對可選的靜態類型變量使用不同的語法)
pydantic
package 有一個validate_arguments
裝飾器,可以在運行時檢查類型提示。 您可以將此裝飾器添加到您希望強制執行類型提示的所有函數或方法。
我編寫了一些代碼來幫助為整個模塊實現自動化,這樣我就可以為我的測試套件啟用運行時檢查以幫助調試,但是對於使用該庫的代碼則關閉它們,這樣就不會影響性能。
import sys
import inspect
import types
from pydantic import validate_arguments
class ConfigAllowArbitraryTypes:
"""allows for custom classes to be used in type hints"""
arbitrary_types_allowed = True
def add_runtime_type_checks(module):
"""adds runtime typing checks to the given module/class"""
if isinstance(module, str):
module = sys.modules[module]
for attr in module.__dict__:
obj = getattr(module, attr)
if isinstance(obj, types.FunctionType):
setattr(module, attr, validate_arguments(obj, config=ConfigAllowArbitraryTypes))
elif inspect.isclass(obj):
# recursively add decorator to class methods
add_runtime_type_checks(obj)
然后在我的測試套件中,我通過使用模塊名稱調用add_runtime_type_checks
來添加裝飾器。
import mymodule
def setup_module(module):
"""executes once"""
add_runtime_type_checks('mymodule')
def test_behavior():
...
請注意,使用pydantic
它可能會在類型檢查時進行一些意外的轉換,因此如果所需的類型是int
並且您傳遞了 function 0.2
,它會靜默地將其轉換為0
而不是失敗。 原則上,您可以使用typen
庫的enforce_type_hints
裝飾器做幾乎相同的事情,但它不進行遞歸檢查(因此您不能使用list[int]
之類的類型,只能使用list
)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.