*Memo:
- My post explains an iterator (1).
- My post explains a generator.
- My post explains a class-based iterator with __iter__() and/or __next__().
- My post explains the shallow and deep copy of an iterator.
iter() or __iter__() can create an iterator with an iterable as shown below:
print(iter())
# TypeError: iter expected at least 1 argument, got 0
v1 = [0, 1, 2, 3, 4] # list
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <list_iterator object at 0x0000026906F3C460>
print(type(v2))
# <class 'list_iterator'>
for x in v2:
print(x)
# 0
# 1
# 2
# 3
# 4
v1 = (0, 1, 2, 3, 4) # tuple
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <tuple_iterator object at 0x000002821F7695A0>
print(type(v2))
# <class 'tuple_iterator'>
for x in v2:
print(x)
# 0
# 1
# 2
# 3
# 4
v1 = {0, 1, 2, 3, 4} # set
v1 = frozenset({0, 1, 2, 3, 4}) # frozenset
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <set_iterator object at 0x00000282203069C0>
print(type(v2))
# <class 'set_iterator'>
for x in v2:
print(x)
# 0
# 1
# 2
# 3
# 4
v1 = {'name': 'John', 'age': 36, 'gender': 'Male'} # dict
v1 = {'name': 'John', 'age': 36, 'gender': 'Male'} # dict.keys()
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <dict_keyiterator object at 0x0000028220423FB0>
print(type(v2))
# <class 'dict_keyiterator'>
for x in v2:
print(x)
# name
# age
# gender
v1 = {'name': 'John', 'age': 36, 'gender': 'Male'}.values() # dict.values()
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <dict_valueiterator object at 0x00000282204FEE80>
print(type(v2))
# <class 'dict_valueiterator'>
for x in v2:
print(x)
# John
# 36
# Male
v1 = {'name': 'John', 'age': 36, 'gender': 'Male'}.items() # dict.items()
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <dict_itemiterator object at 0x00000282202F1670>
print(type(v2))
# <class 'dict_itemiterator'>
for x in v2:
print(x)
# ('name', 'John')
# ('age', 36)
# ('gender', 'Male')
v1 = 'Hello' # str
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <str_ascii_iterator object at 0x0000026906CEA560>
print(type(v2))
# <class 'str_ascii_iterator'>
for x in v2:
print(x)
# H
# e
# l
# l
# o
v1 = b'Hello' # bytes
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <bytes_iterator object at 0x0000026B42119F30>
print(type(v2))
# <class 'bytes_iterator'>
for x in v2:
print(x)
# 72
# 101
# 108
# 108
# 111
v1 = bytearray(b'Hello') # bytearray
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <bytearray_iterator object at 0x0000026B423CDBA0>
print(type(v2))
# <class 'bytearray_iterator'>
for x in v2:
print(x)
# 72
# 101
# 108
# 108
# 111
v1 = range(5) # range
v2 = iter(v1)
v2 = v1.__iter__()
print(v2)
# <range_iterator object at 0x000001F954F52150>
print(type(v2))
# <class 'range_iterator'>
for x in v2:
print(x)
# 0
# 1
# 2
# 3
# 4
A generator can create an iterator as shown below:
def func():
yield 0
yield 1
yield from [2, 3, 4]
print(func) # <function func at 0x000001FCD2E3CAE0>
print(type(func)) # <class 'function'>
gen = func()
print(gen) # <generator object func at 0x00000282207E3CC0>
print(type(gen)) # <class 'generator'>
for x in v2:
print(x)
# 0
# 1
# 2
# 3
# 4
A generator comprehension can create a generator's iterator as shown below:
gen = (x.upper() for x in ['a', 'b', 'c', 'd', 'e'])
print(gen) # <generator object func at 0x00000282207E3CC0>
print(type(gen)) # <class 'generator'>
for x in gen:
print(x)
# A
# B
# C
# D
# E
A huge iterator doesn't get MemoryError
as shown below:
gen = (x for x in range(100000000))
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
print(next(gen)) # 4
# ...
itertools(repeat(), pairwise() and combinations()) can create iterators as shown below:
from itertools import repeat
v = repeat(object='Hello', times=3)
print(v)
# repeat('Hello')
print(type(v))
# <class 'itertools.repeat'>
print(next(v)) # Hello
print(next(v)) # Hello
print(next(v)) # Hello
print(next(v)) # StopIteration:
from itertools import pairwise
v = pairwise('ABCD')
v = pairwise(['A', 'B', 'C', 'D'])
print(v)
# <itertools.pairwise object at 0x000001BE9A1ABF70>
print(next(v)) # ('A', 'B')
print(next(v)) # ('B', 'C')
print(next(v)) # ('C', 'D')
print(next(v)) # StopIteration:
from itertools import combinations
v = combinations(iterable='ABC', r=2)
print(v)
# <itertools.combinations object at 0x000002690700D170>
print(next(v)) # ('A', 'B')
print(next(v)) # ('A', 'C')
print(next(v)) # ('B', 'C')
print(next(v)) # StopIteration:
An iterator can be continuously used through multiple variables as shown below:
v1 = v2 = v3 = iter([0, 1, 2, 3, 4]) # Equivalent
# v1 = iter([0, 1, 2, 3, 4])
print(v1) # v2 = v1
print(v2) # v3 = v2
print(v3)
# <list_iterator object at 0x000002821F75D240>
print(next(v1)) # 0
print(next(v2)) # 1
print(next(v3)) # 2
print(next(v1)) # 3
print(next(v2)) # 4
print(next(v3)) # StopIteration:
An iterator except the one created by a generator or generator comprehension can be copied as shown below:
*Memo:
-
copy.copy() does shallow copy:
- It has no arguments.
-
copy.deepcopy() does deep copy:
- It has no arguments.
-
iter()
doesn't shallow copy. -
copy.deepcopy()
should be used because it's safe, doing copy deeply whilecopy.copy()
isn't safe, doing copy shallowly.
import copy
v1 = iter(['A', 'B', 'C', 'D', 'E'])
v2 = copy.copy(v1)
v2 = copy.deepcopy(v1)
print(v1) # <list_iterator object at 0x00000200F45D19C0>
print(v2) # <list_iterator object at 0x000002821F75D240>
print(next(v1)) # A
print(next(v2)) # A
print(next(v1)) # B
print(next(v2)) # B
print(next(v1)) # C
print(next(v2)) # C
print(next(v1)) # D
print(next(v2)) # D
print(next(v1)) # E
print(next(v2)) # E
print(next(v1)) # StopIteration:
print(next(v2)) # StopIteration:
import copy
def func():
yield 'A'
yield 'B'
yield from ['C', 'D', 'E']
gen1 = func()
gen2 = copy.copy(gen1)
gen2 = copy.deepcopy(gen1)
# TypeError: cannot pickle 'generator' object
import copy
gen1 = (x.upper() for x in ['A', 'B', 'C', 'D', 'E'])
gen2 = copy.copy(gen1)
gen2 = copy.deepcopy(gen1)
# TypeError: cannot pickle 'generator' object
The variables v1
and v2
refer to the same iterator unless copied as shown below:
*Memo:
-
is
keyword oris
andnot
keyword can check ifv1
andv2
refer or don't refer to the same iterator respectively.
import copy
v1 = iter(['A', 'B', 'C', 'D', 'E'])
v2 = v1 # v2 refers to the same iterator as v1.
print(v1) # <list_iterator object at 0x000001FCD6B64DC0>
print(v2) # <list_iterator object at 0x000001FCD6B64DC0>
print(v1 is v2, v1 is not v2)
# True False
# v2 refers the different iterator from v1.
v2 = copy.copy(v1)
v2 = copy.deepcopy(v1)
print(v1) # <list_iterator object at 0x000001FCD6B64DC0>
print(v2) # <list_iterator object at 0x000001FCD6B66020>
print(v1 is v2, v1 is not v2)
# False True
sorted() can convert an iterator to a list, then sort the list, then the sorted list is converted to an iterator with iter()
as shown below:
*Memo:
- The 1st argument is
iterable
(Required-Type:Iterable):- Don't use
iterable=
.
- Don't use
- The 2nd argument is
key
(Optional-Default:None
-Type:Callable or NoneType). - The 3rd argument is
reverse
(Optional-Default:False
-Type:bool
) to reverse the list. -
sorted()
creates a copy:- Be careful,
sorted()
does shallow copy instead of deep copy as my issue.
- Be careful,
import copy
v1 = iter([3, 5, -2, 1, -4])
v2 = sorted(copy.copy(v1))
v2 = sorted(copy.copy(v1), key=None, reverse=False)
print(v2)
# [-4, -2, 1, 3, 5]
v2 = iter(sorted(copy.copy(v1)))
print(next(v2), next(v2), next(v2), next(v2), next(v2))
# -4 -2 1 3 5
v2 = iter(sorted(copy.copy(v1), reverse=True))
print(next(v2), next(v2), next(v2), next(v2), next(v2))
# 5 3 1 -2 -4
v2 = iter(sorted(copy.copy(v1), key=abs))
print(next(v2), next(v2), next(v2), next(v2), next(v2))
# 1 -2 3 -4 5
v2 = iter(sorted(copy.copy(v1), key=abs, reverse=True))
print(next(v2), next(v2), next(v2), next(v2), next(v2))
# 5 -4 3 -2 1
v1 = ("apple", "Banana", "Kiwi", "cherry")
""" Case sensitive sort """
v2 = iter(sorted(v1))
print(next(v2), next(v2), next(v2), next(v2))
# Banana Kiwi apple cherry
""" Case insensitive sort """
v2 = iter(sorted(v1, key=str.upper))
v2 = iter(sorted(v1, key=str.lower))
print(next(v2), next(v2), next(v2), next(v2))
# apple Banana cherry Kiwi
""" Sort by the length of a word """
v2 = iter(sorted(v1, key=len))
print(next(v2), next(v2), next(v2), next(v2))
# Kiwi apple Banana cherry
Top comments (0)