When can an iterable be replaced with an iterator? - python-3.x

I am trying to understand in which situations an iterable can be replaced with an iterator in Python 3.
More specifically, consider this example:
ml = [1,2,3,4]
iter_ml = iter(ml)
deq_ml = collections.deque(ml)
deq_iter_ml = collections.deque(iter_ml)
print(ml)
print(iter_ml)
print(deq_ml)
print(deq_iter_ml)
This produces the output
[1, 2, 3, 4]
<list_iterator object at 0x7f6ee8eef4c0>
deque([1, 2, 3, 4])
deque([1, 2, 3, 4])
If I check the documentation of deque, it accepts an iterable as the first argument. But here when I provided an iterator over the iterable, that worked too
However, it didn't work when the iterator is given to print
Same is the confusion with islice. It works with both iterables and iterators.
How to know if those can be used interchangeably?

Thank you for the links and comments.
I think the source of my confusion was this:
Calling iter on iterable will return an iterator.
But what would be the behavior when iter was called on an iterator. As that would be the case when I replace iterable with iterator in deque and islice
Confusion cleared
As, one the links from the comments above state
An ITERATOR is an object that is self-iterable (meaning that it has an iter method that returns self).
I got my answer. Also this class post by Vincent nicely depicts that each iterator is always an iterable.

Related

What kind of comprehension is this?

One of my students found that, for ell (a list of string) and estr (a string), the following expression is True iff a member of ell is contained in estr:
any(t in estr for t in ell)
Can anyone explain why this is syntactically legal, and if so what does the comprehension generate?
This is a generator expression.
func_that_takes_any_iterable(i for i in iterable)
It's like a list comprehension, but a generator, meaning it only produces one element at a time:
>>> a = [i for i in range(10)]
>>> b = (i for i in range(10))
>>> print(a)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print(b)
<generator object <genexpr> at 0x7fb9113fae40>
>>> print(list(b))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print(list(b))
[]
When using a generator expression in isolation, the parentheses are required for syntax reasons. When creating one as an argument to a function, the syntax allows for the extra parentheses to be absent.
Most functions don't care what type of iterable they're given - list, tuple, dict, generator, etc - and generators are perfectly valid. They're also marginally more memory-efficient than list comprehensions, since they don't generate the entire thing up front. For all() and any(), this is especially good, since those methods short-circuit as soon as they would return False and True respectively.
As of python 3.8, the syntactic restrictions for the walrus operator := are similar - in isolation, it must be used inside its own set of parentheses, but inside another expression it can generally be used without them.
This is syntactically legal because it's not a list. Anything of the form (x for x in array) is a generator. Think of it like lazy list, which will generate answer only when you ask.
Now generator are also an iterable so it's perfectly fine for it to be inside an any() function.
willBeTrue = any(True for i in range(20))
So this will basically generate 20 Trues and any function looks for any true value; thus will return True.
Now coming to your expression:
ans = any(t in estr for t in ell)
This {t in estr} returns a boolean value. Now generator makes len(ell) amount of those boolean value and any thinks good.. since atleast one is true I return True.

zip objects created using the same iterables not equal. Why?

Why two zip objects (made using same two iterables) aren't equal?
list1 = [1, 2, 3, 4]
list2 = [5, 6, 7, 8]
a = zip(list1, list2)
b = zip(list1, list2)
print(a == b)
The above code is printing False.
Shouldn't it give `True' since the zip objects are similar?
The returned zip object is an iterator. To be able to test the value equality of it, potentially the entire iterator would need to be walked, which would exhaust it, so you'd never be able to use it after. And if only part of the iterator was walked during the test, it would be left in a "difficult to determine state", so it wouldn't be usable there either.
I'm not sure if zip is considered to be a "user-defined class", but note this line in the docs:
User-defined classes have __eq__() and __hash__() methods by default; with them, all objects compare unequal (except with themselves)
In theory they could have made a workaround that would allow equality testing of a zip object (in cases where the object that was zipped wasn't itself an iterator), but there was likely no need. I can't think of a scenario where you would need to test such a thing, and where comparing the object forced as a list isn't an option.

list a map object in Python3

Example
def f(x):
return x**2
list(map(f,[x for x in range(3)]))
Q1:
How does list automatically iterates over the map object? What's actually going on here?
Q2:
Since list is a class and a map object is an iterator, so does this mean that whenever a class acts on an iterator, it will always automatically iterates over the iterator?
Can anyone help me out here? Thanks a lot!
The map function is actually a powerful tool for speed. It is no different in functionality than a for loop.
The map function is written to execute directly by C code - hence making it run faster than if it were executed by interpreted python code. It works with any function (first param) and iterable (second param).
list(map(f,[x for x in range(3)])) # [0, 1, 4]
is the same as..
for x in range(3):
f(x)
You can use a lambda expression as well. This would produce the same results as your function, without declaring the function:
list(map(lambda f: (f ** 2), [x for x in range(3)])) # [0, 1, 4]
The list initializer delegates to list.extend() if it is passed an iterable.
No. This behavior is specific to sequence types.

Is there a way to refer to an operator as a two-argument closure?

Sometimes I need to pass an operator as a closure, like this:
do.some.thing() { x,y -> x+y }
I'm wondering if there is any shorthand "operator pointer" syntax, analogous to the "method pointer" syntax, that would give me the operator already wrapped into a two-argument closure.
I see that most arithmetic operators are available as methods on Number and related classes:
public Number plus(Number right)
Add two numbers and return the result.
but they are instance methods and I can't figure out if I can use the method pointer operator .& to turn them into a two-argument closure.
You can do this sort of thing as well...
Not sure uncurry is the right term, but it's almost right ;-)
import org.codehaus.groovy.runtime.MethodClosure
def uncurry(MethodClosure c) {
{a, ...b -> a."$c.method"(*b) }
}
Then, to make a 2 arg closure from Number.plus, you can do:
def plus = uncurry(Number.&plus)
And then:
assert plus(1, 2) == 3
Which also works with other method handles as well:
def collate = uncurry(List.&collate)
assert collate([1, 2, 3, 4, 5], 2, true) == [[1, 2], [3, 4], [5]]
No, there's no such operator.
The method pointer operator won't work because the MethodClosure it creates basically has an object, in this case the Number class, and a method name. So if you do...
def c = Number.&plus
Then calling c will attempt to call plus() on the Number class, which of course won't work.
The only shortcut I can think of is to declare your operator closures once and simply reuse them as needed.

Groovy getAt() vs get()

Consider the list :
def list = [1, 2, 3]
If I use
list.getAt(0)
or
list.get(0)
Both will give the same output.
But is there any difference between getAt() and get()?
The documentation explains it:
Support the subscript operator for a List.
def list = [2, "a", 5.3]
assert list[1] == "a"
So there's no difference, but getAt() is the method allowing Groovy code to use list[1] instead of list.get(1)
See http://groovy.codehaus.org/Operator+Overloading for how operator overloading works.
The documentation doesn't explain this well, but what actually seems to be the difference in my testing is that getAt(i) will return null when referencing indexes not actually in the list, while the get(i) method will throw an IndexOutOfBoundsException when an index not in the list is passed in, just as plain old Java would.

Resources