光用python提供的内置函数是满足不了开发需求的,我们需要把一些重复的代码逻辑封装起来定义成一个函数,以便于我们调用,于是就有了自定义函数
自定义函数
语法
def:关键字
funcname:函数的名字
args:参数
return:返回值
def funcname(args): ''' 文档注释 :param args: :return: ''' print('hello',args) #函数体,也就是函数要做什么事 return 123
好,你一脸懵逼,不要紧,举个栗子:
def funcname(args): ''' 文档注释 :param args: :return: ''' print('hello',args) #函数要做什么事 return 123
调用函数,直接用函数的名字里面加上要传的参数:
funcname("sb") #调用函数,将sb传到args的位置
输出结果:
函数和变量就是一回事
return就是函数被调用返回的结果,需要赋值给变量保存下来,还是上面的例子:
def funcname(args): ''' 文档注释 :param args: :return: ''' print('hello',args) return 123 res=funcname("sb") #将调用的返回值赋值给res保存下来 print(res)
再看输出结果:
是不是return的123出来了?
函数一碰到return就会终止,并把return返回的值作为它的一个返回结果,如果赋值给一个变量了,就会把这个值保存下来,如果不赋值,就没有人拿
def funcname(args): ''' 文档注释 :param args: :return: ''' print('hello',args) return 123 print("==========") print("==========") #碰到return就会终止,后面的不会执行
funcname("sb")
看下结果:return后的没有被执行
为什么要有返回值?如果不加返回值呢?
举个栗子:
def funcname(args): ''' 文档注释 :param args: :return: ''' print('hello',args) #没有return
res=funcname("sb") print(res)
看下结果:
没有返回值,python会自动返回None
因为函数通常要做某一个功能,无论执行多少行代码,最终都要完成一件事, 得到一个结果,return就是把这个结果给别人去用
返回值:
1.没有返回值---------python会默认返回None
2.一个返回值---------返回的就是return后定义的那一个值,该返回啥就返回啥
3.多个返回值----------返回的是一个元组
举个例子:
def funcname(args): ''' 文档注释 :param args: :return: ''' print('hello',args) return 1,2,3,4 #多个返回值 res=funcname("sb") print(res)
看下结果:
返回的是一个元组
类似于去超市买东西,可以不买东西出来,可以买一个东西出来,可以买许多东西出来,比如说杜蕾斯,香皂,润滑油,皮鞭等,就要有多个返回值
返回值可以使任意的类型,比如:字符串。列表,元组,字典
举个例子:返回值里套一个元组的方式
def funcname(args): ''' 文档注释 :param args: :return: ''' print('hello',args) return 1, 2, 3, 4, 5, 6, ('a', 'b') res=funcname("sb") print(res)
看下结果:
返回值里就是一个元组套了一个元组
函数的三种定义方式:
第一种:
无参函数:(就是没有参数)
def av(): print('你喜欢那个AV女优')
定义没有参数,调用也不用参数
def av(): print('你喜欢那个AV女优') av()
输出结果:
有参函数:
def av(x,y,z): print('你想上哪个?',x,y,z)
定义有参数,调用也必须有参数
def av(x,y,z): print('你想上哪个?',x,y,z) av("苍井空","波多野结衣","吉泽明步")
输出结果:
空函数:
def f3(): pass f3()
这种函数写不写参数不是重点,重点是pass
比如说开发一个ftp功能
def put(): ''' file upload :return: ''' pass def get(): ''' file download :return: ''' pass def cd(): ''' change directory :return: ''' pass
比如get函数有了灵感,就可以把get函数写了一遍,哪个有用写哪个, 没写的以后看到注释可以再替换pass
pass就是什么也不干的意思,在各个地方都可以用,比如if
比如说你put函数没来得及写,但是你同事着急用,这期间你怀孕了请假要去堕胎,这时候你同事可以做一个判断,对你同事没有影响
a=1 if a > 1: print('===') #如果a大于1,你同事可以写自己的功能 else: put()
函数定义的的三种调用形式
先定义一个函数,求两个值最大的值,return可以任何形式,这里我们用三元表达式的方式
def my_max(x,y): return x if x > y else y
第一种调用形式:语句形式
什么叫语句:print就是一个语句
什么叫表达式:1+1;2*1就是表达式
def my_max(x,y): return x if x > y else y my_max(1,2)
这就是语句形式,没有任何运算,也没有任何等式
通常用于没有返回值的调用 ,有返回值得调用通常用表达式的形式
第二种调用形式:表达式的形式
可以将返回值保存下来
def my_max(x,y): return x if x > y else y res=my_max(1,2) print(res)
可以用返回值进行运算
def my_max(x,y): return x if x > y else y res=10*my_max(1,2) #用10乘以返回值 print(res)
输出结果
第三种调用方式:作为参数
求三个数的最大的值
def my_max(x,y): return x if x > y else y res=my_max(10,my_max(11,12)) print(res)
先用11和12比较,然后把12作为参数传给下一个,用12和10比较,原理就是在求10 11 12这三者的最大值
形参和实参
形式参数
def foo(x,y): print(x) print(y)
其中x,y就是形式参数,没有被调用,就只是两个名字,根本不占用空间,别人一调用它,才会占用空间,在函数的内部产生内存占用,实际上就是把实参的值传给形参
在python中,名字没有任何储值功能,任何的赋值都是绑定一个名字到一个值
实际参数
foo(1,2)
1和2就是实际参数
实际参数是真实占用内存空间的,形式参数只有被调用,被传值了,它才占用内存空间
实参给形参传值的过程就是一个变量名的绑定过程
参数的动态性:(不受限制)
可以传浮点型
def foo(x,y): return x+y foo(1.2,3)
输出结果:
可以传字符串
def foo(x,y): return x+y print(foo("a","b"))
输出结果:
这么做有好处也坏处:
好处:在定义函数时变得非常简单,非常灵活
坏处:调用时候容易出错,懵逼了?看下面例子:
举个栗子:
我们传1和a试一试
def foo(x,y): return x+y print(foo(1,"a"))
看下结果:
what?报错了?告诉你数字和字符串不能相加
那怎么解决呢?在实际工作中,不可能完成限制功能,只能曲线救国,有三种方法
解决动态性问题:
第一种:(推荐)
就是写好文档注释
def foo(x,y): ''' :param x:int #告诉别人x必须是int :param y:int #告诉别人y必须是int :return: ''' return x+y print(foo(1,"a"))
别人在用的时候先help一下,一看就知道这个函数怎么用了
def foo(x,y): ''' :param x:int #告诉别人x必须是int :param y:int #告诉别人y必须是int :return: ''' return x+y print(help(foo))
输出结果:
加上文档注释并没有对函数起到限制作用,如果非法传参还是会报错
第二种:(很多人这样去做,但高手一般不会这么做)
def foo(x,y): if type(x) is int and type(y) is int: #当x是int和y是int类型时,才会返回 return x+y
函数的主要功能是加法,中间加了个if,跟功能一毛钱关系都没有只是为了报错采取的一个措施,相当于代码的擦屁股纸,你也可以这么做,见仁见智
第三种:(更加骚气)
def foo(x:int,y:int)->int: #解释一下x是int类型,y是int类型,返回值是int类型 return x+y print(foo(1,2))
:int只是一种注释,语法没毛病,没有影响,不会报错,只是调用了另一种叫__annotations__注释:
def foo(x:int,y:int)->int: return x+y print(foo.__annotations__) print(foo(1,2))
看一下结果:
解释一下x是int类型,y是int类型,返回值是int类型
高手一般觉得第一种牛逼!
形参和实参的使用:
在使用的角度
实参的使用
实参给形参传值有两种方式
先定义一个函数吧
def foo(x,y): print(x) print(y)
第一种:
按照位置(顺序)传值
举个栗子:
foo(1,2)
输出结果:
foo(2,1)
输出结果:
按位置定义的实参受位置影响
2.按照关键字传值
一个key对应一个value
foo(y=2,x=1)
x已经定义等于1,y已经定义等于2,所以先打印1,再打印2
这个位置没关系,不受位置影响
注意问题一:针对同一个形式参数,你可以按照位置或者按照关键字传值,但是只能用一种方式,不能重复给一个参数传值
举个栗子:
foo(1,x=1,y=2)
输出结果:
注意问题二:按关键字传值必须在按位置传值的右边
foo(x=1,2)
乍一看,没毛病,来看一下输出
x=1是关键字,2是位置,关键字传值必须在按位置传值的右边,所以报错
形参的使用
形参一共有四种形式:
位置参数,默认参数,*args可变参数,**kwargs
先定义一个示例函数:
def foo(x,y,z): print(x) print(y) print(z)
1.位置参数
特性:多一个不行,少一个不行,必须传值
可以按位置传值,也可以按关键字传值
举个栗子:
foo(1,2,3)
多一个少一个都会报错,这里不进行演示了,看一下按照位置传参
foo(x=1,z=2,y=3)
输出结果
2.默认参数
在定义形参是给它一个值
定义已经传值,在调用时可以不用传值,如果非要传,会覆盖掉,针对任何形式参数,都有两种传值方式
def foo(x,y=2): print(x) print(y) foo(1)
y已经被定义成了2,所以2不用传值
def foo(x,y=2): print(x) print(y) foo(1,3)
y已经定义了2,非要传给y一个3,那么他会覆盖
按照位置来传参
def foo(x,y=2): print(x) print(y) foo(x=1,y=5)
1.通常把变化比较小的参数定义成默认参数
假如注册信息男的多的话
def register(user,age,gender='male'): print(user) print(age) print(gender) register('egon',18)
2.默认参数必须在位置参数右边
def foo(x=1,y): pass
3.默认参数一定要定义成一个不可变类型
def foo(x,l=[]): l.append(x) return l print(foo(1)) #[1] print(foo(2)) #[2] print(foo(3))
记住要等于一个明确的不可变类型
4.默认参数在定义时就已经被赋了一个明确的值了,后期的变化对其无效
name='cangjingkong' def foo(x,y=name): print(x) print(y) name='av' foo(1)
后期重新定义name对其无效,因为一开始就被赋值了