Python 如何键入一个类型为封闭类的方法?
我在python 3中有以下代码:Python 如何键入一个类型为封闭类的方法?,python,python-3.x,pycharm,typing,python-3.5,Python,Python 3.x,Pycharm,Typing,Python 3.5,我在python 3中有以下代码: class Position: def __init__(self, x: int, y: int): self.x = x self.y = y def __add__(self, other: Position) -> Position: return Position(self.x + other.x, self.y + other.y) 但是我的编辑器(PyCharm)说,无法
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: Position) -> Position:
return Position(self.x + other.x, self.y + other.y)
但是我的编辑器(PyCharm)说,无法解析参考位置(在\uuuuuuuuuuuuuuuu添加
方法中)。我应该如何指定我期望返回类型为Position
编辑:我认为这实际上是一个问题。它实际上在警告和代码完成中使用了这些信息
但是如果我错了,请纠正我,并且需要使用其他语法。在解析类主体本身时,名称“Position”不可用。我不知道您是如何使用类型声明的,但是Python的PEP 484——如果使用这些类型提示,大多数模式都应该使用PEP 484,它表示您可以在此时将名称作为字符串:
def __add__(self, other: 'Position') -> 'Position':
return Position(self.x + other.x, self.y + other.y)
检查-符合的工具将知道从那里打开类名并使用它。(请务必记住,Python语言本身不做这些注释——它们通常用于静态代码分析,或者可以有一个用于运行时类型检查的库/框架——但必须显式设置)
更新同样,从Python 3.7开始,检查-从Python 3.8开始,可以从uuu future uuu导入注释编写
,以推迟注释的评估-前向引用类应该可以直接工作。TL;DR:如果您使用的是Python 3.10或更高版本,它就可以工作了。从今天(2019年)开始,在3.7+中,必须使用future语句(from uuuu future\uuuuu import annotations
)打开此功能。在Python 3.6或更低版本中,使用字符串
我猜你得到了这个例外:
NameError: name 'Position' is not defined
这是因为必须先定义Position
,然后才能在注释中使用它,除非您使用的是Python 3.10或更高版本
Python 3.7+:来自未来导入注释
Python 3.7引入了一个模块,该模块使用来自uuu future\uuuuu导入注释的future语句,将注释自动存储为字符串:
from __future__ import annotations
class Position:
def __add__(self, other: Position) -> Position:
...
这将成为Python3.10中的默认设置。由于Python仍然是一种动态类型化语言,因此在运行时不会进行类型检查,因此类型注释应该不会对性能产生影响,对吗?错!在Python3.7之前,如果您导入类型,您将看到在升级到3.7时,类型模块通常是如此。
Python将类型指定为string很好,但是我们基本上绕过了解析器,这让我有点恼火。因此,您最好不要拼错这些文本字符串中的任何一个:
def __add__(self, other: 'Position') -> 'Position':
return Position(self.x + other.x, self.y + other.y)
一个微小的变化是使用绑定的typevar,至少在声明typevar时只需编写一次字符串:
from typing import TypeVar
T = TypeVar('T', bound='Position')
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: T) -> T:
return Position(self.x + other.x, self.y + other.y)
如果可以接受基于字符串的类型提示,则也可以使用\uuuu qualname\uuuu
项。它包含类的名称,并且在类定义的主体中可用
class MyClass:
@classmethod
def make_new(cls) -> __qualname__:
return cls()
通过这样做,重命名类并不意味着修改类型提示。但我个人不希望智能代码编辑器能够很好地处理此表单。如果您只关心修复名称错误:未定义名称“位置”
,您可以将类名指定为字符串:
def __add__(self, other: 'Position') -> 'Position':
或者,如果您使用Python3.7或更高版本,请在代码顶部添加以下行(就在其他导入之前)
但是,如果您也希望它适用于子类,并返回特定的子类,则需要通过定义TypeVar
使用Generic
类
稍微不常见的是,TypeVar
绑定到self
的类型。基本上,这种类型提示告诉类型检查器,\uuu add\uuu()
和copy()
的返回类型与self
的类型相同
from __future__ import annotations
from typing import TypeVar
T = TypeVar('T', bound=Position)
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self: T, other: Position) -> T:
return type(self)(self.x + other.x, self.y + other.y)
def copy(self: T) -> T:
return type(self)(self.x, self.y)
我❤️
然而,关于类型提示继承与self的关系有一点需要说明,即如果您使用类名的文本副本粘贴作为字符串来输入提示,那么您的类型提示将不会以正确或一致的方式继承
解决方法是通过将类型提示放在函数本身的返回上来提供返回类型提示
✅ 例如,请执行以下操作:
class DynamicParent:
def func(self):
# roundabout way of returning self in order to have inherited type hints of the return
# https://stackoverflow.com/a/64938978
_self:self.__class__ = self
return _self
❌ 而不是这样做:
class StaticParent:
def func(self) -> 'StaticParent':
return self
下面是您希望通过环行交叉口执行类型提示的原因✅ 如上图所示
class StaticChild(StaticParent):
pass
class DynamicChild(DynamicParent):
pass
static_child = StaticChild()
dynamic_child = DynamicChild()
✅ dynamic_child
屏幕截图显示,在引用self时,类型提示可以正确工作:
❌ static\u child
屏幕截图显示类型提示错误地指向父类,即类型提示不会随继承而正确更改;它是static
,因为它总是指向父类,即使它应该指向子类
没错,这不是PyCharm的问题,而是Python 3.5 PEP 484的问题。我怀疑如果你通过mypy type工具运行它,你会得到同样的警告。>如果你使用的是Python 4.0,顺便问一下,你看到Sarah Connor了吗?:)@JoelBerkeley我刚刚在3.6上测试过它,类型参数对我有用,只是别忘了从输入导入
,因为在计算字符串时,您使用的任何类型都必须在范围内。啊,我的错误,我只是在类中输入了'
,不是类型参数对于任何使用来自未来导入注释的的人来说都很重要-这必须在所有其他导入之前导入。我希望Python有一个typing.Self
来明确指定这一点。我来这里是想看看是否存在类似您的typing.Self
的东西。在利用多态性时,返回硬编码字符串无法返回正确的类型。在我的例子中,我想实现一个反序列化类方法。我决定返回一个dict(kwargs)并调用some_class(**some_class.deserialize(raw_data))
。这里使用的类型注释适用于正确实现这一点以使用
Position.__add__.__annotations__['return'] = Position
Position.__add__.__annotations__['other'] = Position
>>> for k, v in Position.__add__.__annotations__.items():
... print(k, 'is Position:', v is Position)
return is Position: True
other is Position: True
def __add__(self, other: 'Position') -> 'Position':
return Position(self.x + other.x, self.y + other.y)
from typing import TypeVar
T = TypeVar('T', bound='Position')
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self, other: T) -> T:
return Position(self.x + other.x, self.y + other.y)
class MyClass:
@classmethod
def make_new(cls) -> __qualname__:
return cls()
def __add__(self, other: 'Position') -> 'Position':
from __future__ import annotations
from __future__ import annotations
from typing import TypeVar
T = TypeVar('T', bound=Position)
class Position:
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __add__(self: T, other: Position) -> T:
return type(self)(self.x + other.x, self.y + other.y)
def copy(self: T) -> T:
return type(self)(self.x, self.y)
class DynamicParent:
def func(self):
# roundabout way of returning self in order to have inherited type hints of the return
# https://stackoverflow.com/a/64938978
_self:self.__class__ = self
return _self
class StaticParent:
def func(self) -> 'StaticParent':
return self
class StaticChild(StaticParent):
pass
class DynamicChild(DynamicParent):
pass
static_child = StaticChild()
dynamic_child = DynamicChild()