1. 函数必须先定义后调用,没有定义函数是一定不能够调用的
2. 函数在定义阶段,只检测语法是否错误,不检测逻辑是否有问题
3. 逻辑上的错误只会在调用阶段检测
4. 函数一定是被调用之后才会执行函数内部的代码块,不调用函数一定不会执行函数的
# 如何调用函数
函数名() # 只要函数名加括号一定会执行函数体代码
函数如果在定义阶段有参数,那么,在调用的时候,连参数一块写上
**************************************************************************************函数调用的内部原理:
1. 先在内存空间中申请一块空间地址来存储函数体代码
2. 把函数名和函数体所在的空间地址绑定在一起
3. 以后只需要通过函数名()来访问函数体代码即可
def用来声明一个函数,python的函数包括函数名称、参数、函数体、函数体中涉及到的变量、返回值。
实际上,函数名称其实是一个变量名,def表示将保存在某块内存区域中的函数代码体赋值给函数名变量。例如:
def myfunc(x,y,z):
...CODE...
上面表示将函数体赋值给变量名myfunc。如下图:
既然是变量,就可以进行输出:
def myfunc(x):
return x+5
print(myfunc)
输出结果:
由于python是解释型语言,所以必须先定义函数,才能调用函数。
如果导入一个模块文件,导入的时候会解释、执行文件中的代码,包括def语句,也就是说,导入文件时会先声明好函数。
请一定理解本节内容,也许细节方面可能会有些不准确,但对于深入理解函数来说(不仅限于python语言),是非常有帮助的,特别是理解作用域规则的时候。
python是解释性语言,读一行解释一行,解释一行忘记一行。而函数是一种代码块,代码块是一个解释单元,是一个整体。在代码块范围内不会忘记读取过的行,也不会读一行就立即解释一行,而是读取完所有代码块内的行,然后统筹安排地进行解释。关于这一点,在后面的文章代码块详述中有非常详细的解释,建议一读。
当python读取到def所在行的时候,知道这是一个函数声明语句,它有一个属于自己的代码块范围,于是会读完整个代码块,然后解释这个代码块。在这个解释过程中,会记录好变量以及该变量的所属作用域(是全局范围内的变量还是函数的本地变量),但一定注意,def声明函数的过程中不会进行变量的赋值(参数默认值除外,见下文),只有在函数调用的时候才会进行变量赋值。换句话说,在def声明函数的过程中,在函数被调用之前,函数所记录的变量一直都是变量的地址,或者通俗一点理解为记录变量的名称,而不会进行变量的赋值替换。
实际上,变量的明确的值会当作常量被记录起来。如a=10
的10被作为常量,而变量a赋值给变量b时b=a
,a显然不会作为常量。
如下函数:
x=3
def myfunc(a,b):
c=10
print(x,a,b,c)
myfunc(5,6)
输出结果为:"3 5 6 10"。
上面的函数涉及到了4个变量:a、b、c、x。其中:
在def的过程中,会完完整整地记录好这些变量以及所属作用域,但只会记录,不会进行变量的赋值。如下图:
然后函数被调用,这时候才会开始根据记录的作用域搜索变量是否存在,是否已经赋值(非本地变量),并对需要赋值的变量赋值:
a=5,b=6
,然后赋值普通的本地变量c=10
如图:
最后执行print(x,a,b,c)
输出这些变量的值。
还需注意,python是读一行解释一行的,在函数调用过程中,因为c=10
在print()
的前面,所以是先赋值c=10
,再执行print,如果print在c=10
前面,则先执行print,再赋值,这显然是错误的,因为print()中使用了变量c,但目前还没有对其赋值。这和其它语言可能有些不同(特别是编译型语言),它们可能会无视变量赋值以及变量使用的位置前后关系。
如果上面的示例中,函数myfunc调用之前,将变量x赋值为另一个值:
x=3
def myfunc(a,b):
c=10
print(x,a,b,c)
x=33
myfunc(5,6)
这时将输出:"33 5 6 10"。因为x是全局变量,只有在函数调用的时候才会去找到变量x对应的值,而这时全局变量的值已经是33。
# 我们将以下代码复制到python文件中,保存为 九九乘法表.py
def multiple_table():
for i in range(1,10):
for j in range(1,10):
if j<=i:
print(f"{i} * {j} = {i*j}",end='\t')
print("")
multiple_table() # 调用该函数
# 给函数添加注释,应该定义在函数下方,在连续的三对引号中编写对函数的说明文字
def multiple_table():
"""这是一个生成九九乘法表的函数"""
for i in range(1,10):
print('\n')
for j in range(1,10):
if j<=i:
print("%d * %d = %d" % (i,j,i*j),end = '\t')
def 函数名(形参1,形参2,...,形参n): #函数名的命名符合标识符的命名规则
"函数_文档字符串"
函数体
return 语句 #不带表达式的 return 相当于返回 None,None的逻辑值恒为False
# 例1
def sumup(x,y):
"两数之和"
return x+y
sumup(3,4)
函数参数的作用:增加函数的通用性
位置参数:所谓位置参数,指的是当调用一个函数时,实参按位置依次传递
def sumup(x,y)
return x+y
关键字参数:所谓关键字参数,指的是当调用一个函数的时候,可以用key=value的方式指定给某个参数赋值,这样就不一定严格遵守函数声明里的参数顺序。
def myfunc(a,b,c):
print(f"a:{a},b:{b},c:{c}")
myfunc(c=3,b=4,a=2)
# 同时使用位置参数和关键字参数时,位置参数必须在关键字参数前
def myfunc(a,b,c):
print(f"a:{a},b:{b},c:{c}")
myfunc(3,c=4,b=2)
默认参数(缺省参数):默认参数就是在声明函数的时候使用一些包含默认值的参数,函数声明时,普通参数在前,默认参数在后
# 函数调用时,若没有给默认参数传递参数,则该形参直接取默认参数值,如果调用时给默认参数传递了参数,那么该形参的值应该等于外部传递的参数
def func(x,y,z=1,w=3):
return x+y+z+w
test1 = func(1,2,3)
test2 = func(1,4,w=4)
# 例1:
def print_info(name,gender=True):
gender_text = "男生"
if not gender:
gender_text = "女生"
print("{}是{}".format(name,gender_text))
print_info("小明")
print_info("小美",False)
默认参数必须指向不变对象,可以参考这篇文章
# 使用help()函数查看函数文档的时候,经常会发现在函数的原型中,会看到一个斜杠,该斜杠表示,其左边的参数必须传递位置参数而不能是关键字参数
In [10]: help(abs)
Help on built-in function abs in module builtins:
abs(x, /)
Return the absolute value of the argument.
In [11]: help(sum)
Help on built-in function sum in module builtins:
sum(iterable, /, start=0)
Return the sum of a 'start' value (default: 0) plus an iterable of numbers
When the iterable is empty, return the start value.
This function is intended specifically for use with numeric values and may
reject non-numeric types.
# 上面help(abs)之后,我们看到里面的形参为x,我们调用abs(x=-1.5),Python会报错
In [12]: abs(-1.5) #只能以位置参数传入
Out[12]: 1.5
# 上面help(sum)之后,我们看到里面斜杠右边有个start,从而start参数可以选择以位置参数或者关键字参数传入
In [14]: sum([1,2,4,5],start=2)
Out[14]: 14
In [15]: sum([1,2,4,5],2)
Out[15]: 14
# 同样我们自己创建函数时,也可以利用这个"/"号
def myfunc(a,b,/,c,d):
print(f"a:{a},b:{b},c:{c},d:{d}")
myfunc(3,3,4,d=2)
# 上面我们知道使用"/"可以限制左边的参数只能以位置参数传入,当我们使用"*"时,就可以限制右边的参数只能以关键字参数传入
def myfunc(a,b,*,c,d):
print(f"a:{a},b:{b},c:{c},d:{d}")
myfunc(3,c=4,b=3,d=2)
收集参数就是说只指定一个参数,然后允许调用函数时传入任意数量的参数。
元组参数:在某个参数前加一个 * 号,将前面必须赋值的参数赋值完之后,剩下的打包成一个元组接收(可以接收空元组)。
# 函数定义时,*参数收集所有未匹配的位置参数组成一个tuple对象,局部变量tup指向此tuple对象,局部变量tup指向该tuple对象
def myfunc(d,*tup):
print(f'必须参数:{d}')
print(f'非必须参数:{tup}')
myfunc(10)
myfunc(10,20,30,'test')
必须参数:10
非必须参数:()
必须参数:10
非必须参数:(20, 30, 'test')
# 函数调用时,*参数用来解包tuple对象中的每个元素,作为一个个位置参数传入到函数中
def myfunc(name,age,*interest):
print('name:{},age:{},interest:{}'.format(name,age,interest))
tuple1 = ('football','tourist','eating')
myfunc('xiyangyang','17',*tuple1)
tuple2 = ('meiyangyang','15','eating','swimming','sleeping')
myfunc(*tuple2)
# 如果在收集参数后面还需要指定其他参数,那么在调用函数的时候,就应该使用关键字参数来指定后面的参数,否则Python就会把实参纳入收集参数中
def myfunc(*arg,x,y):
print(f"arg:{arg},x:{x},y:{y}")
myfunc(3,4,3,2)
TypeError: myfunc() missing 2 required keyword-only arguments: 'x' and 'y' #报错信息
def myfunc(*arg,x,y):
print(f"arg:{arg},x:{x},y:{y}")
myfunc(3,4,y=3,x=2)
arg:(3, 4),x:2,y:3
字典参数:元组参数允许你传入0个或者任意个参数,这些可变参数在函数调用时会自动组装成为一个tuple,而关键字参数允许你传入0个或者任意个关键字参数,这些关键字参数会在函数内部自动组装成为一个dict
# 函数定义时,**参数收集所有未匹配的关键字参数组成一个dict对象,局部变量dict_interest指向此dict对象
def student(name,age,**dict_interest):
print('name:{},age:{},interest:{}'.format(name,age,dict_interest))
student('luxiaofeng',18,sport = 'basketball',food = 'humberger')
name:luxiaofeng,age:18,interest:{'sport': 'basketball', 'food': 'humberger'}
# 函数调用时,**参数用于解包dict对象的每个元素,作为一个一个的关键字参数传入到函数中
def student(name,age,**dict_interest):
print('name:{},age:{},interest:{}'.format(name,age,dict_interest))
dict1 = {'sports':'football','food':'humberger'}
student('zouhongwei',19,**dict1)
在来看一个例子:
def demo(*args,**kwargs):
print(args)
print(kwargs)
gl_nums = (1,2,3)
gl_dict = {"name":"小明","age":18}
demo(*gl_nums,**gl_dict)
(1, 2, 3)
{'name': '小明', 'age': 18}
前面我们讲过元组的打包:
tuple = "a","b","c","d" # 这样创建元组的方式叫做元组打包
利用这个,我们可以在函数中同时返回多个值:
def measure():
temperature = 23
wetness = 20
return temperature,wetness
a = measure() # 这里的a就是一个元组
再利用元组解包,就能单独拿到每个值了:
def measure():
temperature = 23
wetness = 20
return temperature,wetness
temperature,wetness = measure()
练习题1:定义一个函数 sum_numbers ,能够接收一个 num 的整数参数,计算 1~num的整数和
# 递归
def sum_numbers(num):
if num == 1:
return 1 # 设定一个出口
temp = num + sum_numbers(num-1)
return temp
print(sum_numbers(100))