Python 定义类包含的内容
好吧,这可能是一个非常愚蠢的问题,而且可能不可能,或者我在用一种扭曲的方式思考。所以,考虑一下:Python 定义类包含的内容,python,Python,好吧,这可能是一个非常愚蠢的问题,而且可能不可能,或者我在用一种扭曲的方式思考。所以,考虑一下: class MyClass: def __init__(self, x): self.x = x self.y = self.x ** 2 class MyList: def __init__(self, list): self.list = list def __iter__(self): return
class MyClass:
def __init__(self, x):
self.x = x
self.y = self.x ** 2
class MyList:
def __init__(self, list):
self.list = list
def __iter__(self):
return iter(self.list)
foo = [MyClass(x=1), MyClass(x=2)]
bar = MyList(list=foo)
for i in bar:
print(i.x, i.y)
好的,这里我们有两个类:MyClass
只是一个非常普通的类,没有什么特别的。它有两个实例属性:x
和y
。
另一方面,MyList
应该是一个定义迭代器的类,迭代器应该只包含第一个类MyClass
中的元素
然后我创建了两个MyClass
的实例,将它们放入一个列表foo
,并使用此列表创建MyList
(bar
)的实例
现在,再一次,传递给MyList
的列表应该只包含MyClass
元素!我想让Python意识到这一事实!当我在循环中迭代bar
的内容时,我希望Python知道bar
中的元素是MyClass
对象
目前,我的程序中的一个主要不便之处是Python没有意识到这一点,并且自动完成无法工作。换句话说:在循环中,当我写
I.
时,我希望所有可能的参数都弹出(在本例中是x
和y
)。我可以在Python中为MyList
定义一些神奇的方法,让Python知道这个列表的内容吗?在您实际运行代码之前,Python不知道任何类型。即检查类型并根据类型执行操作时
在开发代码时,您需要的是IDE/特性。在默认的Python REPL中,这种查找可能不可用。在更复杂的环境中,这种情况确实会发生,您可以看到给定类的属性
例如,在我经常使用的REPL中,当在循环中时:
for i in bar:
我按下I.
并点击tab
两个元素I.x I.y
。这是因为REPL
的开发允许在开发代码时进行更大的内省
我很确定大多数著名的IDE都提供了这一点;Pythons默认的REPL非常简单(
3.5
有一些自动完成功能,但是!:D),并且没有提供太多功能。如果您使用的是PyCharm IDE,那么您可以这样做:
for i in bar:
assert isinstance(i, MyClass)
或者这个:
for i in bar: # type: MyClass
PEP484
用Python≥ 在3.0中,您可以将函数注释()与的类型暗示语义一起使用。尽管后一个建议仅在Python3.5和中被接受,但它在语法上向后兼容所有支持PEP3107的Python版本,因此在任何3.x版本的Python中使用类型提示注释都应该是可行的。[1]
它是否有助于IDE或交互式解释器(REPL)更好地执行自动完成,取决于IDE或解释器,甚至取决于Python的设置≥ 3.5
对于Python2,提供了一个支持PEP0484的工具可能尊重也可能不尊重的
添加您关心的类型提示
让我们看看基于注释的提示(Python3.x)如何查找代码。(基于注释的、与Python-2.x兼容的提示留给读者作为练习。)
为了表明迭代MyList
实例会生成MyClass
对象,我们通过在函数定义的冒号后面附加一个->
(由减号和大于号组成的箭头)以及返回值的类型来提示的返回类型\uu iter\uu()
本身不返回MyClass
,它返回一个迭代器。为了表明它应该是MyClass
上的迭代器,我们使用[2]中的:
为了履行这一承诺,self.list
必须只包含MyClass
实例。因此,请让我们的调用者通过键入\uuuu init\uuuu()
参数来提供这样的信息:
from typing import Iterator, Iterable
# ...
class MyList:
def __init__(self, list: Iterable[MyClass]):
self.list = list
def __iter__(self): -> Iterator[MyClass]
return iter(self.list)
请注意,我选择了Iterable
泛型抽象基类,而不是更具体的(也不是Mapping
、Sequence
或AbstractSet
),因为您的实现只依赖于iter(…)
)
[1] 除了过度使用时的可读性。因此,如果您确实使用了类型提示,请“负责任地使用类型提示”
[2] 包含在Python中≥ 3.5. 对于较低版本,使用installable withpip install typing
我认为有两个独立的问题:如何强制MyList只包含MyClass的实例(注意:我会使MyList成为list
的子类,并覆盖append、init等)以及如何使自动完成工作(注意:\udit\udit
).autocompletion不是python的一部分,而是IDE.FWIW,通常python的理念是让代码;对类/函数接受的数据施加不必要的限制会降低它们的灵活性和可重用性,因此只有在有充分理由这样做时才应该这样做。顺便说一句,list
是一个不好的变量名,因为它会隐藏内置的list
类型,这可能会导致神秘的错误。另外,self.y=x*x
比self.y=self.x**2
更简单。Python通常使用“duck-typing”,它不是一种类型化语言,而是一种动态语言。然而,您希望对参数和返回值提供一些保证并不是什么新鲜事,有一些方法可以改进python:实际上,即使是Python2的REPL也可以执行一些自动完成(至少在*nix系统上),但您必须启用它。最简单的方法是使用一个合适的PYTHONSTARTUP脚本,如中所示。感谢对此的澄清。我不知道REPL特性依赖于IDE。是的,我正在使用PyCharm,尽管这不是一个在所有环境下都能工作的解决方案,但至少目前它对我有效!还有一个问题:我想这是不可能添加到iter魔术方法中的,所以IDE自动知道这一点,而不必在
from typing import Iterator, Iterable
# ...
class MyList:
def __init__(self, list: Iterable[MyClass]):
self.list = list
def __iter__(self): -> Iterator[MyClass]
return iter(self.list)