(三)python 函数
一 函数简介
- 在日常开发中有很多代码需要在多处使用 , 为了提高代码的复用性可以把代码需要复用的代码以函数的形式进行封装 。
二、 函数的调用
- Python中内置了很多的函数来帮助我们快速的开发 , 我们可以直接调用 。 http://docs.python.org/3/library/functions.html#abs
- 如: abs()求绝对 值的函数 。 在python的交互环境下可以直接通过help(abs)查看abs函数的帮助信息 。
- 在调用函数的时候 , 传入参数的数量和类型不对时都会报出TypeError错误 。 并且python会给你报出明确的错误信息 。
- Python中用于数据类型的转换的函数 :
- int(): 将其他类型的数据转换为int类型
- float() : 将其他类型的数据转换为float
- str() : 将其他类型的数据转换为字符串类型的数据
- bool() : 将其他类型的数据转换为布尔值 (只有空字符串或者整数位0是才能转换为False)
- 函数名其实就是一个指向函数对象的引用 ,完全可以把函数名赋给另一个变量 , 相当于给函数起别名
三、 定义函数
在python中定义函数要使用def语句 , 一次写出函数名 、 括号 、 括号中的参数和冒号: , 然后在缩进块中编写函数体 , 函数的返回值用return语句返回 。
# -*- coding: utf-8 -*- #python 中函数定义 def testFunction(age): if age>100 : print("你是老年人") else : print("你是年轻人") #调用函数 age=input("请输入你的年龄:") testFunction(int(age)) input("输入任意字符退出!")
- 函数内一旦执行到return语句时 , 函数就执行完毕 , 并将结果返回 。 如果函数中没有return其实也是有返回值的只是返回值为None , return None可以简写为return
- 可以在Python的交互环境下 使用 from test import testFunction (假设你已经定义好了testFunction函数并保存在了 test.py文件中) 。
- 空函数: 如果想定义一个什么都不做的空函数 , 可以使用pass语句 。 (实际上pass语句相当于一个占位符 , 比如现在还没想好怎么写这个函数的代码 , 但是需要代码跑起来 。没有pass语句的空函数会报错 。 )
函数的参数检查:
- 在调用函数时如果参数个数不对会报出TypeError错误 , 但是在参数类型不对时 我们自己定义的函数和python内置的函数却不太一样 。 这说明自己定义的含义不太完善 。
在自己定义的函数中增加参数类型检查:
# -*- coding: utf-8 -*- #python 中函数定义 def testFunction(age): if not isinstance(age , int): raise TypeError("参数类型不正确") else : print("参数类型正确") if age>100 : print("你是老年人") else : print("你是年轻人") #调用函数 age=input("请输入你的年龄:") testFunction(age) input("输入任意字符退出!")
返回多个值:
python中函数可以返回多个值 ,但这只是一种假象 , 实际上是把返回的值放到一个元祖中 , 变量取值时按顺序取出元祖中的值 。
# -*- coding: utf-8-*- #使用数学函数时需要先导入math包 import math #python中定义函数可以返回多个返回值 # 情景: 在游戏开发时 传入当前的位置横纵坐标 , 移动角度 , 移动步数 之后返回移动之后的位置横纵坐标 def move(x , y , step , angle) : nx = x + step*math.cos(x) ny = y + step*math.sin(y) return nx , ny #执行代码 , 并获取返回值 x, y =move(1 ,1 ,3 ,30) print("移动之后的位置是:" , x , "," ,y) #查看返货值的实质 r = move(1,1,3,30) print("返回值的实质:" , r)
四 、 函数的参数
常规参数:在定义函数时括号内定义需要的参数
def move(x , y , step , angle) : nx = x + step*math.cos(x) ny = y + step*math.sin(y) return nx , ny 缺点: 灵活性低 , 必须要求参数个数完全一致时才能正确执行
默认参数 : 在定义函数时 ,定义参数时给定默认值 ,当传入参数时使用传入的参数 , 当不传参数时使用默认的参数值
def move(x=0 , y=0 , step=1 , angle=45) : nx = x + step*math.cos(x) ny = y + step*math.sin(y) return nx , ny 当传入参数时使用传入的参数 , 当不传入参数时 , 使用默认的参数
- 使用默认参数时要注意:
- 必填参数一定要在默认参数之前 , 否则python解释器会报错 。
- 设置默认参数时要考虑之后的使用 。
- 在使用默认参数时 ,如果def move(x=0 , y=0 , step=1 , angle=45)只想传入其中第1个和第4个参数 , 则可以move(x=1 , angle=45)在传参是指定参数名 调用。
- 默认参数必须是不变对象(str 、 数值类型)Python在定义的时候 , 默认参数的值就被计算出来了 , 如果默认的参数是一个可变变量(引用变量) , 默认参数被操作后参数的内容改变 , 但是参数的引用并没有被改变如果要用可变对象 , 则默认值设置为None
- 扩充:
- 不变对象存在的意义:不变对象一旦创建 , 对象内部的数据就不能修改 , 这样就减少了由于修改数据导致的错误 。 在多线程情境下 , 不变对象读写时不需要加锁 , 效率很高。
- 使用默认参数时要注意:
可变参数
- 在定义函数时 , 由于参数个数不确定 , 我们可以定义可变参数 , 在函数调用数 , 可以传入任意个参数 。
可变参数的定义方式和定义list的方式区别在于定义可变参数前加*
def calc(*numbers): sum=0 for n : numbers : sum = sum + n*n return sum 在调用时直接calc(1,3,4)即可
- 在函数内部参数numbers实际接收到的是一个tuple
当已经有了一个list或tuple需要传入函数中时该怎么办?
nums=[1,2,4] 可以使用在nums前加* 的方式传入 calc(*nums)
示例
#Python可变参数 def cacl(*numbers): sum=0 for n in numbers: sum=sum+n*n return sum #可变参数接收普通参数 r = cacl(1,2,3,4) print(r) #可变参数接收list或tuple nums=(1,2,3,4) r2=cacl(*nums) print(r2)
关键字参数
可变参数允许你传入0个或任意个参数 , 在函数接收时自动组装成一个tuple , 而关键字参数允许你传入0个或一个键值对 , 在函数接收时自动装成一个
# -*- coding : utf-8 -*- #python 关键字参数 def person (name , age , **kw): print ("name:" , name , " , age:" , age, " , other:" , kw) #调用方式一 person('怪兽1' , 10) #调用方式二 person("怪兽2" , 11 , city="北京" , like="砸房子") #调用方式三 other={'brithday':"国庆节" , 'city':"北京" ,} person("怪兽3",12 , brithday=other["brithday"] , city=other["city"])
- 可以用于用户注册的情景 , 当用户注册时除了必填的用户名密码外 ,如果用户还想提供其他的信息 , 就可以使用关键字参数 。
- 在使用时, 可以在参数中填入内容 ,也可以先组装一个dict在传进去(类似于可变参数 。 )
命名关键字参数
- 在使用关键字参数时 , 函数的调用者可以传入不收限制的关键字参数 , 至于到底传入了哪些参数 ,在代码执行中需要用到哪些参数 , 还需要在函数内部进行检查
如果想要限制关键字参数的内容 , 就可以使用命名关键字函数
#python命名关键字参数 print("命名关键字参数:") #命名关键字参数和普通参数之间用*分割 def dog(name , age , * ,city , like) : print ("name:" , name , " , age:" , age, " , city:" , city , " , like:" , like) #如果已经定义了可变参数, ,那么可变参数之后的默认为命名关键字参数 def cat(name , age , *nums , like): print ("name:" , name , " , age:" , age, nums, " , like:" , like) #调用 #dog('花花' , 10 , city="北京" , like="皮球" , brithday="2132342") dog('花花' , 10 , city="北京" , like="皮球" ) cat('花花' , 10 , 1,2,4,5,6,7,8, like="皮球" )
- 参数组合
- 注意: 在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
- 最神奇的是你可以通过一个tuple和一个dict 调用任何函数(func(*args , **kw)) , 无论函数是如何定义的(这真的是太没节操了) 。
- 虽然支持多种参数组合的形式 , 但是尽量少的使用参数组合 , 否则接口的理解性很差 。
五 、 递归函数
递归思想: 在函数内部调用自己本身 。
# -*- coding : utf-8 -*- #Python函数递归 def fact(n): if n==1 : return 1 return n*fact(n-1) #调用 i = fact(10) print(i)
- 递归函数定义简单 , 逻辑清晰 , 理论上所有的递归函数都可以写成循环的方式 , 但是循环的逻辑不如递归清晰 。
- 注意 ,使用递归函数要防止栈溢出(方法的执行在栈中 , 递归层次太多导致栈溢出)
- 解决递归调用栈溢出的方法是通过尾递归优化 , 事实上尾递归和循环的效果是一样的 ,所以把循环看成一种特殊的尾递归函数也是可以 、
- 尾递归: 尾递归是指在函数返回的时候 , 调用自己本身 , 并且return语句不能包含表达式 , 这样 编译器或解释器就可以把尾递归做优化 , 使递归本身无论调用多少次 , 都只占用一个占内存 , 不会出现 栈溢出的情况 。
- 遗憾的是大多数编程语言都没有针对尾递归做优化, Python解释器也没有做优化 , 所以即使在python中递归用尾调用的形式 , 仍然会造成栈溢出 。 (介绍 尾递归优化 : https://blog.csdn.net/azhegps/article/details/72638906)