A detailed explanation of lists, tuples, dictionaries, collections, generators, iterators, iterable objects, zip, enumerate, packaging, unpacking

foreword

  Lists, tuples, dictionaries, collections, and generators are all pythoniterable objects, which are often forgotten when used. This blog post summarizes them.

1. List

  A list ( list) is Pythona 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 0the 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

  1. Traverse : for/whileLoop through the list;
  2. 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;
  3. find : in,not in;
  4. Modification : Specify index modification, such as li[2] = 'a';
  5. Delete : del[i],pop(),remove()delete according to the subscript, delete the last element, delete according to the element value;
  6. Sorting : li.sort(reverse=False), li = sorted(li, reverse=False)one is an object, the other is a function implementation;
  7. 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 <)>
  8. Nesting : Lists can be nested, such as [1,2,3,[1,2,[1,2,3]]];
  9. slice : asli[:3]

2. Tuple

2.1. Rules of use

  PythonA 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.

  1. traversal: for/whileloop;
  2. Count: len()used to calculate the number of tuples;
  3. slice: tu[2:];
  4. Conversion: tuple(li)convert the list to a tuple;
  5. 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

  1. 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);
  2. Added: direct dicts['num'] = 110ornew_dict = {"num": 110}, dicts.update(new_dict)
  3. 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 the valuevalue, and clear the dictionary;
  4. 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 use get()the method, num = dicts.get('num'). You can also use inthe method;
  5. Change: directly modify the value corresponding to the key, dicts['a'] = 'xiaoming';
  6. Length: Get the number of dictionary elements, len();
  7. key: dicts.keys();
  8. 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.

  1. traversal: for/whileloop;
  2. add: my_set.add(6);
  3. 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;
  4. find: in;
  5. length: len();
  6. Sets: |, &, -, ^are union, intersection, difference, and cross-complement respectively.

5. Iterators, generators, iterable objects

5.1. Iterators

Definition of iterator type:

  1. When two methods __next__and are defined in the class__iter__
  2. __iter__The method needs to return the object itself, ie:self
  3. __next__method, return the next data, if there is no data, you need to throw an stopIterationexception.

  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 Stoplterationan exception is thrown to terminate the iteration. The iterator has no length, cannot lenbe used to get the length, and has no lenproperties. 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 yiledkeyword. That is, if a function contains yieldkeywords (no matter how many there are yield), the function becomes a generator.
yieldwhat's the effect?

  • Every time the program encounters the keyword in the code yield, it will return the result
  • vieldReserve 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. yieldIt can be understood that after returnone yieldis 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 yielddo 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 yieldmore advanced usage, you can move to this video: Python development and programming advanced tutorial, thread communication/decorator/iterator/asynchronous IO/magic method/reflection .

sendThe role of the method:

  • nextCall the generator like a method (there are two ways to call the generator: method nextand sendmethod)
  • sendWhen 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 nextthe method directly to activate the builder
  • Call send(None)the method to activate the builder

5.3. Iterable objects

  In Pythonany 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 forbe 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 pythonthrough 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 rangemethod 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 rangemethod:

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 yielditerates over elements.
  • Iterable object: generally a data container, and retains an iterative interface __iter__.
  1. __iter__The method is left to the program to obtain the iterator object iter();
  2. After obtaining the iterable object, pass __next__or next()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 fora 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 LargeListIteratoran instance and passing a large list to it, we can foruse 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.

  1. If the number of elements of each iterator is inconsistent, the length of the returned list is the same as the shortest object
  2. Using *the notation operator, tuples can be unpacked into lists.

pythonCommon 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 ziphow 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,l2the output is the content of the list,l1,l2is 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 method Pythonof 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 print zip()the returned iterable, it will appear as a class identifier, rather than outputting the contents of the tuple directly. This is because in Python, 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. iterationThe parameters are parameters that need to be traversed, such as dictionaries, lists, tuples, etc., and the start parameter is the starting 0parameter 0. enumerateThe function has two return values, the first return value is startthe number from the parameter, and the second parameter is iterationthe 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 0starts 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 dby *args打, and then unpacked, a=1, b=-2(keyword) is **kwargspacked by.

Guess you like

Origin blog.csdn.net/qq_38683460/article/details/130965042