0

调用接受列表的函数时,谁负责(调用者 - 用户,或被调用者 - 函数)确保它是 alist而不是 a generator

一个例子:

>>> def print_collection(coll):
...     for element in coll:
...         print element

>>> def print_collection_twice(coll):
...     print_collection(coll)
...     print_collection(coll)

有了一个列表,它就可以毫无意外地工作:

>>> print_collection_twice( [x*2 for x in xrange(3)] )
0
2
4
0
2
4

对于生成器,显然它只打印一次,这可能会导致一个讨厌的错误:

>>> print_collection_twice( (x*2 for x in xrange(3)) )
0
2
4

这里的最佳做法是什么?一个函数是否应该假设一个列表,并且用户有责任提供list,或者该函数应该始终real_list = list(input_list)在开始时执行以便用户不关心?

编辑

知道如何检查元素的类型,而且assert我的问题相当高级

4

3 回答 3

3

任何一种方法都是站得住脚的。函数的责任是记录它想要什么样的参数,调用者的责任是传递与文档一致的参数。如果函数说它需要一个列表并且你传递了一个生成器,则不能保证它会起作用。

真正的问题是函数应该说它想要什么,答案是它应该只说它需要什么,仅此而已。因此,如果您真正需要的只是一个可迭代的,请不要说您需要一个列表。一般来说,如果你的函数需要使用一般可迭代对象没有的列表特性(例如,索引),那么它应该只使用这些特性,如果有人传入一个不具备的参数,自然会引发异常支持他们。如果您的函数不需要这些功能,则不需要列表。

您的示例有些不切实际,因为它所做的只是打印参数。在现实生活中,除了消耗可迭代对象之外,您几乎总是需要做一些事情,而“您需要做的事情”的性质将阐明您应该接受什么样的论点。但是,对于您的具体示例,我会说是的,请调用list它(在里面print_collection_twice,而不是在里面print_collection)。原因是print_collection_twice想要多次使用数据,这对于通用迭代是不可能的。

于 2013-02-07T07:55:36.887 回答
1

最佳实践当然是记录您的需求。记录参数是否应该是可迭代的或序列。Python 的哲学是使用鸭子类型,因此您应该简单地尝试使用参数,就好像它是一个序列一样。

如果你想检查参数是否是一个序列,一个不创建新列表的简单方法是使用len内置函数:

>>> len(iter([1,2,3]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'listiterator' has no len()

如果你得到异常,你可以调用list,或者tuple获取一个序列,或者让异常通过并让用户处理它。选择哪种“政策”取决于您,这完全取决于您。Python 程序员应该仔细阅读文档并传递可以正常工作的参数,因此您可以声明您想要一个可迭代的参数并始终调用list以获取序列,或者声明您想要一个序列并在对象是可迭代的。当您还允许可迭代时,我不认为说明参数应该是一个序列的意义。

顺便说一句,如果你只是想对一个可迭代对象进行多次迭代,你可以使用itertools.tee.

例如:

def print_twice(iterable):
    old, new = itertools.tee(iterable)
    for element in old:
        # do stuff
    for element in new:
        # do stuff
于 2013-02-07T07:55:47.993 回答
0

在我看来,这取决于函数内部的应用程序。重要的是,您要记录您的函数是否也只接受列表或迭代器。函数内部的显式list()调用可能会导致长列表的多余开销,如果您只想迭代列表/生成器一次,则这是不必要的。

于 2013-02-07T07:52:40.473 回答