03-python语法基础-函数定义
函数定义
参考引用资料
https://zhuanlan.zhihu.com/p/108907210
- 函数的使用必须遵循’先定义,后调用’的原则。
- 函数的定义就相当于事先将函数体代码保存起来,然后将内存地址赋值给函数名,函数名就是对这段代码的引用,这和变量的定义是相似的。
- 没有事先定义函数而直接调用,就相当于在引用一个不存在的’变量名’
def 函数名(参数1,参数2,...): """文档描述""" 函数体 return 值
- def: 定义函数的关键字;
- 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能;
- 括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型;
- 冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码;
- """文档描述""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性;
- 函数体:由语句和表达式组成;
- return 值:定义函数的返回值,return是可有可无的。
- 定义函数发生的事情
- 申请内存空间保存函数体代码
- 将上述内存地址绑定函数名
- 定义函数不会执行函数体代码,但会检查函数体语法
- 调用函数发生的事情
- 透过函数名找到函数的内存地址
- 加括号()就是在触发函数体代码的执行
#定义阶段 def foo(): print('in the foo') bar() def bar(): print('in the bar') #调用阶段 foo()
- 按照在程序出现的形式和位置,可将函数的调用形式分为三种:
#1、语句形式: foo() #2、表达式形式: m=my_min(1,2) #将调用函数的返回值赋值给x n=10*my_min(1,2) #将调用函数的返回值乘以10的结果赋值给n #3、函数调用作为参数的形式: # my_min(2,3)作为函数my_min的第二个参数,实现了取1,2,3中的较小者赋值给m m=my_min(1,my_min(2,3))
- 若需要将函数体代码执行的结果返回给调用者,则需要用到return。return后无值或直接省略return,则默认返回None,return的返回值无类型限制,且可以将多个返回值放到一个元组内。
>>> def test(x,y,z): ... return x,y,z #等同于return (x,y,z) ... >>> res=test(1,2,3) >>> print(res) (1, 2, 3)
函数参数
- 函数的参数分为形式参数和实际参数,简称形参和实参:
- 形参即在定义函数时,括号内声明的参数。形参本质就是一个变量名,用来接收外部传来的值。
- 实参与形参的绑定关系在函数调用时生效,函数调用以后结束绑定关系
- 实参即在调用函数时,括号内传入的值,值可以是常量、变量、表达式或三者的组合:
#1:实参是常量 res=my_min(1,2) #2:实参是变量 a=1 b=2 res=my_min(a,b) #3:实参是表达式 res=my_min(10*2,10*my_min(3,4)) #4:实参可以是常量、变量、表达式的任意组合 a=2 my_min(1,a,10*my_min(3,4))
形参与实参的具体使用
位置参数
- 在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值
def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值 print('Name:%s Age:%s Sex:%s' %(name,age,sex)) register() #TypeError:缺少3个位置参数
- 在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应
def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值 print('Name:%s Age:%s Sex:%s' %(name,age,sex)) register() #TypeError:缺少3个位置参数
关键字参数
- 在调用函数时,实参可以是key=value的形式,称为关键字参数,凡是按照这种形式定义的实参,可以完全不按照从左到右的顺序定义,但仍能为指定的形参赋值
def register(name,age,sex): #定义位置形参:name,age,sex,三者都必须被传值 print('Name:%s Age:%s Sex:%s' %(name,age,sex)) register() #TypeError:缺少3个位置参数 register(sex='male',name='lili',age=18)#关键字参数
- 注意:在调用函数时,实参也可以是按位置或按关键字的混合使用,但必须保证关键字参数在位置参数后面,且不可以对一个形参重复赋值
>>> register('lili',sex='male',age=18) #正确使用 >>> register(name='lili',18,sex='male') #SyntaxError:关键字参数name=‘lili’在位置参数18之前 >>> register('lili',sex='male',age=18,name='jack') #TypeError:形参name被重复赋值
默认参数
- 在定义函数时,就已经为形参赋值,这类形参称之为默认参数
- 当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。
>>> def register(name,age,sex='male'): #默认sex的值为male ... print('Name:%s Age:%s Sex:%s' %(name,age,sex)) ... >>> register('tom',17) #大多数情况,无需为sex传值,默认为male Name:tom Age:17 Sex:male >>> register('Lili',18,'female') #少数情况,可以为sex传值female Name:Lili Age:18 Sex:female
- 需要注意:
- 默认参数必须在位置参数之后
- 默认参数的值仅在函数定义阶段被赋值一次
>>> x=1 >>> def foo(arg=x): ... print(arg) ... >>> x=5 #定义阶段arg已被赋值为1,此处的修改与默认参数arg无任何关系 >>> foo() 1
- 默认参数的值通常应设为不可变类型
def foo(n,arg=[]): arg.append(n) return arg foo(1) [1] foo(2) [1, 2] foo(3) [1, 2, 3]
- 虽然默认值可以设置为任意数据类型,但不推荐使用可变类型
m= [1111,] def func(x,y=m): print(x,y) m.append(333333) func(1)
可变长度的参数(*与**的用法)
- 参数的长度可变指的是在调用函数时,实参的个数可以不固定,
- 而在调用函数时,实参的定义无非是按位置或者按关键字两种形式
- 可变长度的位置参数
- 在最后一个形参名前加*号,溢出的位置实参,以元组的形式保存下来赋值给该形参
- *后可以跟任意名字,但约定俗成为*args
- 可变长度的位置参数
>>> def foo(x,y,z=1,*args): #在最后一个形参名args前加*号 ... print(x) ... print(y) ... print(z) ... print(args) ... >>> foo(1,2,3,4,5,6,7) #实参1、2、3按位置为形参x、y、z赋值,多余的位置实参4、5、6、7都被*接收,以元组的形式保存下来,赋值给args,即args=(4, 5, 6,7) 1 2 3 (4, 5, 6, 7)
-
-
- 如果在传入实参L时没有加*,那L就只是一个普通的位置参数了
-
>>> foo(1,2,L) #仅多出一个位置实参L 1 2 ([1, 2, 3],)
-
-
- 如果形参为常规的参数(位置或默认),实参仍可以是*的形式
-
>>> def foo(x,y,z=3): ... print(x) ... print(y) ... print(z) ... >>> foo(*[1,2]) #等同于foo(1,2) 1 2 3 >>> func(*'hello')#等价于func('h','e','l','l','o')
-
- 可变长度的关键字参数(**kwargs)
- 在最后一个形参名前加**号,溢出的关键字实参,以字典的形式保存下来赋值给该形参
- **后可以跟任意名字,但约定俗成为*kwargs
- 可变长度的关键字参数(**kwargs)
>>> def foo(x,**kwargs): #在最后一个参数kwargs前加** ... print(x) ... print(kwargs) ... >>> foo(y=2,x=1,z=3) #溢出的关键字实参y=2,z=3都被**接收,以字典的形式保存下来,赋值给kwargs 1 {'z': 3, 'y': 2}
-
-
- 如果我们事先生成了一个字典,仍然是可以传值给**kwargs的
-
>>> def foo(x,y,**kwargs): ... print(x) ... print(y) ... print(kwargs) ... >>> dic={'a':1,'b':2} >>> foo(1,2,**dic) #**dic就相当于关键字参数a=1,b=2,foo(1,2,**dic)等同foo(1,2,a=1,b=2) 1 2 {'a': 1, 'b': 2}
-
-
- 实参中带*或**
-
def func(x,y,z): print(x,y,z) func(*{''x:1,'y':2,'z':3})#等价于func('x','y','z') func(**{''x:1,'y':2,'z':3})#等价于func(x=1,y=2,z=3)
-
-
- 形参与实参中都带*或**
-
def func(x,y,**kwargs): print(x,y,kwargs) #等价于func(x=111,y=222,a=333,b=444) func(**{''x:111,'y':222,'a':333,'b':444}) #结果 111 222 {'a':333,'b':444}
-
-
混用*与**:*args必须在**kwargs之前
-
#基于考虑扩展性的问题 def func(*args,**kwargs): print(args) print(kwargs) func(1,2,3,4,5,6,x=111,y=222,z=333) #结果 (1,2,3,4,5,6) {'x',:111,'y':222,'z':333} #变型: def func(x,*args,**kwargs): print(args) print(kwargs) func(1,2,3,4,5,6,x=111,y=222,z=333)
命名关键字参数(了解)
- 在定义了**kwargs参数后,函数调用者就可以传入任意的关键字参数key=value,如果函数体代码的执行需要依赖某个key,必须在函数内进行判断
>>> def register(name,age,**kwargs): ... if 'sex' in kwargs: ... #有sex参数 ... pass ... if 'height' in kwargs: ... #有height参数 ... pass ...
- 想要限定函数的调用者必须以key=value的形式传值,Python3提供了专门的语法:需要在定义形参时,用作为一个分隔符号,号之后的形参称为命名关键字参数。对于这类参数,在函数调用时,必须按照key=value的形式为其传值,且必须被传值
>>> def register(name,age,*,sex,height): #sex,height为命名关键字参数 ... pass ... >>> register('lili',18,sex='male',height='1.8m') #正确使用 >>> register('lili',18,'male','1.8m') # TypeError:未使用关键字的形式为sex和height传值 >>> register('lili',18,height='1.8m') # TypeError没有为命名关键字参数height传值。
组合使用(了解)
- 所有参数可任意组合使用,但定义顺序必须是:位置参数、默认参数、args、命名关键字参数、*kwargs
>>> def func(x,y,z): ... print(x,y,z) ... >>> def wrapper(*args,**kwargs): ... func(*args,**kwargs) ... >>> wrapper(1,z=3,y=2) 1 2 3
- 可变参数*args与关键字参数kwargs通常是组合在一起使用的,如果一个函数的形参为*args与kwargs,那么代表该函数可以接收任何形式、任意长度的参数
#基于考虑扩展性的问题 def func(*args,**kwargs): print(args) print(kwargs) func(1,2,3,4,5,6,x=111,y=222,z=333) #结果 (1,2,3,4,5,6) {'x',:111,'y':222,'z':333}
- 函数内部还可以把接收到的参数传给另外一个函数
>>> def func(x,y,z): ... print(x,y,z) ... >>> def wrapper(*args,**kwargs): ... func(*args,**kwargs) ... >>> wrapper(1,z=3,y=2) 1 2 3
-
- 在为函数wrapper传参时,其实遵循的是函数func的参数规则,调用函数wrapper的过程分析如下:
- 位置实参1被*接收,以元组的形式保存下来,赋值给args,即args=(1,)
- 关键字实参z=3,y=2被**接收,以字典的形式保存下来,赋值给kwargs,即kwargs={'y': 2, 'z': 3};执行func(args,kwargs),即func(*(1,),* *{'y': 2, 'z': 3}),等同于func(1,z=3,y=2)
- 在为函数wrapper传参时,其实遵循的是函数func的参数规则,调用函数wrapper的过程分析如下:

浙公网安备 33010602011771号