Chapter 6: Overview of Functions
This article is part of the Python General Programming tutorial series, which has been fully completed. Its goal is to take you from zero to mastery of the Python programming language. This tutorial does not talk about Python in vague generalities; instead it gives refined, in-depth explanations across 5 stages and 25 chapters. So you need to study patiently to really gain something. Although it does not involve using any frameworks, it gives a holistic explanation of operating systems and network communication, and even rewrites some open-source modules and servers. After finishing it, you will not only have mastered the Python programming language, but also gained the ability to learn other programming languages quickly, to read any Python source code without obstacles, and a comprehensive understanding of computers and networks. For complete beginners, it is an excellent textbook for entering the computer field and mastering a programming language. For those who already have some Python foundation, I believe this tutorial will take your skills to the next level.
I. Introduction to Functions
1. Why Functions Exist
Based on the project in the first stage, we can see the following problems, and functions are exactly what solve them.
- The program’s organizational structure is unclear and readability is poor
- Code redundancy
- It is extremely hard to manage and maintain, and extensibility is poor
2. Notes on Defining and Calling Functions
Note that a function name is essentially similar to a variable (when you print a variable, what you see directly is the variable’s value—Guido did a conversion internally to make it more intuitive for you; when you print a function, what you see directly is a memory address. At the low level, both variable names and function names correspond to memory addresses, because the process of defining them is the process of allocating memory), so the naming rules for function names are the same as those for variable names. A function is like a capability, and that capability performs an action, so by convention function names are written as verbs or verb phrases.
- A tool with a certain capability is a function in a program
- The process of preparing the tool in advance is defining the function
- Taking the prepared tool and using it is calling the function
所以函数的使用必须遵循:先定义,再调用
II. Defining Functions
1. Notes on Function Definition
def 函数名(参数1,参数2,...):
"""
文档注释
"""
代码块1
代码块2
代码块2
return 返回值
# def: the keyword for defining a function
# Function name: used to call the function
# The function name must reflect what the function does
# Docstring: describes the function to improve its readability
# Code block: the code that implements the function
# return: the function's return value
Now that we have functions, if we need to implement the following printing feature, we can do it with functions.
# print('========================')
# print('hello Albert')
# print('========================')
# 1 Define first
def print_sym(sym, count): # print_sym=<function print_msg at 0x000001B2A33698C8>
print(sym * count)
def print_msg(msg):
print('\033[045m%s\033[0m' % msg)
# 2 Then call (a function name followed by parentheses calls the function)
print(print_sym) # A function name corresponds to a memory address
print_sym('#', 30)
print_msg('hello Albert')
print_sym('#', 30)
# About function parameters, for now you only need to know: pass as many arguments when calling as there are parameters in the definition.
2. Three Forms of Function Definition:
# 1 Function with parameters: when the function body depends on the arguments passed in, we use a function with parameters
def max2(x, y): # x=100,y=101
if x > y:
print(x)
else:
print(y)
max2(100, 101)
# 2 Function without parameters: when the function body does not use passed-in arguments, passing arguments is obviously unnecessary
def func():
print('----------------------')
print('---soft run-----------')
print('----------------------')
def interact():
name = input('username>>: ').strip()
pwd = input('password>>: ').strip()
print(name, pwd)
interact() # No parameters in the definition means no arguments are needed when calling
func() # No parameters in the definition means no arguments are needed when calling
# 3 Empty function: the function body is pass; define the functional structure in advance and run a feature by calling the function
def auth(username, password):
"""
这是一个用户认证功能,在Pycharm中,当输入三引号回撤之后,下面的三行代码自动出现
:param username:
:param password:
:return:
"""
def put():
"""
上传功能
:return:
"""
pass
def get():
"""
下载功能
:return:
"""
pass
def ls():
"""
list contents
:return:
"""
pass
III. Calling Functions
1. Notes on Calling Functions
# Using a function must follow the principle: define first, call later
# Note: calling a function directly without defining it first is like referencing a variable name that does not exist
# Definition stage: only the syntax is checked; the function body is not executed
# Call stage: find the function's memory address by its name, then execute the function body
# A function name followed by parentheses calls the function
# Definition stage
def foo():
print('from foo')
bar()
def bar():
print('from bar')
# Call stage
foo()
"""
会报错
# 定义阶段
def foo():
print('from foo')
bar()
# 调用阶段
foo()
def bar():
print('from bar')
"""
2. Three Ways to Call a Function
# 1 A basic call
def func():
print('from func')
func()
# 2 Call and assign the return value to a variable
def max2(x, y):
if x > y:
return x
else:
return y
res = max2(10, 3)
print(res)
res = max2(10, 3) * 100 # Similar to the above, doing further computation on the return value
print(res)
# 4 Pass the return value in again as an argument
res = max2(max2(10, 3), 11)
print(res)
IV. Function Return Values
1. Notes on Return Values
# When should there be a return value?
# When, after the function body finishes running, a result needs to be returned to the caller
2. Three Forms of Return Values
# 1 No return, or nothing after return: the return value is None
def func():
pass
def func1():
return
def func2():
return None
res = func()
res1 = func1()
res2 = func2()
print(res)
print(res1)
print(res2)
# 2 return followed by one value returns that value itself
def func3():
return 1
res3 = func3()
print(res3)
# 3 return can take comma-separated values, returning multiple values as a tuple to the caller
def func4():
return 1, 2, [1, 2, 3]
res4 = func4()
print(res4) # (1, 2, [1, 2, 3])
3. Two Things to Note About return
- There is no type restriction on what return returns
- return marks the end of a function. A function can contain multiple returns, but as soon as one is executed, the function ends immediately and the value after return becomes the return value of this call
def func5():
print('first')
return 1
print('second')
return 2
print('third')
return 3
res5 = func5()
print(res5)
V. Function Parameters
1. Parameters and Arguments
"""
形参(形式参数):指的是在定义函数时,括号内定义的参数,形参其实就变量名
实参(实际参数):指的是在调用函数时,括号内传入的值,实参其实就是变量的值
"""
# x,y are parameters
def func(x, y): # x=10, y=11
print(x)
print(y)
# 10,11 are arguments
func(10, 11)
"""
注意:
实参值(变量的值)与形参(变量名)的绑定关系只在函数调用时才会生效/绑定
在函数调用结束后就立刻解除绑定
"""
2. Five Ways of Passing Arguments to Functions with Parameters
(1) Positional Arguments
The parameters and arguments discussed above are two concepts of functions with parameters. Next come the ways of passing arguments, and positional arguments are the most basic one. Position means order: positional parameters are parameters defined one after another from left to right.
# Parameters defined by position when defining a function are called positional parameters
def foo(x, y, z):
print(x, y, z)
"""
注意:
位置形参的特性是:在调用函数时必须为其传值,而且多一个不行,少一个也不行
"""
# Arguments defined by position when calling a function are called positional arguments
# foo(1,2) # Error
# foo(1,2,3,4) # Error
foo(1, 3, 2) # x = 1, y = 3, z = 2
"""
注意:位置实参会与形参一一对应
"""
(2) Keyword Arguments
When calling a function, arguments defined in the form key=value are called keyword arguments. Keyword arguments pass values to parameters in keyword form, given positional parameters, so their difference from positional arguments lies mainly in how the arguments are passed.
def foo(x, y, z):
print(x, y, z)
"""
注意:
1 相当于直呼其名地为形参传值,意味着即便是不按照顺序定义,仍然能为指定的参数传值
foo(2,1,3) # x=2,y=1,z=3
foo(y=2,x=1,z=3) # x=1,y=2,z=3
2 在调用函数时,位置实参与关键字实参可以混合使用,但必须遵循形参的规则
foo(1,z=3) # 报错
3 不能为同一个形参重复传值
foo(1,x=1,y=3,z=2) # 报错
4 位置实参必须放到关键字实参的前面
foo(y=3,z=2,1) # 报错
"""
foo(1, z=3, y=2)
(3) Default Parameters
So far we have covered only one kind of parameter, positional parameters, and two kinds of arguments, positional arguments and keyword arguments. Next we will cover another kind of parameter, called a default parameter. It means that a parameter has already been assigned a value at the definition stage; such a parameter is called a default parameter.
# 1 It already has a value at the definition stage, which means a value can be omitted at the call stage
def register(name, age, sex='male'):
print(name, age, sex)
register('Albert', 18, )
register('James', 34, )
register('林志玲', 20, 'female')
register('周星驰', 50)
# 2 Positional parameters must come before default parameters
# def func(y=1,x): # Error
# pass
# 3 The value of a default parameter is assigned only once, at the definition stage; that is, it is fixed at definition time
m = 10
def foo(x, y=m):
print(x, y)
m = 'a' # The default parameter inside foo does not change
foo(1)
foo(1, 11)
# 4 The value of a default parameter should be an immutable type (important)
# Suppose the default parameter is not an immutable type; take a list as an example
def register(name, hobby, l=[]):
l.append(hobby)
print(name, l)
register('Kobe', 'play') # Kobe ['play'] everything is fine~
register('James', 'read') # James ['play', 'read'] what?!
register('Albert', 'music') # Albert ['play', 'read', 'music'] this is the BUG that appears when it is not an immutable type
# The data goes wrong because every call modifies the same list
# To achieve the same functionality, the corrected version is as follows
def register(name, hobby, l=None):
if l is None:
l = []
l.append(hobby)
print(name, l)
register('Kobe', 'play')
register('James', 'read')
register('Albert', 'music')
# Use cases:
# For values that change often, define the corresponding parameter as a positional parameter
# For values that are the same in most cases, define the corresponding parameter as a default parameter
(4) Variable-Length Arguments
<1> Basic Use of Variable-Length Arguments
Variable length means the number of arguments is not fixed. Arguments can be defined by position or by keyword, so variable-length arguments mean that the number of arguments defined in either of these two forms is not fixed. However, arguments ultimately pass values to parameters, so parameters must have two corresponding solutions to handle these two forms of variable-length arguments respectively.
# * receives all the overflow positional arguments, saves them as a tuple and assigns it to a variable args (any name works; args is the convention)
def foo(x, y, z, *args): # args=(4,5,6,7,8)
print(x, y, z)
print(args)
foo(1, 2, 3, 4, 5, 6, 7, 8, )
# ** receives all the overflow keyword arguments, saves them as a dictionary and assigns it to kwargs
def foo(x, y, z, **kwargs): # kwargs={'c':3,'a':1,'b':2}
print(x, y, z)
print(kwargs)
foo(x=1, y=2, z=3, a=1, b=2, c=3)
<2> Star and Double Star (Unpacking)
In many cases the role of 【*】 is unpacking; we gave a brief explanation of this when discussing the difference between the list methods append and extend.
# Whenever an argument is prefixed with *, its value is unpacked
def foo(x, y, z, *args): # args=([4,5,6,7,8],)
print(x, y, z)
print(args)
foo(1, 2, 3, *[4, 5, 6, 7, 8]) # foo(1,2,3,4,5,6,7,8)
foo(1, 2, 3, *(4, 5, 6, 7, 8)) # foo(1,2,3,4,5,6,7,8)
foo(1, 2, 3, *'hello') # foo(1,2,3,'h','e','l','l','o')
def foo(x, y, z):
print(x, y, z)
# foo(*[1, 2, 3, 4]) # foo(1,2,3,4) # Error
# foo(*[1, 2, ]) # foo(1,2,) # Error
foo(*[1, 2, 3]) # foo(1,2,3)
# Whenever an argument is prefixed with **, its value is unpacked
def bar(x, y, z, **kwargs):
print(x, y, z)
print(kwargs)
bar(1, 2, 3, **{'a': 1, 'b': 2}) # foo(1,2,3,b=2,a=1)
def boo(x, y, z):
print(x, y, z)
# boo(1, **{'z': 3, 'y': 2, 'x': 111}) # Error boo(1,z=3,y=2,x=111)
boo(1, **{'z': 3, 'y': 2}) # foo(1,z=3,y=2)
# Use case for *
def sum2(*args):
res = 0
for num in args:
res += num
return res
print(sum2(1, 2, 3, 4, 5, 6, 7))
# Use case for **
def auth(name, pwd, **kwargs):
print(name)
print(pwd)
print(kwargs)
auth(name='Albert', pwd='123')
auth(name='Albert', pwd='123', group='group1')
<3> Using Them Together (Key Point)
def index(name, age, gender):
print('welcome %s %s %s' % (name, age, gender))
def wrapper(*args, **kwargs): # args=(1,2,3),kwargs={'x':1,'y':2,'z':3}
# print(args)
# print(kwargs)
index(*args, **kwargs) # index(*(1,2,3),**{'x':1,'y':2,'z':3}) # index(1,2,3,z=3,y=2,x=2)
# wrapper(1,2,3,x=1,y=2,z=3) # Error
wrapper(name='Albert',age=18,gender='male')
wrapper('Albert', age=18, gender='male')
wrapper('Albert', 18, gender='male')
wrapper('Albert', 18, 'male')
"""
执行过程:
wrapper的所有参数都原封不动地传给index,而index函数只接收三个位置参数
星与星星的组合使用在源码中非常常见,这也是装饰器的核心之一,这非常重要。
"""
(5) Keyword-Only Arguments
<1> Introduction to Keyword-Only Arguments
Parameters after the 【star】 are all keyword-only arguments. Their characteristics are that they must be given a value, the caller is constrained to pass values in the form key=value, and the caller must use the specified key names.
Without keyword-only arguments, when we need the constraints above, we would have to do it as in the following code.
def auth(*args, **kwargs):
"""
使用方式auth(name="Albert",pwd="123")
:param args:
:param kwargs:
:return:
"""
if len(args) != 0:
print('必须用关键字的形式传参')
return
if 'name' not in kwargs:
print('必须用指定的key名name')
return
if 'pwd' not in kwargs:
print('必须用指定的key名pwd')
return
name = kwargs['name']
pwd = kwargs['pwd']
print(name, pwd)
print(help(auth)) # Print the docstring
auth(x='Albert', y='123')
auth('Albert', '123')
auth('Albert', pwd='123')
auth(name='Albert', pwd='123') # Constrain the caller to pass values in the form key=value
<2> Using Keyword-Only Arguments
# Using keyword-only arguments
def foo(x, y, *, z):
print(x, y, z)
# foo(1,2) # Error
# foo(1,2,3) # Error
# foo(1,2,a=3) # Error
foo(1, 2, z=3)
# In fact, the core of keyword-only arguments is the *; args is just a variable, and having it or not makes no difference
def auth(*args, name, pwd):
print(name, pwd)
auth(pwd='123', name='Albert')
# Keyword-only arguments are a hard restriction, but Python's style is based on convention rather than restriction
def register(name, age):
"""
我们不会在这里添加对name和age的要求限制
:param name:
:param age:
:return:
"""
print(type(name), type(age))
register(123, [1, 2, 3])
# The most complex case of accepting parameters once keyword-only arguments are used
def foo(x, y=1, *args, z, m=2, **kwargs): # m=2 is the default value of a keyword-only argument
pass
# In general, the two forms foo1 and foo2 are enough
def foo1(x, y=1):
pass
def foo2(x, *args, **kwargs):
pass