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 with
pip 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)