A question allows you to truly understand Python programming

When writing this topic, Zhou Huajian's slightly hoarse singing sounded uncontrollably in his mind:

The familiar song came from a distance,
why are those heart sounds so weak.
Long time no see, how are you now?
Is there such a song that
will make you gently follow and sing the theme song together with the
ups and downs of our lives ; Is there such a song that will remind you of me suddenly, make you happy and sad, so a me...




The music is over, back to the topic. I recently browsed LeetCode and found a very interesting topic. When I tried to answer in Python, I actually used sets, map functions, zip functions, lambda functions, sorted functions, and the debugging process also involved the concepts of iterators, generators, and list comprehensions. A seemingly simple topic, although the final code can be merged into one line, it almost uses the programming skills of Python once. Through this topic, maybe you will truly understand Python programming from now on.

This question is called "Lucky Numbers in the List". What is a lucky number? In a list of integers, a number is called a "lucky number" if it occurs as often as its numerical value. For example, in the list [1, 2, 2, 3], the number 1 and the number 2 appear 1 and 2 respectively, so they are lucky numbers, but 3 only appears once, and 3 is not a lucky number.

Understand the concept of lucky numbers, let's try to find the lucky numbers in the list [3, 5, 2, 7, 3, 1, 2, 4, 8, 9, 3]. This process can be divided into the following steps:

  • Find unique numbers in a list

  • Count how many times each number appears in the list

  • Find those numbers whose number of occurrences is equal to the number itself

Step 1, find the unique numbers in the list

Find the numbers that are not repeated in the list, that is, remove the repeated elements in the list, referred to as "de-duplication". The cleanest way to deduplicate is to use collections.

>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3]
>>> unique = set(arr)
>>> unique
{
    
    1, 2, 3, 4, 5, 7, 8, 9}

Step 2, count the number of times each number appears in the list

We know that the list object comes with a count() method, which can return the number of times an element appears in the list. The specific usage is as follows:

>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3]
>>> arr.count(8) # 元素8在数组arr中出现过22

Next, we only need to traverse each element after deduplication, count the number of occurrences of each of them one by one, and save it into a suitable data structure. This step is all done.

>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3]
>>> unique = set(arr) # 去除重复元素
>>> pairs = list() # 空列表,用于保存数组元素和出现次数组成的元组
>>> for i in unique:
    pairs.append((i, arr.count(i)))

>>> pairs
[(1, 1), (2, 2), (3, 3), (4, 1), (5, 1), (7, 1), (8, 2), (9, 1)]

As a novice, the code is written like this, which is already very good. However, an aspiring programmer will never rest on his laurels. Their favorite thing to do is to do everything possible to eliminate for loops, such as using map functions and filter functions to replace for loops; even if they can't refuse for loops, they will try to hide loops as much as possible, such as in list comprehensions. Since here is to call the count() method of the list for each element, it is most suitable to replace the for loop with the map function.

>>> m = map(arr.count, unique)
>>> m
<map object at 0x0000020A2D090E08>
>>> list(m) # 生成器可以转成列表
[1, 2, 3, 1, 1, 1, 2, 1]
>>> list(m) # 生成器只能用一次,用过之后,就自动清理了
[]

The map function returns a generator (generator), which can be traversed like a list, but cannot see the elements intuitively like a list, unless we use list() to convert this generator into a list (actually there is no need to convert generator to list). Please note that generators are different from iterators, or generators are a special kind of iterators that can only be traversed once, and when the traversal ends, they disappear automatically. Iterators can be traversed repeatedly. For example, the range() function returns an iterator:

>>> a = range(5)
>>> list(a)
[0, 1, 2, 3, 4]
>>> list(a)
[0, 1, 2, 3, 4]

After talking about generators and iterators, let's get back to the original topic. Using the map mapping function, we get the number of occurrences of each element, and we also need to form a tuple with the corresponding element. At this time, the zip() function is used. The zip() function creates a generator to aggregate the elements of each iterable object (iterator, generator, list, tuple, set, string, etc.), the elements are aggregated according to the same subscript, and the lengths are ignored if greater than The element with the shortest iteration object length.

>>> m = map(arr.count, unique)
>>> z = zip(unique, m)
>>> z
<zip object at 0x0000020A2D490508>
>>> list(z)
[(1, 1), (2, 2), (3, 3), (4, 1), (5, 1), (7, 1), (8, 2), (9, 1)]
>>> list(z)
[]

Obviously, the zip() function returns a generator, which can only be used once and disappears after that.

Step 3, find those numbers whose occurrence is equal to the number itself

With each element and the number of times it occurs, we just need to loop through it... no wait, why do we have to loop? We just need to filter each element and find those tuples whose occurrence is equal to the element itself, why not try the filter function filter()?

>>> def func(x): # 参数x是元组类型
    if x[0] == x[1]:
      return x

>>> m = map(arr.count, unique)
>>> z = zip(unique, m)
>>> f = filter(func, z)
>>> f
<filter object at 0x0000020A2D1DD908>
>>> list(f)
[(1, 1), (2, 2), (3, 3)]
>>> list(f)
[]

The filter function filter() accepts two parameters. The first parameter is a function to determine whether an element meets the filter conditions. The second parameter is the iterable object that needs to be filtered. The filter() function also returns a generator, which can only be used once and disappears after that.

Writing this, we're almost done. However, as an aspiring programmer, can you tolerate such a weird-looking function as func()? The answer is no! You will definitely replace it with a lambda function. Also, maybe we need to sort the results by the size of the elements. With sorting, the complete code is as follows:

>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3]
>>> unique = set(arr)
>>> m = map(arr.count, unique)
>>> z = zip(unique, m)
>>> f = filter(lambda x:x[0]==x[1], z)
>>> s = sorted(f, key=lambda x:x[0])
>>> print('幸运数是:', [item[0] for item in s])
幸运数是:[1, 2, 3]

Ultimate code, done in one line

If you have ever had the painful experience of being ravaged by code that looks like a book written in one line but can achieve complex functions, now you can also write the above code in one line and ravage others.

>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3]
>>> print('幸运数是:', [item[0] for item in sorted(filter(lambda x:x[0]==x[1], zip(set(arr), map(arr.count, set(arr)))), key=lambda x:x[0])])
幸运数是:[1, 2, 3]

Dramatic reversal, this time I really understand Python!

Some people say, why is it so troublesome? Wouldn't it be simpler and more readable to write it this way? Sure enough, I really want more!

>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3]
>>> [x for x in set(arr) if x == arr.count(x)]
[1, 2, 3]

Recommended reading

My cousin said that this Python scheduled task can earn 5,000 yuan. Do you believe me?

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326839140&siteId=291194637