1. 什么是函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。之前文章中我们已经使用过python提供的内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
2. 自定义函数
(1)
函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。
(2)
任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
(3)
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
(4)
函数内容以冒号 : 起始,并且缩进。
(5)
return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。
(6)
语法格式:
def 函数名(参数列表):
函数体
# demo
def Max(num1, num2):
if num1 > num2:
return num1
else:
return num2
print('the max value is:> ', Max(1, 4))
3. 参数传递
说明,如果读者接触过c/c++,那么这部分不用看,它就是相当于c/c++函数中的值传递和引用传递。
(1)
可更改(mutable)与不可更改(immutable)对象
在 python 中,str、 tuple 和 number 是不可更改的对象,而 list、dict、set则是可以修改的对象
。
[a]
. 不可变类型
变量赋值 a=5 后再赋值 a=10,这里实际是新生成一个 int 值对象 10,再让 a 指向它,而 5 被丢弃,不是改变 a 的值,相当于新生成了 a。说白了,就是两次a所占的内存空间是不同的
。
# 通过id()来获取变量a所占的内存空间, 发现两次a所占的内存空间并不是相同的,
# 说明第2次的赋值并不是简单的对第1次的a进行了值的修改,
# 而是重新开辟了内存空间来存储.
a = 5
print(id(a))
a = 10
print(id(a))
[b]
. 可变类型
变量赋值 la=[1,2,3,4] 后再赋值 la[2]=222 则是将 la 的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了。
说白了,就是对同一个la进行了操作
。
# 这里进行la[2]的操作实际上就就是对一开始的la进行了操作,
# 操作的是同一块内存空间.
la = [1,2,3,4]
print(id(la))
la[2] = 222
print(id(la))
print(la)
[c]
. 参数传递总结
a)
不可变类型
类似 c/c++ 中的值传递,如整数、字符串、元组
。如 fun(a),传递的只是 a 的值,没有影响 a 对象本身。如果在 fun(a) 内部修改 a 的值,则是新生成一个 a 的对象。
实参
:在函数调用时, 函数名后面括号中的参数称之为"实际参数", 简称”实参“。
形参
:定义函数时函数名后面括号中的变量称为"形式参数",简称"形参"。
# 以下函数中, num称之为形参, number称之为实参.
def test(num):
pass
number=111
test(number)
# 可以看见在调用函数前后,形参和实参指向的是同一个对象(id()相同),
# 在函数内部修改形参后,形参指向的是不同的 id.
def change(number):
print(id(number)) # 指向的是同一个对象
number = 10
print(id(number)) # 一个新对象, 新的内存空间
number = 1
print(id(number))
change(number)
b)
可变类型
类似 c++ 中的引用传递,如 列表,字典,集合
。如 fun(la),则是将 la 真正的传过去,修改后 fun 外部的 la 也会受影响。
# 我们在函数内部对传入的dict进行改变, 发现外部的dict也同步改变了,
# 说明实参和形参都是同一个对象, 都指向同一块内存空间.
def changeDict(d):
print(id(d))
d['num'] = 102
print(d)
dict = {"num": 101, "name": "Zhang"}
print(id(dict))
changeDict(dict)
print(dict)
4. 函数参数
(1)
必需参数
必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
# 调用test()函数, 必须传入一个参数, 否则报错
def test(str):
pass
test() # 调用函数时没有传入参数, 则报错
(2)
关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值
。使用关键字参数允许函数调用时参数的顺序与声明时不一致
,因为 python 解释器能够用参数名匹配参数值
。
# 假如我们规定函数的第1个参数是字符串类型, 第2个参数是整数类型,
# 第三个参数是列表类型, 那么一般地, 我们在函数调用时应该按照事先
# 规定的顺序传入对象, 但是由于我们在函数调用时指定了关键字参数,
# 所以顺序可以随意写, 因为python解释器能够用参数名匹配参数值.
def test(str, num, ls):
print(str, num, id)
test(num=191, ls=[1,2], str="HelloWorld")
(3)
默认参数
调用函数时,如果没有传递参数,则会使用默认参数。
# 为参数age指定了默认值, 调用函数时, 如果没有传入第三个参数,
# 将使用默认值.
def printStudent(num, name, age=18):
print('student info is:>', num, name, age)
printStudent(101, 'Zhang') # 没有指定第3个参数, 将使用默认值
printStudent(102, 'Wang', 19) # 指定了第3个参数
(4)
不定长参数
在定义函数时,可能事先不确定传入函数的参数个数,此时可以用不定长参数来接收。
(a)
格式1
加了星号 *
的参数会以元组(tuple)的形式导入
,存放所有未命名的变量参数。
# 格式
def fun([formal_args,] *var_args_tuple ):
pass
# demo
def params(arg1, *vartuple):
print(arg1, end=", ")
print(vartuple) # 元组
for item in vartuple:
print(item, end="、")
params(22)
params(10, 20, 30, 40)
(b)
格式2
加了两个星号 **
的参数会以字典的形式导入
。
# 格式
def fun([formal_args,] **var_args_dict ):
pass
# demo
# 因为剩余参数是以字典的形式导入的, 所以调用函数时必须将剩余参数
# 以key=value的形式进行调用, 否则报错
def Params(arg1, **var_args_dict):
print(arg1)
print(var_args_dict)
Params(1, num=101, name='Zhang', age=18)
(c)
其他
声明函数时,参数中星号 * 可以单独出现
。
如果单独出现星号 *
,则星号 * 后的参数必须用关键字传入
。
def sum(a, b, *, c):
return a + b + c
# sum(1,2,3) # error
sum(1,2,c=3) # 正确
5. return语句
return [表达式]
语句用于退出函数,选择性地向调用方返回一个表达式。
如果函数没有任何返回值,则相当于返回 None
。
6. 函数的嵌套
不同于c/c++,python中的函数是可以嵌套的
。
# 来看一个demo
def foo(factor):
def bar(number):
return number * factor
return bar # 返回一个函数
x = foo(2)
print("the result is:> ", x(5))
print("the result is:> ", (foo(2))(5))
在这里,一个函数(bar()
)位于另一个函数(foo()
)中,且外面的函数返回里面的函数。也就是返一个函数,而不是调用它。
重要的是,返回的函数能够访问其定义所在的作用域
。
实际上,我们称bar()这样存储其所在作用域的函数称之为 闭包
。