函数就是把一系列操作打包起来,方便以后遇到相似场景直接调用。函数可以是自己定义的,也可以使用Python的内置函数。
1.自定义函数(基础)
def 函数名称(参数名称):
返回结果
这样可以使我们的代码看起来很简洁,使代码模块化,然后遇到相似场景时直接调用,使得编程效率大大提升。
2.函数中的变量(基础)
我们在函数中常常会使用到变量,变量所处的位置不同,用起来就不一样。定义在函数里的变量被称为“局部变量”,而函数外的变量则被称为“全局变量”。
province = "浙江省"
#定义函数
def my_place():
city = "杭州"
print(city)
#调用函数
my_place()
杭州
上面的例子中province
是全局变量,city
是局部变量。
如果我们在函数中直接使用全局变量,会直接报错。
province = "浙江省"
#定义函数
def my_place():
city = "杭州"
print(city)
province = "中国"+province
print(province)
#调用函数
my_place()
UnboundLocalError: local variable 'province' referenced before assignment
Python发现,函数中没有事先定义province这个变量,即函数里不能直接使用外部的全局变量。有没有解决办法呢?其实很简单,加上global关键字就可以。
province = "浙江省"
#定义函数
def my_place():
city = "杭州"
print(city)
global province
province = "中国"+province
print(province)
#调用函数
my_place()
杭州
中国浙江省
下面再举几个复杂点的例子:
函数里变量的使用遵循就近原则,函数会调用自己里面定义的变量。
age = 18
#定义函数
def my_func():
name = "Van"
print(name)
age = 24
age += 1
print(age)
#调用函数
my_func()
Van
25
函数嵌套时和单个函数规则一样
#定义函数
def my_func():
age = 24
def my_inner_func():
age += 1
print(age)
my_inner_func()
#调用函数
my_func()
UnboundLocalError: local variable 'age' referenced before assignment
如何改正呢?
age = 18
#定义函数
def my_func():
age = 24
def my_inner_func():
age = 6
age += 1
print(age)
my_inner_func()
#调用函数
my_func()
7
如果特别想调用my_func中定义的age,此时可以使用另一个关键字nonlocal
:
age = 18
#定义函数
def my_func():
age = 24
def my_inner_func():
nonlocal age
age += 1
print(age)
my_inner_func()
#调用函数
my_func()
25
对比下global和nonlocal:
age = 18
#定义函数
def my_func():
age = 24
def my_inner_func():
global age
age += 1
print(age)
my_inner_func()
#调用函数
my_func()
19
3.函数中的参数(基础)
所谓参数,就是在定义函数时,可以接受一些“变量”进行操作。
#定义函数
def get_sum(a, b):
sum = a + b
return sum
#调用函数
get_sum(1, 2)
如果不传入参数会怎样?
#定义函数
def change_list(list):
list[1] = "欲买桂花同载酒"
list[2] = "终不似"
list[3] = "少年游"
mylist = [1, 2, 3, 4]
#调用函数
change_list()
print(mylist)
TypeError: change_list() missing 1 required positional argument: 'list'
#定义函数
def change_list(list):
list[1] = "欲买桂花同载酒"
list[2] = "终不似"
list[3] = "少年游"
mylist = [1, 2, 3, 4]
#调用函数
change_list(mylist)
print(mylist)
[1, '欲买桂花同载酒', '终不似', '少年游']
当然也可以直接指定参数值,然后直接调用,这对于一些参数的顺序传值挺有用。
def book_info(name, author):
print("名字:", name)
print("作者:", author)
book_info(name="这才是心理学", author="斯坦诺维奇")
名字: 这才是心理学
作者: 斯坦诺维奇
也可以直接指定默认值,当Python发现调用函数时没有传入该参数,就会直接使用指定的默认值。
def book_info(name, author="斯坦诺维奇"):
print("名字:", name)
print("作者:", author)
book_info(name="这才是心理学")
名字: 这才是心理学
作者: 斯坦诺维奇
如果定义函数时,对于一些参数不确定要定义多个,可以使用可变参数,一般是在参数名称的前面加上*
号,表示该位置的参数可以是多个。
def book_info(*name, author):
print("名字:", name)
print("作者:", author)
book_info("这才是心理学", "超越智商", "决策与理性", author="斯坦诺维奇")
名字: ('这才是心理学', '超越智商', '决策与理性')
作者: 斯坦诺维奇
可以看到,当函数中的参数前加颗*
后,就可以接受多个,并且最后都转化成元组。
还有一种情况是加两颗*
,它会将传来的参数转化为字典。此时,调用时要用key-value形式。
def book_info(author, **name):
print("名字:", name)
print("作者:", author)
book_info(author="斯坦诺维奇", name1="这才是心理学", name2="超越智商", name3="决策与理性")
名字: {'name1': '这才是心理学', 'name2': '超越智商', 'name3': '决策与理性'}
作者: 斯坦诺维奇
4.返回值(进阶)
函数体中 return 语句的结果就是返回值。如果一个函数没有 reutrn 语句,其实它有一个隐含的 return 语句,返回值是 None,类型也是 'NoneType'。return 语句的作用:结束函数调用、返回值(JetBrains_孙健)。
和print相比,print 仅仅是打印在控制台,而 return 则是将 return 后面的部分作为返回值作为函数的输出,可以用变量接走,继续使用该返回值做其它事(JetBrains_孙健)。
指定返回值:
#指定返回值
def my_func(x):
return x+1
print(my_func(5))
6
当使用 return 语句返回 "返回值",可以将其赋给其它变量作其它的用处。
隐含返回值:
#隐含返回值
def my_func(x):
print(x)
print(my_func(5))
5
None
所有函数都有返回值,如果没有 return 语句,会隐式地调用 return None 作为返回值。
return除了返回值,还可以起到结束函数调用的作用。
#结束函数调用
def my_func(x):
print(x)
return x+1
print(x+2)
print(my_func(5))
5
6
需要注意的是,return 只能返回单值,但值可以存在多个元素;
#返回值类型
def my_func():
return [1, 3, 5]
print(type(my_func()))
print(my_func())
<class 'list'>
[1, 3, 5]
def my_func():
return 2,4,6
print(type(my_func()))
print(my_func())
<class 'tuple'>
(2, 4, 6)
上面的return 2,4,6
看似返回多个值,实际上隐式地被Python封装成了一个元组返回。
有时我们会遇到函数嵌套的情况
#函数嵌套
def outer():
def inner():
print("inner")
print("outer")
outer()
outer
这里调用 outer(),只会执行 print("outer")
,因为 inner 也是一个函数,函数如果要调用,就必须用函数名()
方式,如:
#函数嵌套
def outer():
def inner():
print("inner")
print("outer")
inner()
outer()
outer
inner
5.匿名函数(进阶)
当不需要显式地定义函数,直接传入匿名函数更方便。比如有的函数是一次性的。
def add(x):
return x+2
add(3)
5
(lambda x : x+2)(3)
5
即匿名函数直接在后面添加括号传参即可。我们也可以传入多个参数:
(lambda x, y : x+y)(1, 2)
3
(lambda *args : print(args))(4, 5)
(4, 5)
上面的例子比较简单,假设需要遍历0-9 10个数字,然后取余数。麻烦的写法是:
def get_list():
my_list = []
for x in range(10):
my_list.append(x%2)
return my_list
print(get_list())
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
使用lambda来写:
print([(lambda x : x%2)(x) for x in range(10)])
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
以上两种方法作用是相同的,只是写法不同,可以看到匿名函数的方法很简洁。
匿名函数也可以进行赋值,只是一般不采取这种办法。
add = lambda x,y : x+y
print(add(4, 5))
9
print((lambda x,y : x+y)(4, 5))
9
匿名函数可以结合map()、filter()函数使用。
map()是 Python 内置的高阶函数,它接收一个函数 func 和一个序列,并通过把函数 f 依次作用在序列的每个元素上,得到一个新的 序列并返回。即当执行map 函数时,列表里每个值都会被取出来,然后拿去执行传入的函数。
my_list = [0,1,2,3,4,5]
print(list(map(lambda x : x+5, my_list)))
[5, 6, 7, 8, 9, 10]
my_list = [0,1,2,3,4,5]
def add(x):
return x+5
print(list(map(add, my_list)))
[5, 6, 7, 8, 9, 10]
6.闭包(进阶)
在学习闭包的知识时,会用到前面提到的作用域,即程序运行时变量可被访问的范围,定义在函数内的变量是局部变量,局部变量的作用范围只能是函数内部范围内,它不能在函数外引用。还会用到嵌套函数,函数不仅可以定义在模块的最外层,还可以定义在另外一个函数的内部,像这种定义在函数里面的函数称之为嵌套函数。
那么什么是闭包呢?这里举一个例子
#内部inner_func可以直接访问外部变量x
def outer_func(x):
def inner_func():
print(x)
inner_func()
outer_func(9)
9
#在内部无法去修改外部变量x
def outer_func(x):
def inner_func():
x += 1
print(x)
inner_func()
outer_func(9)
UnboundLocalError: local variable 'x' referenced before assignment
#若想改变,需要使用nonlocal去声明
def outer_func(x):
def inner_func():
nonlocal x
x += 1
print(x)
inner_func()
outer_func(9)
10
#闭包
def outer_func(x):
def inner_func():
print(x+1)
return inner_func
outer = outer_func(9)
outer()
10
一般情况下,函数中的局部变量仅在函数的执行期间可用,具体到上面的例子,outer=outer_func(9)
已经调用了一次函数,按理说“9” 被调用之后就不能再被使用了,但是使用outer()
时,竟然会记住这个变量。
再来举个闭包的例子
#闭包
def outer_func(x):
def inner_func(y):
print(x+y)
return inner_func
outer = outer_func(9)
outer(1)
outer(5)
outer(8)
10
14
17
当使用闭包的时候,不需要定义全局变量,也可以访问到函数内部的数据。也就是说可以拿到闭包提供的隐藏数据。
闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义的属性值一样,自由变量的可见范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量(刘志军)。
如何创建一个闭包?
首先,它得是一个嵌套函数,接着内部函数引用 nonlocal 变量,最后嵌套函数的返回是内部函数。
7.参数传递机制
在讨论参数传递机制前,先看两个例子:
第一段代码,a的值没有变,还是2
def my_func(x):
x += 1
a = 2
my_func(a)
print(a)
2
第二段代码,a的值变了,从[1,2,3]
变成了[1,2,3,9]
。
def my_func(x):
x.append(9)
a = [1, 2, 3]
my_func(a)
print(a)
[1, 2, 3, 9]
借助http://pythontutor.com/,可以看到第一段代码:
对于第二段代码
image.png
背后的原因是整数型和列表的数据类型不同。整数型是不可变类型,而列表是可变类型,当整数型 x+=1
时,会开辟一个新空间创造新的内存地址给引用,而列表x.append(9)
是可变的,不会创建新的内存地址,因此都指向了同一个内存地址。
以上就是Python中函数的传递:赋值传递,关键点在于数据类型是否可变。整型、元组、字符串等类型是不可变的,因此作为参数进行函数操作时会创建新地址,而像列表、字典是可变的数据类型,因此不会创建新的地址。
值得注意的是,当对可变类型进行+=
操作时,会创建新空间:
def my_func(x):
x + [4]
a = [1,2,3]
my_func(a)
print(a)
[1, 2, 3]
参考资料:
Python内置函数详解
Python 中的函数以及函数中的变量
Python 函数中的那些参数的使用
使用 lambda 一行实现一个函数
匿名函数-廖雪峰
匿名函数-极客学院
Python中map函数使用
一步一步教你认识Python闭包
python之函数的返回值
函数中参数的传递机制
网友评论