Table of contents
foreword
Lists, tuples, dictionaries, collections, and generators are all python
iterable objects, which are often forgotten when used. This blog post summarizes them.
1. List
A list ( list
) is Python
a data structure in which can store different types of data. Different elements are separated by commas.
1.1. Rules of Use
- Use square brackets [ ] to indicate start and end.
- Different elements are separated by commas.
- The permutation of each element is numbered, and lists with the same elements but different permutations belong to different lists.
- The list index starts from
0
the beginning, and we can access the values in the list through the subscript index.- Lists can store different types of elements (integers, floating point numbers, strings, etc.), and can be nested (lists, dictionaries, sets)
1.2. Common functions
- Traverse :
for/while
Loop through the list;- Add :
append(),extend(),insert()
Add an element to the end, add elements of one list to another list, and add elements at the specified position;- find :
in,not in
;- Modification : Specify index modification, such as
li[2] = 'a'
;- Delete :
del[i],pop(),remove()
delete according to the subscript, delete the last element, delete according to the element value;- Sorting :
li.sort(reverse=False)
,li = sorted(li, reverse=False)
one is an object, the other is a function implementation;- Operations :
+
,*
,==
, are used for splicing lists, repetition times, judging whether they are equal, and judging the size of the list (starting from the first element to compare one by one until you find an unequal one<
)>
- Nesting : Lists can be nested, such as
[1,2,3,[1,2,[1,2,3]]]
;- slice : as
li[:3]
2. Tuple
2.1. Rules of use
Python
A tuple ( tuple
) is like a list, except that the elements of the tuple cannot be modified. Use parentheses for tuples and square brackets for lists.
- Use parentheses () to denote beginning and end.
- Different elements are separated by commas.
- The permutation of each element is numbered, and tuples with the same elements but different permutations belong to different tuples.
- Tuples can use subscript indexing to access the values in the tuple.
- Tuples do not allow modification of tuples.
- When there is only one element in the tuple, a comma needs to be added at the end
(1,)
2.2. Common functions
Since the tuple is an immutable type, many functions in the list are not applicable to the tuple, such as adding, deleting, checking, sorting, etc., when the tuple needs to be changed.
- traversal:
for/while
loop;- Count:
len()
used to calculate the number of tuples;- slice:
tu[2:]
;- Conversion:
tuple(li)
convert the list to a tuple;- Operations:
+
,*
,==
, are used for splicing lists, repetition times, judging whether they are equal, and judging the size of the list (starting from the first element to compare one by one until you find an unequal one<
)>
3. Dictionary
A dictionary is a container for storing data. Like a list, it can store multiple data. Each element is made up of two parts, a key and a value.
3.1. Rules of use
Dictionary usage rules:
- Use curly braces {} to indicate the start and end, and each element appears in a key:value pair.
- Different elements are separated by commas.
- The permutation of each element is unordered (indexing is not supported), and dictionaries with the same elements but different permutations belong to the same dictionary.
- Access values by key. (Note: If you use a key that does not exist, the program will report an error.)
- The key value is unique, and the dictionary does not allow duplicates. If you try to use duplicate keys in the dictionary, only the last key-value pair will remain in the dictionary, and the previous key-value pairs will be discarded. The key itself must be unique.
3.2. Common functions
- Traversal:
dicts.key()
,dicts.value
,dicts.items()
represent the traversal key, value, and tuple respectively (the key-value pair is a whole, which can be disassembled for key-value pair traversal);- Added: direct
dicts['num'] = 110
ornew_dict = {"num": 110}, dicts.update(new_dict)
- Delete:
del dicts['a']/del dicts
,dicts.pop('a')
,clear()
respectively means to delete the element/the entire dictionary (the dictionary will not exist after deleting the entire dictionary), delete the element and return thevalue
value, and clear the dictionary;- Lookup: Use key access
dicts['a']
, if the key does not exist, an error will be reported. Not sure if the key exists but you can useget()
the method,num = dicts.get('num')
. You can also usein
the method;- Change: directly modify the value corresponding to the key,
dicts['a'] = 'xiaoming'
;- Length: Get the number of dictionary elements,
len()
;- key:
dicts.keys()
;- value:
dicts.values()
;
Demonstrate traversal of dictionaries:
if __name__ == "__main__":
dicts = {
'num': 110, 'name': 'xiaoming'}
print("key遍历:")
for key in dicts.keys():
print(key)
print("value遍历:")
for value in dicts.values():
print(value)
print("元素遍历:")
for item in dicts.items():
print(item)
print("键值对遍历:")
for key, value in dicts.items():
print(key, value)
# 遍历字典默认返回键
print("默认遍历返回键:")
for key in dicts:
print(key)
output:
key遍历:
num
name
value遍历:
110
xiaoming
元素遍历:
('num', 110)
('name', 'xiaoming')
键值对遍历:
num 110
name xiaoming
默认遍历返回键:
num
name
Four. Collection
4.1. Rules of use
- Use curly braces
{}
to indicate the beginning and end, and each element appears as a single element.- Different elements are separated by commas.
- The permutation of each element is unordered (indexing is not supported), and sets with the same elements but different permutations belong to the same set.
- If there are duplicate elements in the collection, the duplicate values will be automatically deleted without reporting an error.
4.2. Common functions
The elements in the collection are unique, so the collection does not support index access, slicing, sorting, modification, etc., and the elements of the collection must be immutable types (integer, string, tuple, etc.), not mutable types (list , dictionaries, etc.). A mutable ( Mutable
) type refers to a data type whose value or content can be modified after creation, while an immutable ( Immutable
) type refers to a data type whose value or content cannot be modified after creation.
- traversal:
for/while
loop;- add:
my_set.add(6)
;- Delete:
remove()
,discard()
,clear()
are all deleted, the first one will report an error when deleting the missing element, the second one will not report an error, and the third one is to clear the collection;- find:
in
;- length:
len()
;- Sets:
|
,&
,-
,^
are union, intersection, difference, and cross-complement respectively.
5. Iterators, generators, iterable objects
5.1. Iterators
Definition of iterator type:
- When two methods
__next__
and are defined in the class__iter__
__iter__
The method needs to return the object itself, ie:self
__next__
method, return the next data, if there is no data, you need to throw anstopIteration
exception.
An iterator is an object that implements __next__
and __iter__
methods (both are indispensable), called iterators. Among them __iter__
, the method returns the iterator itself, and __next__
the method continuously returns the next value in the iterator until there are no more elements in the container, and Stoplteration
an exception is thrown to terminate the iteration. The iterator has no length, cannot len
be used to get the length, and has no len
properties. An exception will be thrown at the end of the iteration, so the iteration cannot be repeated. See how to create iterators yourself:
# 创建迭代器类型:
class IT:
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration()
return self.counter
# 实例化一个迭代器对象
obj1 = IT()
# 返回1
v1 = obj1.__next__()
# 返回2
v2 = obj1.__next__()
# 会抛出异常
# v3 = obj1.__next__()
obj2 = IT()
# 也可以直接通过内置函数next执行
a1 = next(obj2)
a2 = next(obj2)
# 抛出异常
# a3 = next(obj1)
obj3 = IT()
# 首先会执行迭代器对象的__iter__方法并获取返回值,然后一直反复的执行next方法,每次执行的结果都会赋值为item.
for i in obj3:
print(i)
output:
1
2
5.2. Generators
A generator is actually a special kind of iterator, but this kind of iterator is more elegant. __iter()__
It doesn't need to write and __next__
methods like the above class , just a yiled
keyword. That is, if a function contains yield
keywords (no matter how many there are yield
), the function becomes a generator.
yield
what's the effect?
- Every time the program encounters the keyword in the code
yield
, it will return the resultvield
Reserve the running state of the current function, wait for the next call, and execute the following statement from the statement returned last time when the next call is made .
# 创建生成器函数,定义一个函数,只要这个函数中出现了yield就是生成器。
def fun():
yield 1
yield 2
yield 3
# 创建生成器对象(内部是根据生成器类generator创建的对象),生成器类的内部也声明了: __iter__、__next__方法
obj1 = fun() # 此时不会执行函数,因为里面有yield
for i in obj1:
print(i)
output:
1
2
3
A generator is a special kind of iterator. yield
It can be understood that after return
one yield
is executed, the program stops here, and the next time it comes in here, it will be executed from the place where it stopped, so the above program can be output directly 1,2,3
. Friends who yield
do not understand the usage of this function can jump to the blog written by this great god: Detailed explanation of the usage of yield in python - the simplest and clearest explanation . For yield
more advanced usage, you can move to this video: Python development and programming advanced tutorial, thread communication/decorator/iterator/asynchronous IO/magic method/reflection .
send
The role of the method:
next
Call the generator like a method (there are two ways to call the generator: methodnext
andsend
method)send
When the method calls the generator, it can pass data to the generator at the same time to the inside of the generator
There are two methods for preactivating generators:
- Call
next
the method directly to activate the builder- Call
send(None)
the method to activate the builder
5.3. Iterable objects
In Python
any object in , as long as it defines a method that can return an iterator ,__iter__
if the returned iterator is an iterable object, then it is an iterable object (the one that defines a method that can support subscript indexing __getitem__
is not an iterable object, but can pass for loop), in layman's terms, it can for
be traversed through loops.
Here is a puzzle to explain. If an object is an iterable object, then this object can be for looped, because the internal obj.__iter__
method of the for loop is executed first, and what it returns is an iterator object, then we know whether the iterator object can be executed obj.__next__
. Value, and if it returns an iterator object, then whether it is called based on this iterator object inside the for loop __next__
can help us to get the value one by one. There is a doubt here, so why is it possible to follow the for loop with an iterator? If he directly loops the iterator object, does the iterator object __iter__
return himself or an iterator object, right, and then call __next__
the method, so the two make sense and there is nothing wrong with it.
In , you can get the iterator returned by the iterable object python
through the method, and then use the function to get the elements in it one by one. When the iterator is exhausted, calling the next() function again raises an exception. Create an iterable object yourself:iter()
next()
StopIteration
# 如果一个类中有__iter__方法且返回一个迭代器对象 ; 则我们称以这个类创建的对象为可迭代对象。
class Foo:
def __iter__(self):
return 迭代器对象/生成器对象
# 可迭代对象是可以使用for来进行循环的,在循环的内部其实是先执行__iter__方法,
# 获取其迭代器对象,然后再在内部执行这个迭代器对象的next功能,逐步取值。
obj = Foo()
for i in obj:
pass
Let's look at a __getitem__
simple example created by:
class Employee:
def __init__(self, employee):
self.employee = employee
# item是解释器帮我们维护索引值,在for循环的时候自动从0开始计数。
def __getitem__(self, item):
return self.employee[item]
emp = Employee(["zhangsan", "lisi", "wangwu"])
for i in emp:
print(i)
Look at another example:
class IT:
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration()
return self.counter
# 如果类中有__iter__方法并且返回一个迭代器对象,这个创建的对象就成为可迭代对象。
class Foo:
def __iter__(self):
return IT()
# 循环可迭代对象时,内部先执行obj.__iter__并获取迭代器对象,然后在for循环内部不断地执行迭代器对象的next方法
# (区别一下迭代器是先调用__iter__方法返回自己本身,然后在调用next方法,这里是内部先执行iter方法返回迭代器对象,然后迭代器对象去调用next方法。)
obj = Foo()
for i in obj:
print(i)
output:
1
2
Based on the above understanding, let's create an iterator-based range
method ourselves:
class ItRange:
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == self.num:
raise StopIteration()
return self.counter
class my_range:
def __init__(self, max_num):
self.max_num = max_num
def __iter__(self):
return ItRange(self.max_num)
for i in my_range(5):
print(i)
output:
0
1
2
3
4
Let's look at a generator-based custom range
method:
class my_range:
def __init__(self, max_num):
self.max_num = max_num
def __iter__(self):
counter = 0
while counter <self.max_num:
yield counter
counter += 1
for i in my_range(5):
print(i)
output:
0
1
2
3
4
Summarize:
- Iterators: classes that implement
__iter__
the and__next__
interfaces.- Generator: Function that
yield
iterates over elements.- Iterable object: generally a data container, and retains an iterative interface
__iter__
.
__iter__
The method is left to the program to obtain the iterator objectiter()
;- After obtaining the iterable object, pass
__next__
ornext()
to iterate over the elements.
Iterators must be iterable objects, and iterable objects are not necessarily iterators.
Why do we need iterators when we have iterable objects?
answer:
Although iterable objects can be converted to iterators through the iter() function, iterators are designed to provide a way to generate elements lazily. Iterators are computed or generated each time an element is requested, rather than storing all elements in memory at once. Saves space.
Give two examples:
# 创建一个包含大量元素的列表
large_list = [i for i in range(1000000)]
# 使用for循环遍历大型列表
for item in large_list:
process_item(item)
In the above code, we directly use for
a loop to iterate over the large list large_list
. Doing so will load the entire list into memory at once, and then fetch the elements of the list one by one for processing. This approach can consume a lot of memory when dealing with large lists, especially if the list is very large. If the list is too large to fit in memory at once, it may cause out-of-memory problems.
Here's how to use iterators:
class LargeListIterator:
def __init__(self, large_list):
self.large_list = large_list
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.large_list):
raise StopIteration
value = self.large_list[self.index]
self.index += 1
return value
# 创建一个包含大量元素的列表
large_list = [i for i in range(1000000)]
# 使用迭代器逐个获取列表中的元素
iterator = LargeListIterator(large_list)
for item in iterator:
process_item(item)
By creating
LargeListIterator
an instance and passing a large list to it, we canfor
use iterators in a loop to process the elements of the list one by one without loading the entire list into memory at once. This way can handle large lists efficiently, saving memory and improving performance. Iterators generate list elements on-demand as they are needed, rather than generating the entire list all at once.
Seven. zip function
zip()
The function is used to take an iterable object as a parameter, pack the corresponding elements in the object into tuples , and return an iterator of tuples.
- If the number of elements of each iterator is inconsistent, the length of the returned list is the same as the shortest object
- Using
*
the notation operator, tuples can be unpacked into lists.
python
Common iterable objects in: lists, tuples, strings, dictionaries, collections, file objects, generators, etc. It can be judged by the following methods:
from collections.abc import Iterator, Iterable
l1 = [1, 2, 3, 4, 5]
print(isinstance(l1, Iterator))
print(isinstance(l1.__iter__(), Iterator))
L2 = [1, 2, 3, 4, 5]
# Iterable无法判断一个对象是否是可迭代对象,需要配合Iterator进行判断(不是迭代器,但是是一个Iterable表明是一个可迭代对象)
print(isinstance(l1, Iterable))
print(isinstance(l1.__iter__(), Iterable))
output:
False
True
True
True
Let's see zip
how the function is packaged:
list1 = [1, 2, 3, 4, 5]
list2 = ["hello", "good", "nice", "haha"]
# 集合是一个无序,无重复的可迭代对象,0=False,实际上内部按照一定逻辑对集合已经排序好了
set3 = {
True, False, None, 0}
print(set3)
print('*' * 60)
zip_tup = zip(list1, list2, set3) # 打包
for i in zip_tup:
print(i)
print('*' * 60)
zip_tup = zip(list1, list2, set3)
print(next(zip_tup))
print(next(zip_tup))
print(next(zip_tup))
print('*' * 60)
print(zip_tup)
print('*' * 60)
zip_tup = zip(list1, list2, set3)
print(list(zip_tup)) # 可以将包转化为列表,查看包中的内容
output:
{
False, True, None}
************************************************************
(1, 'hello', False)
(2, 'good', True)
(3, 'nice', None)
************************************************************
(1, 'hello', False)
(2, 'good', True)
(3, 'nice', None)
************************************************************
<zip object at 0x0000027E1E353A48>
************************************************************
[(1, 'hello', False), (2, 'good', True), (3, 'nice', None)]
Let's look at how to unpack:
tuples are *
unpacked by
"""
拆包
"""
list1 = [1, 2, 3, 4, 5]
list2 = ["hello", "good", "nice", "haha"]
# 集合是一个无序,无重复的可迭代对象,0=False,实际上内部按照一定逻辑对集合已经排序好了
set3 = {
True, False, None, 0}
zip_tup = zip(list1, list2, set3) # 打包
li_zip = list(zip_tup)
print(li_zip)
print('*' * 60)
zip_tup = zip(list1, list2, set3) # 打包
for item in zip(*zip_tup):
print(item)
output:
[(1, 'hello', False), (2, 'good', True), (3, 'nice', None)]
************************************************************
(1, 2, 3)
('hello', 'good', 'nice')
(False, True, None)
**
Let’s look at a classic example of function parameter passing by unpacking the dictionary :
person = {
"name": "Alice", "age": 25, "city": "New York"}
tu = (1, 2, 3, 4)
def fun(*args, **kwargs):
print(args)
print(kwargs)
fun(tu, person)
fun(*tu, **person)
# 不使用拆包需要手动拆包
fun(1, 2, 3, 4, name="Alice", age=25, city="New York")
Here is a question:
directly define onel1=[1,2,3,4,5]
,l2=[1,2,3,4,5]
printl1
,l2
the output is the content of the list,l1
,l2
is also an iterable object, you can directly print the content, whyzip(l1,l2)
can't you directly output the tuple directly? Isn't his return an iterable object?
answer:
- A list is an iterable object whose contents can be printed directly. The list object internally implements
__iter__()
the method so that it can be iterated. When you print a list directly, the methodPython
of the list is called , which returns a string representation of the list, which contains the contents of the list.__str__()
zip()
The function returns an iterable object, actually an iterator, used to generate a sequence of tuples. When you directly printzip()
the returned iterable, it will appear as a class identifier, rather than outputting the contents of the tuple directly. This is because inPython
, when printing an object, the object's__repr__()
method is called by default to display its representation.
To deepen the impression, let's look at an example:
# 将每个元组的元素作为参数传递给函数
def print_values(a, b):
print("Value of a:", a)
print("Value of b:", b)
# 使用解包操作符 * 将元组的元素作为参数传递给函数
for tuple_values in zip(l1, l2):
print_values(*tuple_values)
print("-" * 10)
# 在打印语句中输出多个值
for tuple_values in zip(l1, l2):
print(*tuple_values)
Value of a: 1
Value of b: 2
----------
Value of a: 2
Value of b: 3
----------
Value of a: 3
Value of b: 4
----------
Value of a: 4
Value of b: 5
----------
1 2
2 3
3 4
4 5
Eight. enumerate function
This function is relatively simple. enumerate(iteration, start)
The function contains two parameters by default. iteration
The parameters are parameters that need to be traversed, such as dictionaries, lists, tuples, etc., and the start parameter is the starting 0
parameter 0
. enumerate
The function has two return values, the first return value is start
the number from the parameter, and the second parameter is iteration
the value in the parameter.
Let's look at an example:
names = ["Alice", "Bob", "Carl"]
for index, value in enumerate(names):
print(f'{
index}: {
value}')
0: Alice
1: Bob
2: Carl
From the results, he returns all the elements in the list and adds the index number, which 0
starts from the default.
Nine. Packing
The usage of * is briefly introduced above, and the packaging and unpacking will be introduced in detail below.
When the function is defined, if it is added in front of the variable , it means that *
all positional parameters are collected into a new tuple, and the entire tuple is assigned to a new variable . Let's look at an example respectively.args
**
kargs
*
When defining a function use:
def f(*args): # * 在函数定义中使用
print(args)
f()
f(1)
f(1, 2, 3, 4)
output:
()
(1,)
(1, 2, 3, 4)
**
When defining a function use:
def f(**kwargs): # ** 在函数定义中使用
print(kwargs)
f()
f(a=1, b=2)
output:
{
}
{
'a': 1, 'b': 2}
10. Unpack
The unpacking parameter is the same as the packing parameter, it is also *
and **
, but this time *
and **
is not used in the definition of the function, but in the calling phase of the function. Only in the calling phase, these two parameters represent unpacking. In a function call, *
tuples or lists can be unpacked into different parameters (when packing, they are packed into tuples, and unpacking can unpack tuples, lists , and sets ) . In a function call, **
a dictionary is unpacked as a key/value, making it a single keyword argument. Let's look at a few examples.
*
When the function is called:
def func(a, b, c, d):
print(a, b, c, d)
args = (1, 2, 3, 4)
func(*args) # * 在函数调用中使用
args = [1, 2, 3, 4]
func(*args)
args = {
1, 2, 3, 4}
func(*args)
output:
1 2 3 4
1 2 3 4
1 2 3 4
**
When the function is called:
def func(a, b, c, d):
print(a, b, c, d)
kwargs = {
"a": 1, "b": 2, "c": 3, "d": 4}
func(**kwargs) # ** 在函数调用中使用
output:
1 2 3 4
Summary:
When defining a function, *
and **
means packing, but inside the function body, *
and **
means unpacking. Look at an example:
def foo(*args, **kwargs):
print(args) # 未解包参数
print(*args) # 解包参数
v = (1, 2, 4)
d = {
'a': 1, 'b': 12}
foo(v, d)
output:
((1, 2, 4), {
'a': 1, 'b': 12})
(1, 2, 4) {
'a': 1, 'b': 12}
It can be seen that at the beginning, v and d as a whole are packed into a tuple, and then unpacked back to the original form.
Looking at an example:
def foo(*args, **kwargs):
print(args) # 未解包参数
print(*args) # 解包参数
print(kwargs) # 未解包参数
v = (1, 2, 4)
d = {
'a': 1, 'b': 12}
foo(v, d, a=1, b=-2)
output:
((1, 2, 4), {
'a': 1, 'b': 12})
(1, 2, 4) {
'a': 1, 'b': 12}
{
'a': 1, 'b': -2}
At this point, v
, is packed d
by *args打
, and then unpacked, a=1
, b=-2
(keyword) is **kwargs
packed by.