Chapter 5: function usage and underlying analysis
Function is also an object, which is used for memory underlying analysis
In Python, "everything is an object". In fact, after executing the def definition function, the system creates the corresponding function object. We perform the following procedure and then explain:
#Test functions are also objects def print_star(n): print("*"*n) print(print_star) print(id(print_star)) c = print_star c(3) print("*****************************") def test01(): print("sxtsxt") test01() c = test01 c() print("*****************************") print(id(test01())) print(id(test01)) print(id(c)) print("*****************************") print(type(test01())) print(type(test01)) print(type(c)) Operation results: <function print_star at 0x0000027C2A01D558> 2732303963480 *** ***************************** sxtsxt sxtsxt ***************************** sxtsxt 140703584378080 2732305045688 2732305045688 ***************************** sxtsxt <class 'NoneType'> <class 'function'> <class 'function'> Process finished with exit code 0
When the above code executes def, a function object will be created in the system and printed_ Star is referenced by this variable:
After we execute "c=print_star", it is obvious that print_ The value of star variable is assigned to variable c, and the memory graph becomes:
Obviously, we can see the variables c and print_star points to the same function object. Therefore, execute c(3) and print_ The effect of star (3) is completely consistent. In Python, parentheses mean calling a function. Without parentheses, python treats functions as normal objects.
Similar to this core principle, we can also do the following operations:
zhengshu = int
zhengshu("234")
Obviously, we assign the built-in function object int() to the variable zhengshu, so that both zhengshu and int point to the same built-in function object. Of course, this is limited to the principle explanation, which is not necessary in actual development.
Scope of variables (global and local variables)
The scope of a variable is called the scope of the variable. Variables with the same name in different scopes do not affect each other. Variables are divided into global variables and local variables.
Global variables:
- Variables declared outside function and class definitions. The scope is the defined module, starting from the defined location until the end of the module.
- Global variables reduce the generality and readability of functions. The use of global variables should be avoided as much as possible.
- Global variables are generally used as constants.
- To change the value of the global variable in the function, use global to declare it
Local variables:
5. Variables declared in the function body (including formal parameters).
6. The reference of local variables is faster than that of global variables, which is preferred.
7. If the local variable and the global variable have the same name, hide the global variable in the function and only use the local variable with the same name
Schematic diagram of local variable and all variable memory:
The blue box represents the stack frame when the local variable is referenced. It can be understood that b (blue) is stored in it and the global variable a (red) is stored in the stack.
[operation] scope test of global variables
#Test global and local variables a=100 def f1(): global a #If you want to change the value of a global variable within a function, add the global keyword declaration print(a) a=300 f1() print(a) Operation results: 100 300
[operation] test of global variable and local variable with the same name
a=100 def f1(): a = 3 #Local variable with the same name print(a) #The local variable is 3 f1() print(a) #a is still 100, unchanged Operation results: 3 100
[operation] output local variables and global variables
a = 100 def f1(a,b,c): print(a,b,c) print(locals()) #Local variables for printouts print("#"*20) print(globals()) #Global variables for printouts f1(2,3,4) Operation results: 2 3 4 {'a': 2, 'b': 3, 'c': 4} #################### {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001B0F1095A08>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:/Users/Lenovo/PycharmProjects/mypro_func/func_05.py', '__cached__': None, 'a': 100, 'f1': <function f1 at 0x000001B0F17158B8>} Process finished with exit code 0
Local variable and global variable efficiency test
The query and access speed of local variables is faster than that of global variables. It is preferred to use them, especially in loops.
In places with special emphasis on efficiency or where there are many cycles, the operation speed can be improved by changing the global variable into local variable.
[operation] test the efficiency of local variables and global variables
#[operation] test the efficiency of local variables and global variables import math import time def test01(): start=time.time() for i in range(10000000): math.sqrt(30) end=time.time() print("time consuming{0}".format(end-start)) def test02(): b=math.sqrt start=time.time() for i in range(10000000): b(30) end=time.time() print("time consuming{0}".format(end-start)) test01() test02() Operation results: Time consuming 1.6264605522155762 Time consuming 1.240330457687378
Transfer of parameters
The parameter transfer of a function is essentially the assignment operation from an actual parameter to a formal parameter. In Python, "everything is an object", and all assignment operations are "referenced assignment". Therefore, parameter passing in Python is "reference passing", not "value passing". The specific operations are divided into two categories:
- The "write operation" on the "variable object" directly acts on the original object itself.
- A "write operation" on the "immutable object" will generate a new "object space" and fill this space with new values. (it has the effect of "value transfer" in other languages, but not "value transfer")
Variable objects include dictionaries, lists, collections, custom objects, etc
Immutable objects include numbers, strings, tuples, function s, etc
Passing a reference to a mutable object
The passed parameter is a variable object (for example, list, dictionary, other customized variable objects, etc.), and the actual passed parameter is the reference of the object. Instead of creating a new object copy in the function body, you can directly modify the passed object.
[operation] parameter passing: pass the reference of variable object
#Transfer of test parameters #Test variable objects a=[10,20] print(id(a)) print(a) print("***********************************") def test01(m): print(id(m)) m.append(300) print(id(m)) test01(a) print(a) print(id(a)) Operation results: 3070225830280 [10, 20] *********************************** 3070225830280 3070225830280 [10, 20, 300] 3070225830280 Process finished with exit code 0
Passes a reference to an immutable object
The passed parameter is an immutable object (for example, int, float, string, tuple, Boolean value), and the actual passed parameter is the reference of the object. During "assignment operation", a new object will be created because the immutable object cannot be modified.
[operation] parameter passing: pass the reference of immutable object
The passed parameter is an immutable object (for example, int, float, string, tuple, Boolean value), and the actual passed parameter is the reference of the object. During "assignment operation", a new object will be created because the immutable object cannot be modified.
[operation] parameter passing: pass the reference of immutable object
#Test immutable objects a=100 def test01(n): print("n:",id(n)) n=n+200 print("n:",id(n)) print(n) test01(a) print("a:",id(a)) print(a) Operation results: n: 140703499005424 n: 2136718083664 300 a: 140703499005424 100
Obviously, through the id value, we can see that N and a start with the same object. After assigning a value to N, n is the new object.
Light copy and deep copy
In order to better understand the underlying principle of parameter transfer, we need to explain "shallow copy and deep copy". We can use built-in functions: copy (shallow copy) and deep copy (deep copy).
Shallow copy: do not copy the contents of the shell sub object, but copy the reference of the shell sub object.
import copy def testCopy(): a = [10, 20, [5, 6]] b = copy.copy(a) print("a:", a) print("b:", b) b.append(30) b[2].append(7) print("Shallow copy......") print("a:", a) print("b:", b) testCopy() Operation results: a: [10, 20, [5, 6]] b: [10, 20, [5, 6]] Shallow copy...... a: [10, 20, [5, 6, 7]] b: [10, 20, [5, 6, 7], 30]
Deep copy: it will copy all the memory of the sub object, and the modification of the sub object will not affect the source object
def testDeepCopy(): a = [10, 20, [5, 6]] b = copy.deepcopy(a) print("a:", a) print("b:", b) b.append(30) b[2].append(7) print("Deep copy......") print("a:", a) print("b:", b) testDeepCopy() Operation results: a: [10, 20, [5, 6]] b: [10, 20, [5, 6]] Deep copy...... a: [10, 20, [5, 6]] b: [10, 20, [5, 6, 7], 30]
Passing an immutable object contains children that are mutable
#When passing immutable objects. The child objects contained in immutable objects are mutable. The variable object is modified in the method, and the source object is also changed. a=(10,20,[5,6]) def test01(m): print(m) print("id_m:",id(m)) m[2][0]=888 print(m) print("id_m:", id(m)) test01(a) print(a) print("id_a:",id(a)) Operation results: (10, 20, [5, 6]) id_m: 2100977373000 (10, 20, [888, 6]) id_m: 2100977373000 (10, 20, [888, 6]) id_a: 2100977373000
Sketch Map
Several types of parameters
Position parameters
When a function is called, the arguments are passed in position order by default, and the number and formal parameters need to match. Parameters passed by location are called location parameters.
Default value parameter
We can set default values for some parameters so that they are optional when passed. This is called the default value parameter. The default value parameter is placed after the positional parameter.
Named parameters
We can also pass parameters according to the name of the formal parameter, which is called "named parameter", also known as "keyword parameter".
#Types of test parameters: location parameter, default value parameter and named parameter def test01(a,b,c,d): print("{0}-{1}-{2}-{3}".format(a,b,c,d)) def test02(a,b,c=50,d=88): print("{0}-{1}-{2}-{3}".format(a,b,c,d)) test01(10,20,30,40) #Position parameters #test01(10,20) #The position parameters do not match, and an error is reported! test01(d=20,c=15,a=88,b=0) #Named parameter, matched by parameter name test02(10,5) #The default value parameter is placed after the position parameter test02(10,5,8) Operation results: 10-20-30-40 88-0-15-20 10-5-50-88 10-5-8-88
Variable parameters
Variable parameters refer to "variable number of parameters". There are two cases:
- *param (an asterisk) collects multiple parameters into a "tuple" object.
- **param (two asterisks), which collects multiple parameters into a "dictionary" object.
[operation] test variable parameter processing (tuple and Dictionary)
#Test variable parameter processing (tuple and Dictionary) def f1(a,b,*c): print(a,b,c) f1(10,20,5,6,7) def f2(a,b,**c): print(a, b, c) f2(10,20,name="gaoqi",age=18) def f3(a,*b,**c): print(a, b, c) f3(10,11,12,13,name="gaoqi",age=18) Operation results: 10 20 (5, 6, 7) 10 20 {'name': 'gaoqi', 'age': 18} 10 (11, 12, 13) {'name': 'gaoqi', 'age': 18} Process finished with exit code 0
Force named parameters
Add a new parameter after the asterisk "variable parameter", which must be "forced to name the parameter" when calling.
[operation] force the use of named parameters
#Force the use of named parameters def f1(*a,b,c): print(a,b,c) # f1(2,3,4) #Will report an error. Since a is a variable parameter, all 2, 3 and 4 are collected. Causes b and c to have no assignment. f1(10,20,b=30,c=88) Operation results: (10, 20) 30 88
lambda expressions and anonymous functions
Lambda expressions can be used to declare anonymous functions. Lambda functions are a simple way to define functions on the same line. lambda
Function actually generates a function object. lambda expressions can only contain one expression, not complex statements. The evaluation result of the expression is the return value of the function.
The basic syntax of lambda expressions is as follows:
lambda arg1,arg2,arg3...: < expression >
arg1/arg2/arg3 are arguments to the function< The expression > is equivalent to the function body. Operation result: the operation result of the expression.
[operation] lambda expression usage
#Testing lambda expressions and anonymous functions f=lambda a,b,c,d:a*b*c*d print(f) print(f(2,3,4,5)) g=[lambda a:a*2,lambda b:b*2,lambda c:c*2] print(g[0](6),g[1](8),g[2](9)) def test01(a,b,c,d): print("*****") return a*b*c*d h=[test01,test01] #Functions are also objects print(h[0](5,6,7,8),h[1](1,2,3,4)) Operation results: <function <lambda> at 0x000002F76DEDC558> 120 12 16 18 ***** ***** 1680 24
eval() function
Function: evaluate the string str as a valid expression and return the calculation result.
Syntax: Eval (source [, global [, locals]]) - > value
Parameters:
source: a Python expression or code object returned by the function compile()
globals: optional. Must be a dictionary
locals: optional. Arbitrary mapping object
# Test eval() function s="print('abcde')" eval(s) a=10 b=20 c=eval("a+b") print(c) dict1=dict(a=100,b=200) d=eval("a+b",dict1) print(d) Operation results: abcde 30 300
The eval function will execute the string as a statement, so it will be injected with security risks. For example, the string contains a statement to delete a file. Then you're in big trouble. Therefore, be careful when using!!!
Recursive function
Recursive functions refer to calling their own functions directly or indirectly within the function body. Recursion is similar to the "mathematical induction" that everyone has learned in middle school mathematics. Each recursive function must contain two parts:
- Termination conditions
Indicates when the recursion ends. It is generally used to return a value and will not call itself. - recursive procedure
Associate the value of step n with step n-1.
Recursive functions will create a large number of function objects and consume too much memory and computing power. Use caution when dealing with large amounts of data.
Test recursive functions:
#Testing the basic principles of recursive functions def test01(n): print("test01:",n) if n==0: print("over") else: test01(n-1) print("*****",n) def test02(): print("test02") test01(4) Operation results: test01: 4 test01: 3 test01: 2 test01: 1 test01: 0 over ***** 0 ***** 1 ***** 2 ***** 3 ***** 4 Process finished with exit code 0
Memory diagram:
[operation] use recursive function to calculate factorial
#Use recursive functions to calculate factorials def factorial(n): if n==1: return 1 else: return n*factorial(n-1) d=factorial(5) print(d) print(id(d)) print(type(d)) Operation results: 120 140703557595248 <class 'int'>