Python foundation course day 6

Posted by Jay_Seagrave on Sun, 23 Jan 2022 22:35:29 +0100

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:

  1. Variables declared outside function and class definitions. The scope is the defined module, starting from the defined location until the end of the module.
  2. Global variables reduce the generality and readability of functions. The use of global variables should be avoided as much as possible.
  3. Global variables are generally used as constants.
  4. 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:

  1. The "write operation" on the "variable object" directly acts on the original object itself.
  2. 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:

  1. *param (an asterisk) collects multiple parameters into a "tuple" object.
  2. **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:

  1. Termination conditions
    Indicates when the recursion ends. It is generally used to return a value and will not call itself.
  2. 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'>

Topics: Python Back-end