Asked  7 Months ago    Answers:  5   Viewed   318 times
s = [1,2,3,4,5,6,7,8,9]
n = 3

zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)]

How does zip(*[iter(s)]*n) work? What would it look like if it was written with more verbose code?

 Answers

72

iter() is an iterator over a sequence. [x] * n produces a list containing n quantity of x, i.e. a list of length n, where each element is x. *arg unpacks a sequence into arguments for a function call. Therefore you're passing the same iterator 3 times to zip(), and it pulls an item from the iterator each time.

x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
Tuesday, June 1, 2021
 
qitch
answered 7 Months ago
41

The is operator in Python checks that the two arguments refer to the same object in memory; it is not like the is operator in C#.

From the docs:

The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. x is not y yields the inverse truth value.

What you're looking for in this case is isinstance.

Return true if the object argument is an instance of the classinfo argument, or of a (direct or indirect) subclass thereof.

>>> class animal(object): pass

>>> class dog(animal): pass

>>> myDog = dog()
>>> isinstance(myDog, dog)
True
>>> isinstance(myDog, animal)
True

However, idiomatic Python dictates that you (almost) never do type-checking, but instead rely on duck-typing for polymorphic behavior. There's nothing wrong with using isinstance to understand inheritance, but it should generally be avoided in "production" code.

Tuesday, June 29, 2021
 
Grzegorz
answered 6 Months ago
20

From the doc:

Return a copy of the string with leading and trailing characters removed. If chars is omitted or None, whitespace characters are removed. If given and not None, chars must be a string; the characters in the string will be stripped from the both ends of the string this method is called on.

strip takes a charcters parameter and will try to remove those characters from both end as long as it can.

The parameter is considered as a set of charcters, not as a substring.

lstrip and rstrip works exactly same way, only difference is one of them strip only from left, and another from right side.

Saturday, August 7, 2021
 
OneNoOne
answered 4 Months ago
92

An iterator is an iterable, but an iterable is not necessarily an iterator.

An iterable is anything that has an __iter__ method defined - e.g. lists and tuples, as well as iterators.

Iterators are a subset of iterables whose values cannot all be accessed at the same time, as they are not all stored in memory at once. These can be generated using functions like map, filter and iter, as well as functions using yield.

In your example, map returns an iterator, which is also an iterable, which is why both functions work with it. However, if we take a list for instance:

>>> lst = [1, 2, 3]
>>> list(lst)
[1, 2, 3]
>>> next(lst)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    next(lst)
TypeError: 'list' object is not an iterator

we can see that next complains, because the list, an iterable, is not an iterator.

Tuesday, August 17, 2021
 
Jeff Sherlock
answered 4 Months ago
42

What you're trying to do makes sense, but there's something evil going on inside Python here.

class foo(object):
    c = 0
    def __init__(self):
        self.next = self.next2

    def __iter__(self):
        return self

    def next(self):
        if self.c == 5: raise StopIteration
        self.c += 1
        return 1

    def next2(self):
        if self.c == 5: raise StopIteration
        self.c += 1
        return 2

it = iter(foo())
# Outputs: <bound method foo.next2 of <__main__.foo object at 0xb7d5030c>>
print it.next
# 2
print it.next()
# 1?!
for x in it:
    print x

foo() is an iterator which modifies its next method on the fly--perfectly legal anywhere else in Python. The iterator we create, it, has the method we expect: it.next is next2. When we use the iterator directly, by calling next(), we get 2. Yet, when we use it in a for loop, we get the original next, which we've clearly overwritten.

I'm not familiar with Python internals, but it seems like an object's "next" method is being cached in tp_iternext (http://docs.python.org/c-api/typeobj.html#tp_iternext), and then it's not updated when the class is changed.

This is definitely a Python bug. Maybe this is described in the generator PEPs, but it's not in the core Python documentation, and it's completely inconsistent with normal Python behavior.

You could work around this by keeping the original next function, and wrapping it explicitly:

class IteratorWrapper2(object):
    def __init__(self, otheriter):
        self.wrapped_iter_next = otheriter.next
    def __iter__(self):
        return self
    def next(self):
        return self.wrapped_iter_next()

for j in IteratorWrapper2(iter([1, 2, 3])):
    print j

... but that's obviously less efficient, and you should not have to do that.

Wednesday, September 15, 2021
 
kmunky
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share