When calling a function that accepts a list, who is responsible (caller – a user, or called – the function) to ensure that it is a list and not a generator?
An example:
>>> def print_collection(coll):
... for element in coll:
... print element
>>> def print_collection_twice(coll):
... print_collection(coll)
... print_collection(coll)
With a list it will work without surprises:
>>> print_collection_twice( [x*2 for x in xrange(3)] )
0
2
4
0
2
4
And with a generator, obviously it is printed only once, which might lead to a nasty bug:
>>> print_collection_twice( (x*2 for x in xrange(3)) )
0
2
4
What is the best practice here? Should a function assume a list, and a user is responsable to provide the list, or should the function do always real_list = list(input_list) at the beginning so the user does not care?
Edit
I know how to check type of the element and assert, my question is rather high level
Either approach is defensible. It is the responsibility of the function to document what kinds of arguments it wants, and the responsibility of the caller to pass arguments consistent with the documentation. If the function says it wants a list and you pass a generator, there are no guarantees it will work.
The real question is what the function should say it wants, and the answer is that it should say just what it needs, and no more. So don’t say you need a list if all you really need is an iterable. In general, if your function needs to use features of lists that general iterables don’t have (e.g., indexing), then it should just use those features, and an exception will naturally be raised if someone passes in an argument that doesn’t support them. If your function doesn’t need those features, then it doesn’t need a list.
Your example is somewhat unrealistic because all it does is print the argument. In real life you would almost always need to do something besides just consume the iterable, and the nature of that “something you need to do” would clarify what sort of argument you should accept. For your specific example, though, I would say yes, call
liston it (insideprint_collection_twice, not insideprint_collection). The reason is thatprint_collection_twicewants to use the data more than once, which is not possible for a generic iterable.