Some advanced features of Python

Some advanced features of Python

Advanced features

List generation

1. When writing a list generator, put the element x **2 to be generated in front, followed by a for loop, which is a general usage.

>>> b = [x**2 for x in range(0,11)]
>>> b
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

2. List generation can also output the names of all files and folders in a directory.

>>> b = [d for d in os.listdir(r'C:\Users\Administrator\Desktop\2005 Primary basic class')]
>>> b
['.idea', '2005-Lin Yonghao-examination.py', 'day02', 'day02_homework', 'day03', 'day03_homework', 'day04', 'day04_homework', 'day05',
 'day06', 'day07', 'day08', 'day09', 'day10', 'day11', 'day12', 'day13', 'day14', 'day15', 'day16', 'day17', 'day18']

3. The list generation formula can have two parameters

>>> a = range(5)
>>> b = ['a', 'b', 'c', 'd', 'e']
>>> c = [str(x) + str(y) for x, y in zip(a, b)]
>>> c
['0a', '1b', '2c', '3d', '4e']

>>> a = {'a': 1, 'b': 2, 'c': 3}
>>> b = [k + '=' + str(v) for k, v in a.items()]
>>> b
['a=1', 'b=2', 'c=3']

4. In addition, you can also keep up with if, which is used to filter, not an expression, and cannot be followed by else

>>> b = [x**2 for x in range(11) if x % 2 == 0]
>>> b
[0, 4, 16, 36, 64, 100]

If you want to add else, change the writing. At this time, if before for Else is an expression, and each x is based on if Else calculates a result and generates an expression.

>>> [x if x % 2 == 0 else -x for x in range(1, 11)]
[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

There are several other more advanced uses

# Group the lst according to the specified function fn
def bifurcate_by(lst, fn):
    return [
        [x for x in lst if fn(x)],
        [x for x in lst if not fn(x)]
    ]
print(bifurcate_by(['beep', 'boop', 'foo', 'bar'], lambda x: x[0] == 'b'))
# [['beep', 'boop', 'bar'], ['foo']]
# Group values
def bifurcate(lst, filter):
    return [
        [x for i, x in enumerate(lst) if filter[i] == True],
        [x for i, x in enumerate(lst) if filter[i] == False]
    ]
print(bifurcate(['beep', 'boop', 'foo', 'bar'], [True, True, False, True])) 
# [['beep', 'boop', 'bar'], ['foo']]

generator

The generator can generate elements while iterating,

The first way to create a generator is to replace the [] generated by the list with ()

>>> gene = (x * x for x in range(10))
>>> gene
<generator object <genexpr> at 0x0000015152CE1D68>
# How to call it? The first call method: next()
>>> next(gene)
0
>>> next(gene)
1
>>> next(gene)
4

We can also use the for loop to continuously call the generator (essentially next()), because gene is both a generator object and an iterative object.

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)

The second way to create a generator is to define a function that identifies the return value with the yield keyword

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)

In addition to returning the corresponding value, yield also has an important function: whenever the program finishes executing the statement, the program will pause execution. When it is executed again, it will continue to execute from the last returned yield statement

>>> o = odd() # Create a generator object
>>> next(o)   # Gets the first return value
step 1
1             
>>> next(o)   # When next(o) is called, the program will continue from print('step 2 ')
step 2
3 
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

iterator

Iterator: container class object that supports iteration.
These objects that can act directly on the for loop are collectively referred to as iteratable objects: iteratable.
You can use isinstance() to determine whether an object is an iteratable object:

>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
# list,dict,str, generator, and generator functions with yield are all iteratable objects

An object that can be called by the next() function and continuously return the next value is called an Iterator.
You can use isinstance() to determine whether an object is an Iterator object:

>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
# Generators are also iterators
# But list,dict,str are not iterators

To change list, dict, str, etc. into Iterator, you can use the iter() function:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

Functional programming

closure

A closure is actually a function, but the return result of this function is a reference to the inner function. The inner function can reference the parameters and local variables of the outer function.

def sum(a):
    def add(b):
        return a +b
    return add
    
num2 = sum(2)
num3 = sum(3)
print(type(num2))
print(num3(3))
>>> <class 'function'>   # In fact, it returns a reference to a function
>>> 6

What is its use? In the above example, we can calculate the sum as needed.
Although we use num2 = sum(2) where a = 2 and Num3 = sum (3) where a=3, b is an unknown number. We can calculate any b+2 or b+3 according to our own needs

When we call sum(), each call will return a new function. Even if we pass in the same parameters, the results of num4() and num2() will not affect each other

num4 = sum(2)
print(num2 == num4)
>>> False
  • One thing to keep in mind when returning closures: the return function should not reference any cyclic variables or variables that will change later.
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()  # F1, F2 and F3 are the three elements of fs
>>> f1()
9
>>> f2()
9
>>> f3()
9
# Why are the results the same? When you call f1(), the referenced variable i is 3, so it will return 9
# Similarly, both f2 and f3 are
# What if you have to reference a loop variable? The method is to create another function and bind the current value of the loop variable with the parameters of the function,
# The value bound to the function parameter remains unchanged regardless of subsequent changes to the loop variable
def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i) is executed immediately, so the current value of i is passed into f()
    return fs

Anonymous function

The keyword lambda represents an anonymous function, and the x before the colon represents a function parameter.

There is a limitation to anonymous functions, that is, there can only be one expression. There is no need to write return. The return value is the result of the expression.

>>> f = lambda x: x * x
# def f(x):
#     return x*x
>>> f
<function <lambda> at 0x101c6ef28>
>>> f(5)
25

An anonymous function is also a function object. To call an anonymous function, you need to assign a variable

Anonymous functions can also be used as the return value of functions

Decorator

import time
def after_add(func):
    def add_thing():
        print('----Before decoration------')
        func()
        print('----After decoration------')
    return add_thing

def add_1():
    print((time.strftime('%H:%M:%S',time.localtime(time.time()))))
    
add_2 = after_add(add_1)  # add_2 is actually a function object
add_2()

----Before decoration------
16:15:25
----After decoration------

Using the closure knowledge we will use earlier, after_add actually forms a closure, and the decorator to be talked about is actually add_2 = after_add(add_1)

Then why do we use decorators? If you want to call add_1 before and after printing ---- before decoration ----- and ---- after decoration ----- and do not want to modify add_ What about the definition of function?
Therefore, the definition of Decorator is introduced. This way of dynamically adding functions during code running is called "Decorator".

With Python's @ syntax

import time
def after_add(func):
    print("22")
    def add_thing():
        print('----Before decoration------')
        func()
        print('----After decoration------')
    return add_thing

@after_add   add_1 = after_add(add_1)
def add_1():
    print((time.strftime('%H:%M:%S',time.localtime(time.time()))))

if __name__== '__main__':
    add_1()

    
22    
----Before decoration------
16:15:25
----After decoration------   

Remember two principles:
1,Decorators are enhanced before function calls
2,The same function is enhanced only once

Put @ after_add put in add_ At the definition of 1, it is equivalent to executing a statement

add_1 = after_add(add_1)

Original add_ The 1 () function still exists, only add with the same name_ The 1 variable points to the new function, so add is called_ 1 () will execute the new function, that is, in add_ func() function in thing(). In other words, the following example.

add_2 = after_add(add_1)  # add_2 is actually a function object
add_2()

Function decorator with parameters

def say_thing(func):
    def say_say(name):   # The parameters of the objective function are for your own use
        print("Hello")
        func(name)
    return say_say

@say_thing
def say_name(name):
    print(name)
    
print(say_name.__name__)    
say_name('Li Ming')

First say_name() has been enhanced, @ say_thing is actually equivalent to say_name= say_thing(say_name)

Print say_name.__name__ Say is displayed when_ Say, when we execute say_name("Li Ming") is equivalent to entering say_say(name), where name=='Li Ming '

Then print Hello, and then execute func(name), where name=='Li Ming ',
And this sentence will enter say_name(name), where name=='Li Ming '
To put it bluntly, say_name('Li Ming ') is equivalent to executing a statement

say_thing(say_name)('Li Ming')

First, execute say_thing(say_name) returns say_ The say function returns a function when called. The parameter is' Li Ming '

If there are multiple ornaments, it is enhanced from the inside out.

Partial function

Python's functools module provides many useful functions, one of which is Partial function

The int() function can convert a string into an integer (decimal conversion by default), and can also pass in the base parameter for N-ary conversion

>>> int('12',base=8)
10    # String '12' is converted into an integer 10 = 1 * (8 ^ 1) + 2 * (8 ^ 0) after octal conversion
>>> int('10',base=8)
8

If you want to convert a large number of binary strings, you can not only define your own functions, but also use functools partial

functools.partial helps us create a partial function. You can directly use the following code to create a new function int2:

>>> import functools
>>> int2 = functools.partial(int, base=2)  # Reset the base parameter to the default value of 2
>>> int2('1000000')
64
>>> int2('1010101')
85

Its function is to fix some parameters of a function (that is, set the default value) and return a new function. It will be easier to call this new function.

Finally, when creating a partial function, you can actually receive three parameters: function object, * args and * * kw

When incoming:

int2 = functools.partial(int, base=2)
It's actually fixed int Keyword parameters of function base

in other words

int2('10010')
amount to

kw = { 'base': 2 }
int('10010', **kw)

When incoming:

max2 = functools.partial(max, 10)  # In fact, 10 will be automatically added to the left as part of * args
max2(5,6,7)

amount to

args = (10, 5, 6, 7)
max(*args)

reference material:

python syntax sugar

Liao Xuefeng's official website

[zhenguo]

Keywords: Python string

Added by alvinshaffer on Tue, 11 Jan 2022 01:57:21 +0200