Python学习笔记(6)-函数构建

函数的构建格式

在Python中,函数的构建要遵循一定的格式,如下所示:

  1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
  2. 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  3. 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  4. 函数内容以冒号起始,并且缩进。
  5. return [表达式]结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

Python函数的构建方法示意图如下所示:

mark

一个简单的函数构建

在下面的例子中,构建了一个将华氏度转换为摄氏度的函数,如下所示:

1
2
3
4
5
def fahrenheit_coverter(C):
fahrenheit = C*9/5 +32
return str(fahrenheit) +' F'
C2F = fahrenheit_coverter(35)
print(C2F)

将上述文件保存在C盘,命名为test.py,运行结果如下所示:

1
2
C:\Users\20161111>python t.py
95.0 F

在交互模式下定义函数

如果在交互模式下面定义函数,解释器会显示三个小点来提醒你定义还没有完成,如下所示:

1
2
>>> def test_python():
...

在函数定义完毕的结尾,必须输入一行空白行。定义函数会创建一个函数类的对象,如下所示:

1
2
3
4
5
6
>>> def test_python():
... print("I test def function")
...
>>> test_python()
I test def function
>>>

第一行:def test_python(): 定义函数

第二行:print(“I test def function”) 输入函数的内容,前面要空四格

第三行:是一空行,输入几个空格,直接回车就行;

第四行:继续回车,函数创建完毕,输入test_python(),显示结果。

函数的嵌套

在函数中也能创建函数,如下面的案例:

1
2
3
4
5
6
def print_lyrics():
print("I'm a lumberjack, and I'm okay.")
print("I sleep all night and I work all day.")
def repeat_lyrics():
print_lyrics()
repeat_lyrics()

运行后如下所示:

1
2
3
C:\Users\20161111>python test.py
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.

函数的参数传递

在Python中,类型属于对象,变量是没有类型的,先看下面的代码:

1
2
a = [1, 2, 3]
a = "Runoob"

在上述代码中,[1, 2, 3]是List类型,"Runoob"是String类型,而变量a是没有类型的,它仅仅是一个对象的引用(一个指针),它可以指向List类型的对象,也哦可以是指向String类型对象。

可更改(mutable)与不可更改(immutable)对象

在python中,strings,tuples,和numbers是不可更改的对象,而list,dict等则是可以修改的对象。

  1. 不可变类型:变量赋值a=5后再赋值a=10,这里实际是新生成一个int值对象10,再让a指向它,而5被丢弃,不是改变a的值,相当于新生成了a。
  2. 可变类型:变量赋值la=[1,2,3,4]后再赋值la[2]=5则是将listla的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。

python函数的参数传递

  1. 不可变类型:如整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
  2. 可变类型:如列表,字典。如fun(la),则是将la真正的传过去,修改后fun外部的la也会受影响

在python中,一切皆对象,严格意义上来讲,我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

python传不可变对象实例

看这个案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
biotest@biotest-VirtualBox:~/python3/01basic$ cat para.py
#!/usr/bin/python3
def ChangeInt(a):
a = 10
b = 2
ChangeInt(b)
print(b)
# The result is 2
biotest@biotest-VirtualBox:~/python3/01basic$ python3 para.py
2

在这个案例中,有int对象2,指向它的变量是b,在传递给ChangeInt函数时,按传值的方式复制了变量b,a和b都指向了同一个Int对象,在a=10时,则新生成一个int值对象10,并让a指向它。

python传可变对象实例

可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
biotest@biotest-VirtualBox:~/python3/01basic$ cat para2.py
#!/usr/bin/python3
def changeme(mylist):
mylist.append([1,2,3,4])
print("The value inside function is: ",mylist)
return
mylist=[10,20,30]
changeme(mylist)
print("The value outside function is: ",mylist)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 para2.py
The value inside function is: [10, 20, 30, [1, 2, 3, 4]]
The value outside function is: [10, 20, 30, [1, 2, 3, 4]]

传入函数的和在末尾添加新内容的对象用的是同一个引用,因此输出了相同的结果。

函数的参数

以下是调用函数时可使用的正式参数类型:

  1. 必需参数
  2. 关键字参数
  3. 默认参数
  4. 不定长参数

必需参数

必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。调用printme()函数,你必须传入一个参数,不然会出现语法错误,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
biotest@biotest-VirtualBox:~/python3/01basic$ cat function1.py
#!/usr/bin/python3
def printme(str):
print(str)
return
printme()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function1.py
Traceback (most recent call last):
File "function1.py", line 7, in <module>
printme()
TypeError: printme() missing 1 required positional argument: 'str'

关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。以下实例在函数 printme() 调用时使用参数名:

1
2
3
4
5
6
7
8
9
10
biotest@biotest-VirtualBox:~/python3/01basic$ cat function2.py
#!/usr/bin/python3
def printme(str):
print(str)
return
printme(str="Runoob tutorial")
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function2.py
Runoob tutorial

以下实例中演示了函数参数的使用不需要使用指定顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
biotest@biotest-VirtualBox:~/python3/01basic$ cat function3.py
#!/usr/bin/python3
def printinfo(name, age):
print("Name: ",name)
print("Age: ",age)
return
printinfo(age=50,name="runoob")
printinfo("Zhang",20)
printinfo(20,"Zhang")
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function3.py
Name: runoob
Age: 50
Name: Zhang
Age: 20
Name: 20
Age: Zhang

从结果来看,如果不按顺序输入参数,那么可以使用赋值语句将实参直接赋值给相应的形参。

默认参数

调用函数时,如果没有传递参数,则会使用默认参数。以下实例中如果没有传入age参数,则使用默认值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
biotest@biotest-VirtualBox:~/python3/01basic$ cat function4.py
#!/usr/bin/python3
def printinfo(name,age=35):
print("Name: ",name)
print("Age: ",age)
return
printinfo(age=50, name="runoob")
print("------------------------")
printinfo(name="runoob")
printinfo(20,"runoob")
printinfo("runoob",20)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function4.py
Name: runoob
Age: 50
------------------------
Name: runoob
Age: 35
Name: 20
Age: runoob
Name: runoob
Age: 20

含有默认值的参数必须要放到最后,例如在上面的那个案例中,def printinfo(name,age=35)中age是默认参数,其值是35,它要放到name这个参数后面。

多值参数

有的时候,我们向某个函数传递的参数个数是不确定,这个时候就可以使用多值参数。python中有两种多值参数。

  • 参数前加一个*,就可以接收元组;

  • 参数前加两个*,即**,就有接收字典。

通常在给多值参数命名时,习惯使用以下两个名字,例如:

  • *args—存放元组参数;
  • **kwargs—存放字典。

基本语法如下:

1
2
3
4
def functionname([formal_args,] *var_args_tuple ):
"函数_文档字符串"
function_suite
return [expression]

加了星号(*)的变量名会存放所有未命名的变量参数。如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。如下实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
biotest@biotest-VirtualBox:~/python3/01basic$ cat function5.py
#!/usr/bin/python3
def printinfo(arg1, *vartuple):
print("Output: ")
print(arg1)
for var in vartuple:
print(var)
return
printinfo(10)
printinfo(11,12,13)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 function5.py
Output:
10
Output:
11
12
13

再看一个案例:

1
2
3
4
5
6
7
8
def demo(num, *nums, **person):
print(num)
print(nums)
print(person)
demo(1) # 只传递一个参数
# demo(1,2,3,4,5) # 传递多个参数

运行结果如下所示:

1
2
3
1
()
{}

当只传递1个参数时,这个参数就传递给了num这个形参,运行结果就是1,*nums**person两个分别是元组与字典,并没有给它们传递参数。再看一个案例:

1
2
3
4
5
6
7
8
def demo(num, *nums, **person):
print(num)
print(nums)
print(person)
# demo(1) # 只传递一个参数
demo(1,2,3,4,5) # 传递多个参数

运行结果如下所示:

1
2
3
1
(2, 3, 4, 5)
{}

向函数传递了1,2,3,4,5五个参数,其中1给了num2,3,4,5给了*nums,就是元组,而**person中并没有相应参数。再看一个案例:

1
2
3
4
5
6
7
8
def demo(num, *nums, **person):
print(num)
print(nums)
print(person)
# demo(1) # 只传递一个参数
demo(1,2,3,4,5, name="小明") # 传递多个参数

运行结果如下所示:

1
2
3
1
(2, 3, 4, 5)
{'name': '小明'}

只解释第三行,可以发现name="小明"传递给了**person。再添加一个案例:

1
2
3
4
5
6
7
8
def demo(num, *nums, **person):
print(num)
print(nums)
print(person)
# demo(1) # 只传递一个参数
demo(1,2,3,4,5, name="小明",age=12) # 传递多个参数

运行结果如下所示:

1
2
3
1
(2, 3, 4, 5)
{'name': '小明', 'age': 12}

例如案例:定义一个溇sum_numbers,可以接收任意多个整数,将传递的所有数字累加,并返回累加结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
def sum_numbers(*args):
num = 0
print(args)
for n in args:
num += n
return num
result = sum_numbers(1, 2, 3, 4, 5)
print(result)

运行结果如下所示:

1
2
(1, 2, 3, 4, 5)
152

元组和字典的拆包

在调用带有多值参数的函数时,如果我们想把一个元组变量直接传递给args,将一个字典变量直接传递给kwargs,那就要使用拆包担任,简化参数的传递,拆包的方式就是在元组变量前增加一个*,在字典变量前增加两个*,先看一个简单案例,在这个案例中,函数的实参前面并没有添加*,如下所示:

1
2
3
4
5
6
7
8
9
def demo(*args, **kwargs):
print(args)
print(kwargs)
gl_nums = (1, 2, 3)
gl_xiaoming = {"name":"Xiaoming", "age":18}
demo(gl_nums, gl_xiaoming)

运行结果如下所示:

1
2
((1, 2, 3), {'name': 'Xiaoming', 'age': 18})
{}

从结果中可以发现,gl_nums, gl_xiaoming这两个参数都传递给了形参,都是元组,如果我们想把gl_nums参数传递给元组,即args,把gl_xiaoming传递给字典,即kwargs,那么就需要在gl_nums, gl_xiaoming前面加*号,如下所示:

1
2
3
4
5
6
7
8
9
def demo(*args, **kwargs):
print(args)
print(kwargs)
gl_nums = (1, 2, 3)
gl_xiaoming = {"name":"Xiaoming", "age":18}
demo(*gl_nums, **gl_xiaoming)#添加了星号

运行结果如下所示:

1
2
(1, 2, 3)
{'name': 'Xiaoming', 'age': 18}

匿名函数lambda

python使用lambda来创建匿名函数。所谓匿名,意即不再使用def语句这样标准的形式定义一个函数,它的特点如下:

  1. lambda只是一个表达式,函数体比def简单很多。
  2. lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
  3. lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
  4. 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

语法格式如下所示:

1
lambda [arg1 [,arg2,.....argn]]:expression

看一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
biotest@biotest-VirtualBox:~/python3/01basic$ cat lambda.py
#!/usr/bin/python3
sum = lambda arg1, arg2: arg1 + arg2
# 这里就是定义了一个函数,这个函数实现的功能就是两个数字相加
print("The value summed is : ",sum(10, 20))
print("The value summed is : ",sum(20,20))
biotest@biotest-VirtualBox:~/python3/01basic$ python3 lambda.py
The value summed is : 30
The value summed is : 40

上面的这个案例如果改成常规的函数写法,则是如下所示:

1
2
3
4
5
6
7
8
9
10
biotest@biotest-VirtualBox:~/python3/01basic$ cat lambda.py
#!/usr/bin/python3
sum = lambda arg1, arg2: arg1 + arg2
print("The value summed is : ",sum(10, 20))
print("The value summed is : ",sum(20,20))
biotest@biotest-VirtualBox:~/python3/01basic$ python3 lambda.py
The value summed is : 30
The value summed is : 40

匿名函数与普通函数的区别

看下面的一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
biotest@biotest-VirtualBox:~/python3/03file$ cat lambda.py
#!/usr/bin/python3
def add(a,b):
return a+b
a=2
b=3
print('a+b=', add(a,b))
a=2
b=3
addl = lambda a,b:a+b
print('a+b=',addl(a,b))
print('type(add(a,b)):',type(add(a,b)))
print('type(addl):',type(addl))
biotest@biotest-VirtualBox:~/python3/03file$ python3 lambda.py
a+b= 5
a+b= 5
type(add(a,b)): <class 'int'>
type(addl): <class 'function'>

从上面我们可以看见add(a,b)的类型是int,而addl(a,b)的类型则是函数。从这里也能看出Lambda表达式确实是一种函数的表示方式。lambda的一般形式是关键字lambda后面跟一个或多个参数,紧跟一个冒号,以后是一个表达式。lambda是一个表达式而不是一个语句。它能够出现在python语法不允许def出现的地方。

lambda案例第1:快速组建列表

1
2
3
4
>>> list1 = lambda x:x**2
>>> l = [list1(i) for i in range(10)]
>>> print(l)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

lambda函数:减少函数命名

1
2
3
4
5
6
7
8
9
10
11
12
13
biotest@biotest-VirtualBox:~/python3/03file$ cat lambda1.py
#!/usr/bin/python3
def eat(f):
def many(n):
return n*f
return many
food = input('What kind of fruti do you like?')
f = eat(food)
print(f(5))
biotest@biotest-VirtualBox:~/python3/03file$ python3 lambda1.py
What kind of fruti do you like? Yes
Yes Yes Yes Yes Yes

上述代码改写为lambda则是如下所示:

1
2
3
4
5
6
def eat(f):
return lambda n:f*n
food = input('What kind of fruit do you like?')
f = eat(food)
print(f(5))

再看一个案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
biotest@biotest-VirtualBox:~/python3/03file$ cat original.py # 这是原代码
#!/usr/bin/python3
freshfruit = [' banana',' loganberry ','passion fruit'] # 建立一个列表
print(freshfruit) #显示列表,发现前2个元素有空格
list3 =[w.strip() for w in freshfruit] # 去除空格
print(list3) #空格去掉后的效果
biotest@biotest-VirtualBox:~/python3/03file$ python3 original.py
[' banana', ' loganberry ', 'passion fruit']
['banana', 'loganberry', 'passion fruit']
biotest@biotest-VirtualBox:~/python3/03file$ cat original_trans.py # 改写为lambda形式
#!/usr/bin/python3
freshfruit = [' banana',' loganberry ','passion fruit']
list3 = list(map(lambda x:x.strip(),freshfruit))
# lambda x:x.strip()这里就相当于一个函数,map则是将这个函数映射到freshfruit的每个元素上
print(list3)
biotest@biotest-VirtualBox:~/python3/03file$ python3 original_trans.py
['banana', 'loganberry', 'passion fruit']

案例:使用列表推导式实现嵌套列表的平铺,如下所示:

1
2
3
>>> vec = [[1,2,3],[4,5,6],[7,8,9,10]]
>>> print([num for elem in vec for num in elem])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

这个列表推导式中有2个循环,第1个循环可以看作是外循环,执行得慢,第2个循环是内循环,执行得快,上面代码等价于下面的:

1
2
3
4
5
6
7
8
9
10
11
biotest@biotest-VirtualBox:~/python3/03file$ cat test1.py
#!/usr/bin/python3
vec = [[1,2,3],[4,5,6],[7,8,9,10]]
result = []
for elem in vec:
for num in elem:
result.append(num)
print(result)
biotest@biotest-VirtualBox:~/python3/03file$ python3 test1.py
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

案例:过滤掉不合条件的元素

列表推导式中可以使用if语句来进行筛选,只在结果列表中保留符合条件的元素,下面的代码是过滤当前目录下的所有文件,只保留以.py结尾的文件,如下所示:

1
2
3
>>> import os
>>> print([filename for filename in os.listdir('.') if filename.endswith('.py')])
['a.py', 'count_line.py', 'practice.py', 'python.py', 't.py']

代码解释:

  1. os.listdir()方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序,括号中的参数可以是路径,例如c:\downloads,也可以不加,用’.’来表示。
  2. endswith()方法用于判断字符串是否以指定后缀结尾,如果以指定后缀结尾返回True,否则返回False。可选参数”start”与”end”为检索字符串的开始与结束位置。

案例:过滤满足一定条件的元素

1
2
3
4
5
6
>>> list4= [-1, -4, 6, 7.5 ,-2.3, 9, -11]
>>> print(list4)
[-1, -4, 6, 7.5, -2.3, 9, -11]
>>> result = [i for i in list4 if i < 0]
>>> print(result)
[-1, -4, -2.3, -11]

带有返回值的函数

return[表达式]语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return语句返回None,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
biotest@biotest-VirtualBox:~/python3/01basic$ cat return.py
#!/usr/bin/python3
def sum(arg1, arg2):
total = arg1 + arg2
print("Inside function is : ",total)
return total
total = sum(10, 20)
print("Outside function is : ",total)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 return.py
Inside function is : 30
Outside function is : 30

变量作用域

Python中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:

  1. L(Local)局部作用域

  2. E(Enclosing)闭包函数外的函数中

  3. G(Global)全局作用域

  4. B(Built-in)内建作用域

Pyhton以L–>E–>G–>B的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找,如下所示:

1
2
3
4
5
6
7
x = int(2.9) # 内建作用域
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 闭包函数外的函数中
def inner():
i_count = 2 # 局部作用域

Python中只有模块(module)类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if/elif/else/try/exceptfor/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问,如下代码:

1
2
3
4
5
>>> if True:
... msg = "I am a learner"
...
>>> msg
'I am a learner'

在这个案例中,msg变量定义在if语句块中,但外部还是可以访问的。如果将msg定义在函数中,则它就是局部变量,外部不能访问,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
biotest@biotest-VirtualBox:~/python3/01basic$ cat local_variable.py
#!/usr/bin/python3
def test():
msg_inner = "I am a learner"
print(msg_inner)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 local_variable.py
Traceback (most recent call last):
File "local_variable.py", line 6, in <module>
print(msg_inner)
NameError: name 'msg_inner' is not defined

从错误提示就能看出,msg_inner并示未定义,因为它是局部变量,只能在函数内部使用。

全局变量和局部变量

定义在函数内部的变量拥有一个局部作用域(局部变量),定义在函数外的拥有全局作用域(全局变量)。局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
biotest@biotest-VirtualBox:~/python3/01basic$ cat global_local_variable.py
#!/usr/bin/python3
total = 0
# this is a global variable
def sum(arg1, arg2):
total = arg1 + arg2
# Here, total is a local variable
print("Variable inside function is local variable: ",total)
return total
sum(10, 20)
print("Variable outside function is global variable: ",total)
biotest@biotest-VirtualBox:~/python3/01basic$ python3 global_local_variable.py
Variable inside function is local variable: 30
Variable outside function is global variable: 0

global和nonlocal关键字

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字,在下面的案例中,修改了全局变量num:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
biotest@biotest-VirtualBox:~/python3/01basic$ cat nonlocal.py
#!/usr/bin/python3
num = 1
def fun1():
global num # need to keyword "global" for statement
print(num)
num = 123
print(num)
fun1()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 nonlocal.py
1
123

如果要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量则需要nonlocal关键字了,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
biotest@biotest-VirtualBox:~/python3/01basic$ cat nonlocal2.py
#!/usr/bin/python3
def outer():
num = 10
def inner():
nonlocal num
num = 100
print(num)
inner()
print(num)
outer()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 nonlocal2.py
100
100

再看一个案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
biotest@biotest-VirtualBox:~/python3/01basic$ cat action_scope_error.py
#!/usr/bin/python3
a = 10
def test():
a = a + 1
print(a)
test()
biotest@biotest-VirtualBox:~/python3/01basic$ python3 action_scope_error.py
Traceback (most recent call last):
File "action_scope_error.py", line 7, in <module>
test()
File "action_scope_error.py", line 5, in test
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

错误信息为局部作用域引用错误,因为test函数中的a使用的是局部变量,未定义,无法修改。

递归

递归意思就是函数自己调用自己,如下所示:

1
2
3
4
5
6
7
8
9
10
def sum_number(num):
print(num)
if num == 1:
return # 递归的出品,当参数满足某个条件时,不再执行函数
sum_number(num - 1) # 函数自己调用自己
sum_number(3)

运行结果如下所示:

1
2
3
3
2
1