Pythin语言的函数(中)
前情回顾:
上一篇文章,看似讲了不少,不过呢 讲来讲去围绕的都还是 括号里的一些内容( def xxx(): ),今天这篇文章呢,我们来讲一些哪些括号之外的 神奇世界。
在开始今天的内容之前,我们先回顾一下我们上一篇主要讲的核心案例:
# 求任意数的和
def fn(*nums):
result = 0
for x in nums:
result += x
print(result)
fn(2,9,7,3) # 21
向函数方法 fn() 内传递任意个数的参数,通过 for 循环将所有传递进来的参数 nums 通过 变量x 都 遍历 出来,并通过在 for 循环外预先设定好的 变量result 将所有遍历到的数据 相加,最后在 for 循环外将任意数的和 print(result) 出来。如果对这个开头有什么疑问的可以重新去看看我之前的文章。
那么,接下来我们要想哈:虽然我让这个函数为我们进行计算任意数的和,但是计算后的结果我们一定需要函数输出吗? 或者问:我们能不能把这个计算结果拿去 做别的我们需要的事情?
就上面的案例我们发现:我们把参数传递进去,计算完之后就通过 print( ) 语句把结果输出了。就上面的案例,输出的结果我们是否还无法拿去做别的事情;最起码的上面的案例是做不到的。为什么?因为这个 变量result 我们都没拿到,它至始至终都在函数的内部;无法做到 我想让它计算它就计算,我想让它打印,它就打印,无法做到明确控制。
讲到这,下面就开始我们今天的要讲几个主要内容。
1. 函数的返回值
返回值就是函数执行以后返回的结果;
通过 return 来指定函数的返回值;
我们可以通过一个变量来接收函数的返回值,或者可以直接函数来使用函数的返回值;
参考实例:
1) 单纯的打印一个有返回值的函数是不会有任何的结果的;
def fn():
return 100
fn() #
2) 我们可以通过一个变量来接收函数,直接打印变量里的函数返回值;
def fn():
return 100 # int
r = fn()
print(r) # 100
print(fn()) # 100
#-------------------------------------------------------------------------------
def fn():
return 'Python' # str
r = fn()
print(r) # Python
print(fn()) # Python
#-------------------------------------------------------------------------------
def fn():
return [1, 2, 3] # 列表
r = fn()
print(r) # [1, 2, 3]
print(fn()) # [1, 2, 3]
#-------------------------------------------------------------------------------
def fn():
return {'name':'张三'} # 字典
r = fn()
print(r) # {'name':'张三'}
print(fn()) # {'name':'张三'}
#-------------------------------------------------------------------------------
def fn():
def fn2():
print('Hello')
return fn2
r = fn()
print(r) # <function fn.<locals>.fn2 at 0x000001648ED80940>
print(fn()) # <function fn.<locals>.fn2 at 0x0000018DEE630A60>
r() # Hello
通过以上实例,我们还得出一个结论:return 后面可以跟 任意对象 ,返回值 甚至 可以是一个 函数。
重点:如果函数里仅仅只写一个 return 或者不写 return ,则相当于函数 return Noun。
def fn():
return
r = fn()
print(r) # None
#-------------------------------------------------------------------------------
def fn():
print('Hello')
print('123')
r = fn()
print(r) # Hello
# 123
# None
在函数中,执行到 return 以后就要 返回结果 了,换个解释方式:return 以后的所有代码块将 不会执行;return 一旦执行,函数将 自动结束。(到这里,大家有没有发现:这个 return 和我们之前的 5. Python 条件控制语句细解 里讲到的循环函数 break 很相似啊)
参考实例:
def fn():
print('Hello')
return
print('123')
r = fn() # Hello
#-------------------------------------------------------------------------------
def fn():
for i in range(5):
if i == 3:
break
print(i)
print('循环执行完毕')
fn()
# 0
# 1
# 2
# 循环执行完毕
#-------------------------------------------------------------------------------
def fn():
for i in range(5):
if i == 3:
return
print(i)
print('循环执行完毕')
fn()
# 0
# 1
# 2
回到我们最初开始的案例,现在我们对它进行一点点简单的补充和修改:
# 求任意数的和
def fn(*nums):
result = 0
for x in nums:
result += x
return result
r = fn(2,9,7,3)
print(r) # 21
print(r+17) # 38
通过刚讲解完的函数的返回值 return 顺带着我们再重新的回顾一下我们上一篇文章里提到的另一个问题:一个函数在调用的时候,它的后面 跟 () 是什么意思,不跟 () 又是什么意思。
看参考实例:
def fn():
return 100
print(fn) # <function fn at 0x00000162FE0FE160>
print(fn()) # 100
通过上面实例,我们再回顾一下结果:一个函数在调用的时候,它的后面跟 () 是 调用函数名为 fn 的函数 的意思,不跟 () 是 对象 fn 的意思;解释一下 参考案例 里print(fn)
打印出来的内容<function fn at 0x00000162FE0FE160>
的意思:该对象是一个函数(function) 函数的名字叫fn 它的存在地址于(at)你电脑内存的 0x00000162FE0FE160。
2. 文档字符串 help()
help() 是Python中内置函数,我们可以通过help()函数可以查询Python中 函数的用法;
参考实例:
help(print)
'''
输出结果:
Help on built-in function print in module builtins:
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
'''
即然我们打印了 函数print 的功能,这里呢加一个能让你小小的秀一下操作的小技巧:
# 首先先常规的打印一段字符串:
print('www','baidu','com') # www baidu com
#-------------------------------------------------------------------------------
# 修改以下 函数 print() 里的属性 sep=' ' 为 sep='.'
print('www','baidu','com',sep='.') # www.baidu.com
在定义函数时,可以在函数内部编写文档字符串,文档字符串就是对函数的说明。
参考实例:
def fn(a:bool,b:int,c:str)->int:
'''
这个函数是一个文档字符串实例
参数
:param a: 作用 类型 默认值 ...
:param b: 作用 类型 默认值 ...
:param c: 作用 类型 默认值 ...
:return:
'''
return 100
help(fn)
'''
输出结果:
Help on function fn in module __main__:
fn(a: bool, b: int, c: str) -> int
这个函数是一个文档字符串实例
参数
:param a: 作用 类型 默认值 ...
:param b: 作用 类型 默认值 ...
:param c: 作用 类型 默认值 ...
:return:
'''
# 补充:解释一下这段话的意思 fn(a: bool, b: int, c: str) -> int
# 函数fn 的输入形参a要为布尔值,输入形参b要为整型,输入形参c要为字符串;函数的返回值是一个 整形
3. 函数的作用域
3.1 作用域(scope)
作用域指的是 变量生效的区域 ;
def fn():
a = 10 # 变量a 定义在了函数内部,所以它的作用域就是函数内部;函数外部是无法访问到函数内部的变量a 的。
print('函数内部变量 a =',a) # 函数内部变量 a = 10
return a
fn()
print('函数外部变量 a =',a) # NameError: name 'a' is not defined
#-------------------------------------------------------------------------------
b = 50
def fn():
a = 10
print('函数内部变量 a =',a) # 函数内部变量 a = 10
print('函数内部变量 b =', b) # 函数内部变量 b = 50
fn()
# print('函数外部变量 a =',a) # NameError: name 'a' is not defined
print('函数外部变量 b =',b) # 函数外部变量 b = 50
通过上面的参考实例我们发现 函数在不同的位置它最终访问到的结果是不一样的。在Python中一共有两种作用域:全局作用域 和 函数作用域。
3.2 全局作用域
全局作用域在程序执行时创建,在程序执行结束时销毁;
所有函数以外的区域都是全局作用域;
在全局作用域中定义的变量,都是全局变量,全局变量可以在程序的任意位置进行访问。
3.3 函数作用域
函数作用域在函数调用时创建,在调用结束时销毁;
函数每调用一次就会产生一个新的函数作用域;
在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问。
3.4 关键字 global
如果希望 在函数内部 修改 全局变量,则使用 global 关键字来声明变量。
global 关键字的作用是:声明在函数的内部使用的 变量a 是 全局变量,则此时再去修改 变量a 时,就是在修改全局变量。
参考实例:
def fn():
a = 10
def fn2():
print('函数 fn2 内部变量 a =',a)
fn2()
fn() # 函数 f2 内部变量 a = 10
#-------------------------------------------------------------------------------
def fn():
a = 10
def fn2():
a = 50
print('函数 fn2 内部变量 a =',a)
fn2()
fn() # 函数 f2 内部变量 a = 50
#-------------------------------------------------------------------------------
a = 1 # 全局变量
def fn():
# global a # 如果希望在函数内部修改全局变量,则使用 global 关键字来声明变量。
a = 10 # 声明在函数的内部使用的变量a是全局变量,则此时再去修改变量a时,就是在修改全局变量。
def fn2():
# global a # 如果希望在函数内部修改全局变量,则使用 global 关键字来声明变量。
a = 20 # 声明在函数的内部使用的变量a是全局变量,则此时再去修改变量a时,就是在修改全局变量。
print('函数 fn2 内部变量 a =', a)
def fn3():
# global a # 如果希望在函数内部修改全局变量,则使用 global 关键字来声明变量。
a = 30 # 声明在函数的内部使用的变量a是全局变量,则此时再去修改变量a时,就是在修改全局变量。
print('函数 fn3 内部变量 a =', a)
fn3()
fn2()
print('函数 fn 内部变量 a =',a)
fn()
print('函数 外部变量 a =',a)
# 函数 fn2 内部变量 a = 20
# 函数 fn3 内部变量 a = 30
# 函数 fn 内部变量 a = 10
# 函数 外部变量 a = 30
4. 命名空间
Python一切皆对象,操作的数据不论是操作数字、字符串、函数、类、模块 这些都是对象。在这一切对象当中有个非常重要的角色,它就是:命名空间。
命名空间,名字很高大上,实际上就是 字典 的一种操作方法;因为它的名字很高大上,所以除了偶尔装X的时候可以拿出来秀秀技术以外,就是在 面试 的时候 常常都会被问到。
命名空间实际上就是一个 字典,是一个专门用来 存储变量 的字典;
函数 locals() 用来 获取当前作用域的命名空间,函数locals() 的 返回值 是一个 字典;
我们先打印操作以下,看下函数 locals() 的返回值。参考实例:
a = 2
def fn():
global a
a = 50
print('函数内部:a =',a)
s = locals()
print(s)
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000026B63FC0910>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'G:/lianxi/测试2.py', '__cached__': None, 'a': 2, 'fn': <function fn at 0x0000026B65C8E160>, 's': {...}}
以上面的参考实例为基础,变量 a = 2 它被加载到内存当中,它是需要被调用或是需要被引用的。Python语言此时的操作就类似于 C语言 里的 指针 去指向了这个对象,这个“指针”就是我们所说的 命名空间,或者叫 名称空间;严格意义来说,Python里也有这么类似的一个“指针”,或者说是这么的一种对 对象的引用。 虽然它很重要,但是大家也别想的那么的复杂。我们通过这个 指针 就可以引用这个 指针 所指向的对象内存地址值中的代码块。
不论是操作数字、字符串、函数、类、模块 这些都是对象。 对象, 对象,说白了就是 我们去内存中引用来的一块区域。任你千变万化,你都只是 在内存空间里 的,都只是预先设定好的一个代码块。只是此时,我们的代码在执行的时候是 以命名空间为单位进行执行 的代码。
做了那么多的解释,接下来我们尝试着来看看 命名空间 怎么使用:
基于开始 命名空间 最初的案例我们知道以下这段代码最后的两个输出结果都是 2 :
a = 2
def fn():
global a
a = 50
print('函数内部:a =',a)
s = locals()
print(a) # 2
print(s['a']) # 2
基于上面的实例,如果我想打印一个 变量b ,最后会是什么结果?我们来尝试一下:
a = 2
def fn():
global a
a = 50
print('函数内部:a =',a)
s = locals()
print(b) # NameError: name 'b' is not defined
是不是出现 NameError 这个提示了,是不是没有这个变量啊。只能在打印 变量b 前设定好相对应的 变量和值 才可以正常的打印啊。如:
a = 2
def fn():
global a
a = 50
print('函数内部:a =',a)
s = locals()
b = 100
print(b) # 100
接下来,我们用 命名空间 的方法来演示操作一下:如何在 不以常规的命名变量 的前提下能让我们正常的打印出 变量b。
参考实例:
a = 2
def fn():
global a
a = 50
print('函数内部:a =',a)
s = locals()
s['b'] = 100
print(b) # 100
看我文章的朋友,如果你们在使用我在文章 2. Python要点 里推荐的 Python编辑工具 PyCharm 操作上面这段的代码的话,你们会发现:最后 print(b) 的 b 编辑器给的提示还是报错的呢,为什么最后却还是能打印出一个结果呢?
回到前面我们说过: 命名空间 实际上就是一个 字典,是一个专门用来 存储变量 的字典;函数 locals() 用来 获取当前作用域的命名空间,函数locals() 的 返回值 是一个 字典。代码 s['b'] = 100
实际的意思是:向 命名空间 s = locals()
里传递进一个 键·值对,这个 键·值对 的键是b,值是 100;整个 print(b)
的操作完整的解析是:打印 字典s 里的键为 b 的值。
现在,我们再重新的打印一下 s 来看看它最新的结果:
a = 2
def fn():
global a
a = 50
print('函数内部:a =',a)
s = locals()
s['b'] = 100
print(s)
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CB407D0910>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'G:/lianxi/测试2.py', '__cached__': None, 'a': 2, 'fn': <function fn at 0x000001CB4097D160>, 's': {...}, 'b': 100}
此时,我们再打印 s 的时候,字典s 的最后是不是多了一个'b': 100
。
解释完了 函数 locals() 以及如何操作 locals()的打印值后,我们再回顾上一段内容遗留的一个没有解释的问题:最后 print(b) 的 b 在 PyCharm 编辑器给的提示还是报错,为什么最后却还是能打印出一个结果呢?原因呢其实也很简单,Python解释器其实不认可这种操作的。你这么操作没什么错,但是这种操作你知道就可以了,一般不建议这么的操作;这是一种非常规的操作。画蛇添足,反而麻烦了。
另外呢,还有一点:如果在全局作用域中调用 函数locals() 则获取全局命名空间,如果在函数作用域中调用 函数locals() 则获取函数命名空间。
参考实例:
def fn2():
s = locals() # 获取函数内部的命名空间
print(s)
fn2() # {}
#-----------------------------------------------------------------------------------
def fn2():
a = 10
s = locals()
s['b'] = 50
print(s)
fn2() # {'a': 10, 'b': 50}
#-----------------------------------------------------------------------------------
# 如何实现在函数的内部获取 全局的命名空间 ?
a = 2
def fn():
global a
a = 50
print('函数内部:a =',a)
s = locals()
print(a) # 2
s['b'] = 100
print(b) # 100
print(s) # {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000027C26000910>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'G:/lianxi/测试2.py', '__cached__': None, 'a': 2, 'fn': <function fn at 0x0000027C27C9D160>, 's': {...}, 'b': 100}
def fn2():
global_s = globals()
global_s['a'] = 390
print(global_s)
fn2() # {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000027C26000910>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'G:/lianxi/测试2.py', '__cached__': None, 'a': 390, 'fn': <function fn at 0x0000027C27C9D160>, 's': {...}, 'b': 100, 'fn2': <function fn2 at 0x0000027C27CB0940>}
print(a) # 390
5. 递归函数
指数级增长 我相信大家应该都听过,所谓的“指数”就是 xxx 的 nn 次方;但是,相比于 指数级增长 还有一中更可怕的增长方式不知道大家听过没有,那就是“阶乘式递增”。在介绍什么是“阶乘式递增”之前,让我们先认识一下计算机实现“阶乘式递增”的语法:“递归”。
接下来我们来通过一系列的案例来理解什么是 递归。
案例:尝试求 10 的阶乘 (10!)
在尝试实现“10 的阶乘”之前我们先分析一下:什么是“10 的阶乘”?
接下来我们来看什么是 阶乘:
1 的阶乘:1! = 1
2 的阶乘:2! = 1 * 2
3 的阶乘:3! = 1 * 2 * 3
10 的阶乘:10! = 1 * 2 * 3…* 8 * 9 * 10 = 3628800
即然现在我们知道什么是 阶乘 了,那么怎么实现“10 的阶乘”也就简单了:
参考实例:
print(1*2*3*4*5*6*7*8*9*10) # 3628800
手动实现“10 的阶乘”当然 So easy 了,那如果需要实现 “1000 的阶乘” ,我们该怎么操作呢?此时此刻我们是不是需要循环了。(小提示:while 、 for 、 *=)
参考实例:
b = 1000
for x in range(1,b): # 尝试一下:和 for x in range(a) 有啥区别 :)
b *= x
print(b) # 结果还是由你亲自实现吧,别被震撼到哟。反正,我第一眼看到的时候直接哭了~
接下来 “任意数 的阶乘”我想你不需要怎么思考心里马上就有答案了吧。
参考实例:定义一个函数,函数功能 “实现任意数 的阶乘”
def fn(n):
# 参数n:要求阶乘的数字
# n = a
# 定义一个变量,保存结果
result = n
for i in range(1,n): # 尝试一下:和 for i in range(n): 有啥区别 :)
result *= i
return result
fn(20) #
# print(fn()) # TypeError: fn() missing 1 required positional argument: 'n'
# print(fn) # <function fn at 0x0000012591FEE160>
print(fn(20)) # 2432902008176640000
# 为什么我要打印这么多的结果,因为我之前的语法不严谨 导致了这次我在这一段里的丑态百出。写在此处,引以为戒。有些语法为什么这么写,都是有它合乎逻辑的道理的。
到此呢,我们也知道什么是 阶乘 了,也知道怎么通过代码来实现 阶乘 了,可是现在废话了半天,这些和现在讲的 递归 又有什么关系呢?
我们现在一直在说的 递归,完整的叫法应该叫做 递归式函数。那 递归式函数 又是什么呢? 递归 简单的理解就是:自己 引用 自己; 递归式函数:在函数中,自己 调用 自己。用一个通俗而且我们小时候都讲过的一个小故事来直接打通这个问题:从前有座山,山里有座庙,庙里有个老和尚在给小和尚讲故事,讲什么故事呢?从前有座山,山里有座庙,庙里有个老和尚……
参考实例:无穷递归
def fn():
fn()
fn()
这种“无穷递归”,只要你的电脑性能稍微差点,就会导致死机;类似于“死循环”。
不过呢,我们也没必要妖魔化 递归 ,它只是一个 解决问题的方式,和 循环 很像;它的 整体思想 是:将一个大问题 分解 为一个个的 小问题,直到问题 无法分解 时,再去解决问题。
递归式函数有 2个条件:
1. 基线条件:问题可以被分解为 最小问题,当满足基线条件时,递归就 不执行 了;
2. 递归条件:可以将问题 继续分解 的条件。
那我们现在再回到最初的问题:用 递归 的方式求“任意数的阶乘”。
我们现在先拆解一下用 递归 的方式求“任意数的阶乘”的 2个条件:
2. 递归条件:10! = 10 * 9!
9! = 9 * 8!
8! = 8 * 7!
………
1. 基线条件:1! = 1
参考实例:
def fn(n):
# 参数 n 要求阶乘的数字
# 1. 基线条件
if n == 1:
return 1
# 2. 递归条件
return n * fn(n-1) # return 10 * 9!
print(fn(10)) # 3628800
总结小便条
本篇文章主要讲了以下几点内容:
- 函数的返回值:
• 返回值就是函数执行以后返回的结果;
• 通过 return 来指定函数的返回值;
• return 后面可以跟任意对象,返回值甚至可以是一个函数。 - 文档字符串:
• help() 是 Python 中内置函数,通过 help() 函数可以查询Python中函数的用法;
• 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是对函数的说明。 - 函数的作用域:
• 作用域(scope);
作用域指的是变量生效的区域;
在Python中一共有两种作用域。
• 全局作用域:
全局作用域在程序执行时创建,在程序执行结束时销毁;
所有函数以外的区域都是全局作用域;
在全局作用域中定义的变量,都是全局变量,全局变量可以在程序的任意位置进行访问。
• 函数作用域:
函数作用域在函数调用时创建,在调用结束时销毁;
函数每调用一次就会产生一个新的函数作用域;
在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问。 - 命名空间:
• 命名空间实际上就是一个字典,是一个专门用来存储变量的字典;
• locals()用来获取当前作用域的命名空间;
• 如果在全局作用域中调用locals()则获取全局命名空间,如果在函数作用域中调用locals()则获取函数命名空间;
• 返回值是一个字典。 - 递归函数:
• 递归是解决问题的一种方式,它的整体思想,是将一个大问题分解为一个个的小问题,直到问题无法分解时,在去解决问题;
• 递归式函数有2个条件:
1. 基线条件 问题可以被分解为最小问题,当满足基线条件时,递归就不执行了;
2. 递归条件 可以将问题继续分解的条件。
本章回顾暂时就到这了,如果还有点晕,那就去完成作业吧。拜拜~
作业
1. 创建一个函数,为任意数做任意次幂运算
2. 定义一个函数,用来检测一个任意字符是否是回文。如果是,返回True;如果不是,返回Falsse。
3. 汉诺塔游戏,现在有ABC三根柱子。要求:将A柱所有的圆盘放到C柱。在移动的过程中可以借助B柱。并且规定大圆盘不能放小圆盘上面,每次只能移动一个盘子。用递归的方式来解决汉诺塔问题。
参考答案
def fn(n,i): # 参数n要做幂运算的数字;参数i要做幂运算的次数
if i == 1: # 1. 基数条件
return n
# return n * n ** (i-1) # 2. 递归条件
return n * fn(n,i-1)
print(fn(5,4))
print(5**4)
#-----------------------------------------------------------------------------------
# 先检查首字符和尾字符是否一致,如果不一致 Falsse ,一定不是回文;
# 如果是,后续则一次检查
# abcdefedcba 检查是否是 回文
# bcdefedcb
# cdefedc
# defed
# efe
# f
# 方法一:
def fn(s): # 参数 s 就是要检查的字符串。
if len(s) < 2: # 1. 基数条件 # 字符串长度小于2,则该字符串一定为 回文。
return True
elif s[0] != s[-1]: # 首字符 和 尾字符不相同,则一定不是回文
return False
return fn(s[1:-1]) # 2. 递归条件 # 切片
print(fn('aba'))
# 方法二:
def fn(s): # 参数 s 就是要检查的字符串。
if len(s)<2: # 1. 基数条件 # 字符串长度小于2,则该字符串一定为 回文。
return True
return s[0] == s[-1] and fn(s[1:-1]) # 2. 递归条件 # 首字符 和 尾字符不相同,则一定不是回文 # 切片
print(fn('Hallo'))
#-----------------------------------------------------------------------------------
# 1. 如果只有一个盘子: A --> C
# 2. 如果盘子数 >= 2,我们统一看成2个盘子,分别是“底盘”和“盘堆”。
# 2.1 把最上面的盘子 A --> B
# 2.2 把最下面的盘子 A --> C
# 2.3 把B柱的盘子放到C B --> C
num = int(input('请输入罗马盘的数量:')) # 定义一个函数,解决 汉诺塔 问题
def hannuoTower(num,a,b,c): # 参数:num:盘子数;a:a柱;b:b柱;c:c柱。
if num == 1: # 基数条件
print('第 1 个盘从',a,'->',c)
else: # 递归条件 # num >= 2
hannuoTower(num-1,a,c,b) # 2.1 把最上面的盘子 A --> B
print('第',num,'个盘从',a,'->',c) # 2.2 把最下面的盘子 A --> C
hannuoTower(num-1,b,a,c) # 2.3 把B柱的盘子放到C B --> C
hannuoTower(num,'A','B','C')