参考
-
《简明python教程》
Note:
- 更多连载请查看【python】
文章目录
(DRY) don’t repeat yourself
函数就是那些做成重复使用的程序代码
1 函数的定义
函数是由函数名、参数和函数体组成,格式如下:
def 函数名(形式参数):
函数体
[ return 返回值 ]
eg1:
def subtraction(a,b):
c = a - b
return c
python的函数可以返回多个值,默认是元组的形式,也可以以列表的形式返回
return a,b,c #元组
return [a,b,c] #列表
2 函数的调用
subtraction(3,4)
结果为 -1
3 参数的传递
3.1 参数按照位置顺序传递
subtraction(3,4)
结果为-1
3.2 参数按赋值传递
subtraction(b = 3,a = 4) #参数按赋值传递
结果为1
3.3 参数按照默认值传递
eg2:
def subtraction(a,b=2): #参数按照默认值传递,b使用了默认值
c = a - b
return c
subtraction(4)
结果为2
subtraction(4,1)
结果为3
3.4 元组类型的变长度参数传递(可变参数)
参数的拆包
eg3:
def dayin(a,b):
print(a,b)
dayin(1,2)
结果为
1 2
那么如果要打印1000个数呢?设置1000个形参?那岂不是太麻烦了
定义一个变长参数即可,具体操作如下:
我们可以在参数前面加一个星号 * ,表示这个参数是可变长参数当其他参数赋值完毕时候,剩下的参数依次赋值给这个可变长参数
eg4:
def dayin(a,*b):
print(a,b)
dayin(1,2,3)
结果为
1 (2, 3)
好像不完美,变长部分以元组的形式呈现,我们要的是1 2 3 的效果,改改
eg5:
def dayin(a,*b):
print (a,'',end = '') # ''空格,end = ''表示不换行,
for i in b:
print (i,'',end = '')
dayin(1,2,3)
结果为
1 2 3
试试效果
dayin(1,2,3,4,5,6,7,8)
结果为
1 2 3 4 5 6 7 8
*形参叫做收集参数,eg: print 函数
3.5 字典类型的变长度参数传递(关键字参数)
我们可以在参数前面加两个星号 ** ,表示这个参数是可变长参数是以 实参名=字典值的方式传递
eg6:
def dayin(a,*b,**c):
print(a,b,c)
dayin(1,2,3,d = 4)
结果为
1 (2, 3) {'d': 4}
注意到,字典类型变长参数传递一定是赋值形式传递进去的
dayin(1,2,3,4)
结果为
1 (2, 3, 4) {}
dayin(1)
结果为
1 () {}
我们和3.4节一样,改改函数,取出元组和字典中的数字
eg7:
def dayin(a,*b,**c):
print(a,'',end = '')
if len(b) != 0:
for i in b:
print(i ,'',end = '')
if len(c)!= 0:
for j in c:
print(c[j],'',end = '')
dayin(1,2,3,4,5,6,7,8)
结果为
1 2 3 4 5 6 7 8
dayin(1)
结果为
1
dayin(1,2,3,4,5,haha = 6)
结果为
1 2 3 4 5 6
dayin(1,2,3,4,5,haha = 6,hehe = 7)
结果为
1 2 3 4 5 6 7
dayin(1,2,3,4,5,haha = 6,7,8,9,hehe = 10)
#注意,可变参数的位置一般写在最后,不能乱了顺序,否则会报错!!!
结果报错
SyntaxError: positional argument follows keyword argument
3.6 *args 和 **kwargs
Python中的*args和**kwargs是什么?该如何使用?
args 是 arguments 的缩写,表示位置参数;
kwargs 是 keyword arguments 的缩写,表示关键字参数。
# *arg 的魔法
def func(*arg):
num = 0
for x in arg:
num = num + x
print(num) # 600
func(100, 200, 300)
def printPetNames(*args, **kwargs):
for i in args:
print(f"Owner Name:{i}")
for pet, name in kwargs.items():
print(f"{pet}:{name}")
printPetNames(
"Jonathan", "Bob", "Alex",
dog="Brock",
fish=["Larry", "Curly", "Moe"],
turtle="Shelldon")
4 无返回值的函数
eg8:
def add():
a = 1
b = 2
print(a+b)
add()
结果为
3
有些编程语言中,如果没有返回值(return),叫做过程
python严格来说是没有过程的,因为即使函数中没有return语句,python会默认返回None
5 函数的作用域
变量的作用域就是在程序中能对这个变量操作的区域范围,Python允许同名变量的出现
eg9:
a = 0
def outer():
a = 1
print('outer = %d'%a)
def inner():
a = 2
print('inner = %d'%a)
outer()
结果为
outer = 1
Note:以上,我们调用outer( ),inner( ) 函数只是定义了,并没有调用,所以不会运行这个inner( )的函数体
eg10:
a = 0
def outer():
a = 1
print('outer = %d'%a)
def inner():
a = 2
print('inner = %d'%a)
inner() # 在outer()中新增了对inner()的调用
outer()
结果为
outer = 1
inner = 2
此时
print(a)
结果为
0
Noete:以上,注意到,a变量分别有三次赋值,这里的赋值不是覆盖了,因为他们三个a是在不同的位置
-
值为 0 的 a 是在函数外面,我们称之为全局变量(即它的作用域是全局的)
-
值为 1 的 a 的作用范围是outer( )函数中
-
值为 2 的 a 的作用范围是inner( )函数中
当我们要使用变量时候,先在最近的区域搜索
下面在eg10的基础上,注释一些a的赋值,看看结果,以便更好的掌握函数的作用域
5.1 注释a等于1
eg11:
a = 0
def outer():
#a = 1
print('outer = %d'%a)
def inner():
a = 2
print('inner = %d'%a)
inner()
print ('whole = %d'%a)
outer()
结果为
whole = 0
outer = 0
inner = 2
当我们把 a = 1 注释掉,则外层函数打印 a 的时候,取的全局变量,因为内层函数的作用域只在内层函数中,不能延伸到外层函数。
5.2 注释a等于2
eg12:(★★★★★)
a = 0
def outer():
a = 1
print('outer = %d'%a)
def inner():
#a = 2
print('inner = %d'%a)
inner()
print ('whole = %d'%a)
outer()
结果为
whole = 0
outer = 1
inner = 1
以上,把内层函数里的 a 注释掉,则内层函数使用的 a 是外层函数定义的 a,因为,外层函数距离内层函数比全局变量距离内层函数更近。
Note:不要将变量的作用域和变量重复赋值覆盖搞混,看以下的例子
eg13:
a = 0
a = 4
def outer():
a = 1
print('outer = %d'%a)
def inner():
#a = 2
print('inner = %d'%a)
inner()
print ('whole = %d'%a)
outer()
结果为
whole = 4
outer = 1
inner = 1
5.3 注释a等于1和2
如果外层函数没有定义 a ,则会去取全局变量
eg14:
#如果外层函数没有定义 a ,则会去取全局变量
a = 0
def outer():
#a = 1
print('outer = %d'%a)
def inner():
#a = 2
print('inner = %d'%a)
inner()
print('whole = %d'%a)
outer()
结果为
whole = 0
outer = 0
inner = 0
6 匿名函数 lambda
匿名函数,匿名指的是我们不知道这个函数的名字,当我们使用时候,用lambda(λ)来声明匿名函数,这种匿名函数是没有名字的,是临时使用的
好处:
- Python写一些执行脚本时,使用lambda就可以省下定义函数过程,比如说我们只是需要写个简单的脚本来管理服务器时间,我们就不需要专门定义一个函数然后再写调用,使用lambda就可以使得代码更加精简。
- 对于一些比较抽象并且整个程序执行下来只需要调用一两次的函数,有时候给函数起个名字也是比较头疼的问题,使用lambda就不需要考虑命名的问题了。
- 简化代码的可读性,由于普通的屌丝函数阅读经常要跳到开头def定义部分,使用lambda函数可以省去这样的步骤。
格式:
函数对象名 = lambda 形式参数:表达式
eg15:
def add(x,y=3):
return x+y
add(1,2)
结果为
3
用lambda匿名函数可以一步到位
eg16:
fun1 = lambda x,y=3: x+y
调用和 def 定义的函数一样
fun1(1,2)
output
3
以上,用匿名函数代替了add()函数的功能
x,y是形式参数
x+y 是函数体内容
再来个例子熟悉下
eg17:
#匿名函数也可以有条件分支语句,返回x和y中较大值
find_max = lambda x,y: x if x>=y else y
调用
find_max(1,2)
结果为
2
小建议:当函数较为复杂时,不建议匿名函数,不太好写而且结构难理解
7 函数的递归
递归指的是一种直接或者间接调用自身的算法,它的本质是将问题分解为同类型的子问题。注意与迭代的区别。
以等比数列为例(首项为1,公比为2),看看函数的递归
eg18:
def dengbi(x):
if x == 1:
return x
else:
return 2*dengbi(x-1)
调用
dengbi(10)
结果为
512
再看看斐波拉希数列
eg19:
def Fibonacci(x):
if x == 0:
return 0
elif x == 1:
return 1
else:
return Fibonacci(x-2)+Fibonacci(x-1)
调用
Fibonacci(10)
结果为55
8 链式函数调用
在第一行调用多个函数
def add(x,y):
return x + y
def sub(x,y):
return x - y
x,y = 3,4
print((add if x > y else sub)(x,y))
x,y = 4,3
print((add if x > y else sub)(x,y))
output
-1
7
附录
global
函数无法修改全局变量
string = "string"
def do_nothing():
string = "inside a method"
do_nothing()
print(string) # string
通过 global 可以修改
string = "string"
def do_nothing():
global string
string = "inside a method"
do_nothing()
print(string) # inside a method