我试图在课堂上致电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()
时可以工作的
我希望能够使用dict
,int
和其他所有类型强制类型转换
答案 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
序列和集合类型(例如list
,tuple
,set
,frozenset
)要复杂一些。他们都想要一个iterable:
一个能够一次返回其成员的对象。可迭代的示例包括所有序列类型(例如
list
,str
和tuple
)和一些非序列类型(例如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
,但请注意str
,bytes
和bytearray
是序列。
同时,如果您希望对象可以转换为int
或list
或set
,则可能希望它也可以像其他对象一样工作方法。如果是这种情况,请查看collections.abc
和numbers
,它们不提供不仅是抽象基类(如果需要检查某种类型是否符合某种协议,则使用)以及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__
方法,使其可迭代。这意味着任何期望可迭代的事物(例如大多数集合构造函数,例如list
,set
等)都将与您的对象一起使用。
class Test:
...
def __iter__(self):
return iter(self.data)
注意:您将需要用iter()
包装返回的对象,以便它是一个迭代器( iterable 和之间有区别迭代器)。列表是 iterable (可以迭代),但不是 iterator (支持__next__
,完成后引发StopIteration
等)