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

时间:2018-07-07 02:51:31

标签: 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]

我希望在调用list(a)时运行aslist函数,并且理想情况下我想实现asdict在调用dict()时可以工作的

我希望能够使用dictint和其他所有类型强制类型转换

2 个答案:

答案 0 :(得分:6)

与您可能习惯的许多其他语言(例如C ++)不同,Python没有“类型转换”或“转换运算符”之类的概念。

相反,通常将Python类型的构造函数编写为一些更通用的(鸭子式)协议。


要做的第一件事是转到您关心的任何构造函数的文档,看看它想要什么。即使大多数人会将您链接到Builtin Functions中的条目,也请从Builtin Types开始。

其中许多将链接到Data Model一章中有关特殊方法的条目。


例如,int说:

  

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

然后您可以点击指向__int__的链接,尽管在这种情况下,没有太多的额外信息:

  

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

因此,您想定义一个__int__方法,它应该返回一个int

class MySpecialZero:
    def __int__(self):
        return 0 

序列和集合类型(例如listtuplesetfrozenset)要复杂一些。他们都想要一个iterable

  

一个能够一次返回其成员的对象。可迭代的示例包括所有序列类型(例如liststrtuple)和一些非序列类型(例如dict,文件对象和任何类的对象)您可以使用__iter__()方法或实现序列语义的__getitem__()方法进行定义。

iter函数下对此进行了更好的解释,这也许不是最明显的地方:

  

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

在数据模型的__iter__下:

  

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

     

迭代器对象也需要实现此方法;他们必须返回自己。有关迭代器对象的更多信息,请参见迭代器类型。

因此,对于您的示例,您想成为一个在self.data的元素上进行迭代的对象,这意味着您想要一个__iter__方法来在这些元素上返回一个迭代器。最简单的方法是只在iter上调用self.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())

请注意,与Edward Minnix explained一样,Iterator和Iterable是分开的东西。 Iterable是一种可以在调用其__iter__方法时产生Iterator的东西。所有的迭代器都是可迭代的(它们自己产生),但是许多可迭代的不是迭代器(例如,像list之类的序列)。


dict(和OrderedDict等)也有点复杂。查看文档,您会发现它想要一个映射(即类似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)

几乎所有其他内容都很容易,例如int,但请注意strbytesbytearray是序列。


同时,如果您希望对象可以转换为intlistset,则可能希望它也可以像其他对象一样工作方法。如果是这种情况,请查看collections.abcnumbers,它们不提供不仅是抽象基类(如果需要检查某种类型是否符合某种协议,则使用)以及mixins(使用过)的帮助器。帮助您实施协议。

例如,完整的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)

现在,您几乎可以在可以使用MySeq的任何地方使用tuple了,当然包括从中构造一个list

对于某些类型,例如MutableSequence,快捷键提供的帮助更大-您以5的价格获得了17种方法。


如果您希望同一对象可以列出并且可以听写……那么,您就会遇到设计上的限制。 list想要迭代。 dict想要成对的可迭代对象或映射,这是一种可迭代的对象。因此,您实际上只有两个选择,而不是无限选择:

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

很显然,如果您想实际扮演Mapping的角色,则只能选择一个,一个是第一个。

序列和映射协议重叠的事实从一开始就是Python的一部分,其固有的事实是您可以在两者上都使用[]运算符,并且自从此以来的所有重大更改都保留了该事实。 ,即使其他功能(例如整个ABC模型)变得更加复杂。我不知道是否有人给出过原因,但是大概与扩展切片设计的原因相似。换句话说,使字典和其他映射更容易使用和更具可读性值得付出使它们更复杂,实现起来不太灵活的代价。

答案 1 :(得分:0)

这可以通过重载特殊方法来完成。您将需要为您的类定义__iter__方法,使其可迭代。这意味着任何期望可迭代的事物(例如大多数集合构造函数,例如listset等)都将与您的对象一起使用。

class Test:
    ...

    def __iter__(self):
        return iter(self.data)

注意:您将需要用iter()包装返回的对象,以便它是一个迭代器( iterable 之间有区别迭代器)。列表是 iterable (可以迭代),但不是 iterator (支持__next__,完成后引发StopIteration等)