如何正确编写返回类型被装饰器修改的函数的类型?
简单的例子:
def example_decorator(fn):
def wrapper(data):
res = fn(data)
return ', '.join(res)
return wrapper
@example_decorator
def func(data: list): # -> ???? str ? list ?
new_data = data
new_data.append('XYZ')
return new_data
# When we type func -> list
def test() -> str:
result = func(['ABC', 'EFG'])
print(type(result)) # <class 'str'>
return result # Incompatible return type [7]: Expected str but got list.
test()
Run Code Online (Sandbox Code Playgroud) 给出以下代码:
from typing import Tuple
class Grandparent:
items: Tuple[str, ...] = ()
class Parent(Grandparent):
items = ('foo',)
class Child(Parent):
items = ('foo', 'bar')
Run Code Online (Sandbox Code Playgroud)
mypy报告以下错误:
error: Incompatible types in assignment (expression has type "Tuple[str, str]", base class "Parent" defined the type as "Tuple[str]")
Run Code Online (Sandbox Code Playgroud)
像这样更改代码(在Parent类中再次指定相同的类型)满足mypy:
error: Incompatible types in assignment (expression has type "Tuple[str, str]", base class "Parent" defined the type as "Tuple[str]")
Run Code Online (Sandbox Code Playgroud)
鉴于所有位置items的分配都满足相同/原始定义,为什么我需要在类层次结构中的多个位置重新指定相同的类型?items有没有办法避免这样做?
鉴于班级
from __future__ import annotations
from typing import ClassVar, Dict, Final
import abc
class Cipher(abc.ABC):
@abc.abstractmethod
def encrypt(self, plaintext: str) -> str:
pass
@abc.abstractmethod
def decrypt(self, ciphertext: str) -> str:
pass
class VigenereCipher(Cipher):
@staticmethod
def rotate(n: int) -> str:
return string.ascii_uppercase[n:] + string.ascii_uppercase[:n]
_TABLE: Final[ClassVar[Dict[str, str]]] = dict({(chr(i + ord("A")), rotate(i)) for i in range(26)})
Run Code Online (Sandbox Code Playgroud)
编译失败(使用 3.8.0)
../cipher.py:19: in <module>
class VigenereCipher(Cipher):
../cipher.py:24: in VigenereCipher
_TABLE: Final[ClassVar[Dict[str, str]]] = dict({(chr(i + ord("A")), rotate(i)) for i in range(26)})
../cipher.py:24: …Run Code Online (Sandbox Code Playgroud) python static-methods decorator class-variables python-typing
当使用可选导入时,即包仅在函数内部导入,因为我希望它成为我包的可选依赖项,有没有办法将函数的返回类型提示为属于此可选的类之一依赖?
举一个简单的例子,pandas作为一个可选的依赖:
def my_func() -> pd.DataFrame:
import pandas as pd
return pd.DataFrame()
df = my_func()
Run Code Online (Sandbox Code Playgroud)
在这种情况下,由于import语句在 内my_func,因此该代码将毫不奇怪地引发:
NameError: 名称 'pd' 未定义
如果改为使用字符串文字类型提示,即:
def my_func() -> 'pd.DataFrame':
import pandas as pd
return pd.DataFrame()
df = my_func()
Run Code Online (Sandbox Code Playgroud)
该模块现在可以毫无问题地执行,但mypy会抱怨:
错误:未定义名称“pd”
如何使模块成功执行并保留静态类型检查功能,同时还可以选择此导入?
我试图使用 TypeVar 来指示 init 参数为某种类型。但我做错了,或者根本不可能。
from typing import TypeVar
T=TypeVar("T")
class TestClass:
def __init__(self,value:T):
self._value=value
a = TestClass(value=10)
b = TestClass(value="abc")
reveal_type(a._value)
reveal_type(b._value)
Run Code Online (Sandbox Code Playgroud)
我希望揭露类型a._value会是int并且b._value一直是string。但它们都显示为“T`-1”
任何帮助或见解表示赞赏!
[编辑]
稍微扩展一点的例子。BaseClass 将被覆盖,实际类型提示由覆盖类提供。
from typing import TypeVar
T=TypeVar("T")
class BaseClass:
def __init__(self,value):
self._value = value
class Class1(BaseClass):
def __init__(self,value:str):
super().__init__(value)
class Class2(BaseClass):
def __init__(self,value:int):
super().__init__(value)
a = Class1("A value")
b = Class2(10)
reveal_type(a._value)
reveal_type(b._value)
Run Code Online (Sandbox Code Playgroud) Python 3.9 引入了Annotated允许向类型提示添加任意元数据的类,例如,
class A:
x: Annotated[int, "this is x"]
Run Code Online (Sandbox Code Playgroud)
可以通过设置 的新include_extras参数来获得带注释的类型提示get_type_hints:
>>> get_type_hints(A, include_extras=True)
{'x': typing.Annotated[int, 'this is x']}
Run Code Online (Sandbox Code Playgroud)
并且元数据本身可以通过__metadata__类型提示的属性访问。
>>> h = get_type_hints(A, include_extras=True)
>>> h["x"].__metadata__
('this is x',)
Run Code Online (Sandbox Code Playgroud)
但是,我的问题是,测试类型提示是否正确的正确方法是 Annotated什么?也就是说,类似于:
if IS_ANNOTATED(h["x"]):
# do something with the metadata
Run Code Online (Sandbox Code Playgroud)
据我所知,没有记录在案的方法可以这样做,并且有几种可能的方法,但似乎都不理想。
比较typetoAnnotated不起作用,因为类型提示不是 的实例Annotated:
>>> type(h["x"])
typing._AnnotatedAlias
Run Code Online (Sandbox Code Playgroud)
所以我们必须这样做:
if type(h["x"]) is _AnnotatedAlias:
...
Run Code Online (Sandbox Code Playgroud)
但是,鉴于 中的前导下划线_AnnotatedAlias,这可能需要使用实现细节。
另一种选择是直接检查__metadata__属性:
if hasattr(h["x"], "__metadata__"):
... …Run Code Online (Sandbox Code Playgroud) 鉴于我想正确使用类型注释来命名来自打字模块的元组:
from typing import NamedTuple, List
class Foo(NamedTuple):
my_list: List[int] = []
foo1 = Foo()
foo1.my_list.append(42)
foo2 = Foo()
print(foo2.my_list) # prints [42]
Run Code Online (Sandbox Code Playgroud)
避免 Python 中可变默认值痛苦的最佳或最干净的方法是什么?我有一些想法,但似乎没有什么是好的
None默认使用
class Foo(NamedTuple):
my_list: Optional[List[int]] = None
foo1 = Foo()
if foo1.my_list is None
foo1 = foo1._replace(my_list=[]) # super ugly
foo1.my_list.append(42)
Run Code Online (Sandbox Code Playgroud)
覆盖__new__或__init__不起作用:
AttributeError: Cannot overwrite NamedTuple attribute __init__
AttributeError: Cannot overwrite NamedTuple attribute __new__
Run Code Online (Sandbox Code Playgroud)
特别的 @classmethod
class Foo(NamedTuple):
my_list: List[int] = []
@classmethod
def use_me_instead(cls, my_list=None):
if not my_list: …Run Code Online (Sandbox Code Playgroud)我有很多具有相同签名的函数,比如(int, int) -> int.
有没有办法用 a Callable(或其他东西)来输入这些函数,以避免为每个函数指定参数类型和返回类型?我想做类似的事情(但显然失败了):
from typing import Callable
f: Callable[[int, int], int]
def f(x, y): # with the previous line, this is equivalent to 'def f(x: int, y: int) -> int:'
...
Run Code Online (Sandbox Code Playgroud)
运行 mypy 结果:
file.py:4: error: Name "f" already defined on line 3
Found 1 error in 1 file (checked 1 source file)
Run Code Online (Sandbox Code Playgroud) dataclass假设你想像这样包装装饰器:
from dataclasses import dataclass
def something_else(klass):
return klass
def my_dataclass(klass):
return something_else(dataclass(klass))
Run Code Online (Sandbox Code Playgroud)
应该如何my_dataclass和/或something_else注释以指示返回类型是数据类?请参阅以下示例,了解内置函数如何@dataclass工作但自定义函数@my_dataclass则不然:
@dataclass
class TestA:
a: int
b: str
TestA(0, "") # fine
@my_dataclass
class TestB:
a: int
b: str
TestB(0, "") # error: Too many arguments for "TestB" (from mypy)
Run Code Online (Sandbox Code Playgroud) 我有一个包含多个条目的现有 TypedDict:
from typing import TypedDict
class Params(TypedDict):
param1:str
param2:str
param3:str
Run Code Online (Sandbox Code Playgroud)
我想创建完全相同的 TypedDict 但所有键都是可选的,以便用户只能指定某些参数。我知道我可以做类似的事情:
class OptionalParams(TypedDict, total=False):
param1:str
param2:str
param3:str
Run Code Online (Sandbox Code Playgroud)
但这种方法的问题是我必须复制代码。有没有办法通过使键可选来继承 Params ?我尝试做
class OptionalParams(Params, total=False):
pass
Run Code Online (Sandbox Code Playgroud)
但 linter 不明白参数是可选的
python ×10
python-typing ×10
mypy ×5
type-hinting ×3
python-3.x ×2
decorator ×1
namedtuple ×1
pyre-check ×1
typeddict ×1