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:
Liao Xuefeng's official website
[zhenguo]