如何在一个实例上调用list()时让类返回一个列表

Mar*_*ame 5 python

我正在尝试list()在课堂上打电话时返回一个列表.什么是最好的方法.

class Test():
    def __init__(self):
        self.data = [1,2,3]
    def aslist(self):
        return self.data
a = Test()
list(a)
[1,2,3]
Run Code Online (Sandbox Code Playgroud)

我想要什么时候list(a)调用它来运行该aslist函数,理想情况下我想实现asdict它在dict()调用时有效

我希望能够与做到这一点dict,int和所有其他类型转换

aba*_*ert 7

与您可能习惯的许多其他语言(例如,C++)不同,Python没有"类型转换"或"转换运算符"或类似的任何概念.

相反,Python类型的构造函数通常被编写为一些更通用的(duck-typed)协议.


首先要做的是找到你关心的构造函数的文档,看看它想要什么.从内置函数开始,即使它们中的大多数都会将您链接到内置类型中的条目.

其中许多将链接到数据模型章节中相关特殊方法的条目.


例如,int说:

...如果x定义__int__(),则int(x)返回x.__int__().如果x定义__trunc__(),则返回x.__trunc__()...

然后你可以点击链接__int__,虽然在这种情况下没有太多额外的信息:

调用实现内置函数complex(),int()和float().应该返回适​​当类型的值.

所以,你想要定义一个__int__方法,它应该返回一个int:

class MySpecialZero:
    def __int__(self):
        return 0 
Run Code Online (Sandbox Code Playgroud)

的顺序和设置类型(如list,tuple,set,frozenset)是有点复杂.他们都想要一个可迭代的:

能够一次返回其成员的对象.iterables的实例包括所有类型的序列(如list,str,和tuple)和一些非序列类型,如dict,文件对象,你用定义任何类的对象__iter__()方法或与__getitem__()实现序列语义方法.

iter功能下对此进行了更好的解释,这可能不是最明显的看法:

... 对象必须是支持迭代协议(__iter__()方法)的集合对象,或者它必须支持序列协议(__getitem__()从0开始的整数参数的方法)...

而下__iter__的数据模型:

当容器需要迭代器时,将调用此方法.此方法应返回一个新的迭代器对象,该对象可以迭代容器中的所有对象.对于映射,它应该迭代容器的键.

Iterator对象也需要实现此方法; 他们被要求归还自己.有关迭代器对象的更多信息,请参阅迭代器类型.

因此,对于您的示例,您希望成为一个迭代元素的对象self.data,这意味着您需要一个__iter__返回这些元素的迭代器的方法.要做到这一点最简单的方法是只叫iterself.data-或者,如果你想有aslist其他原因的方法,也许叫iter上什么方法将返回:

class Test():
    def __init__(self):
        self.data = [1,2,3]
    def aslist(self):
        return self.data
    def __iter__(self):
        return iter(self.aslist())
Run Code Online (Sandbox Code Playgroud)

请注意,正如Edward Minnix所解释的那样,Iterator和Iterable是分开的东西.Iterable是在调用其__iter__方法时可以生成Iterator的东西.所有迭代器都是Iterables(它们自己生成),但许多Iterables不是迭代器(例如list,序列).


dict(OrderedDict等)也有点复杂.检查文档,你会发现它想要一个映射(即类似a的东西dict)一个可迭代的键值对(这些对本身就是迭代).在这种情况下,除非您实现完整映射,否则您可能需要回退:

class Dictable:
    def __init__(self):
        self.names, self.values = ['a', 'b', 'c'], [1, 2, 3]
    def __iter__(self):
        return zip(self.names, self.values)
Run Code Online (Sandbox Code Playgroud)

几乎一切是容易的,就像int-但是请注意str,bytesbytearray有序列.


同时,如果您希望您的对象可以转换为a int或a list或a set,则可能希望它在其他方面也可以像其他对象一样.如果是这种情况,请查看collections.abc并且numbers,不提供不仅是抽象基类的帮助程序(如果需要检查某些类型是否满足某些协议,则使用),还有mixins(用于帮助您实现协议).

例如,一个full Sequence应该提供大多数相同的方法tuple- 大约7个 - 但是如果你使用mixin,你只需要自己定义2:

class MySeq(collections.abc.Sequence):
    def __init__(self, iterable):
        self.data = tuple(iterable)
    def __getitem__(self, idx):
        return self.data[idx]
    def __len__(self):
        return len(self.data)
Run Code Online (Sandbox Code Playgroud)

现在你MySeq几乎可以在任何地方使用tuple- 包括list从中构建一个,当然.

对于某些类型的,喜欢MutableSequence的快捷键帮助更-你得到的5价格17种方法.


如果你想让同一个对象可以列表并且可以直接...那么你就会遇到设计的限制.list想要一个可迭代的.dict想要一个可迭代的对,或一个映射 - 这是一种可迭代的.所以,而不是无限的选择,你只有两个:

  • 迭代键并__getitem__使用这些键实现dict,因此list给出了这些键的列表.
  • 迭代键值对dict,因此list给出了这些键值对的列表.

显然,如果你想真正表现得像一个Mapping,你只有一个选择,第一个.

序列和映射协议重叠的事实从一开始就是Python的一部分,固有的事实是你可以[]在它们两个上使用运算符,并且保留了每一个重大变化,即使它具有其他功能(像整个ABC模型)更复杂.我不知道是否有人给出了理由,但据推测它与扩展切片设计的原因相似.换句话说,使dicts和其他映射更容易和更易读使用是值得的,使它们变得更复杂,实现更不灵活.