函数基础
一、函数定义
1、定义
函数的定义每个人理解的不一样,最后定义的也是不尽相同,函数可以说它是一个拥有名称、参数以及返回值的代码块。
2、函数种类
- 内置函数
python解释器已经提供的函数,拿来直接使用而无需定义。
- 自定义函数
个人根据自己的产品需求,定义好某种函数,以后再进行调用。
3、定义函数
#语法格式化 def 函数名(参数1,参数2,参数3,...): '''注释''' 函数体 return 返回的值
从上面定义的语法格式可以看出,定义函数需要使用def语句,接着是函数名,一般需要能反映出其意义,后面是一对圆括号,函数的参数就放在这里,参数之间以“,”分隔,最后以“:”结束;内部是函数的代码块,需要使用缩进进行编写,包含注释,对函数参数等的说明,接着是函数体,也就是实现功能的代码;最后如果有返回值就是用return语句,可以返回多个主,中间使用“,”分隔,如果没有返回值,return语句可以省略。
def login(username,password): """ :param username: 用户名 :param password: 密码 :return: 返回值 """ user=models.User.objects.filter(username=username,password=password).first() if user: return user
函数在定义阶段只检测语法,不执行代码,也就是说,函数在定义阶段就检查语法是否错误,而逻辑错误只能在执行时抛出。
4、文档注释
python语言支持当行或者多行注释,单行使用“#”,多行使用三个单引号或者双引号将注释内容包裹,但其实还有一种注释方法就是:文档注释
单行和多行注释最后都会被编译器去掉,而文档注释内容却可以获取得到。文档注释需要在函数头(包含def关键字的那一行)的下一行使用单引号或者双引号将注释扩起来即可。然后使用"__doc__"函数属性获取文档注释。
def add(x,y): '计算两个数的和' return x+y print(add.__doc__) #计算两个数的和
5、无返回值的函数
如果需要函数无返回值,不使用return即可;或者使用return语句,但return后面什么也不跟,当然这种情况主要用于从函数的的任意深度跳出函数。
如果return语句后面什么也不跟,打印函数,会输出None
def bar(): return print(bar())#None
二、函数参数
def关键字后面圆括号中的参数称为形参,调用函数时指定的参数称为实参。
1、普通传参
函数形参的位置很重要,实参的传递都是按照形参的定义进行传递的。
def bar(name,content): return "姓名:{},详情:{}".format(name,content) print(bar("bright","你好")) #姓名:bright,详情:你好
- 关键字参数
但是有时候参数过多,未必记住具体的位置,此时可以进行关键字传参,在混合使用时,注意关键字参数必须放到位置参数后面,否则或抛出异常。
def bar(name,content,gender): return "姓名:{},详情:{},性别:{}".format(name,content,gender) print(bar("bright","你好",gender="男")) #姓名:bright,详情:你好,性别:男
- 默认值
有时候在一些情境下,大多数参数需要使用某个固定的值,那么可以为函数的形参指定默认值,如果不指定形参的值,函数就会使用形参的默认值。
def bar(name,content,gender="男"): return "姓名:{},详情:{},性别:{}".format(name,content,gender) print(bar("bright","你好")) #姓名:bright,详情:你好,性别:男
2、动态传参
- 可变参数
def dynamic(*params): print(params) dynamic("helloworld",4,5,6,7,False)#('helloworld', 4, 5, 6, 7, False)
可以看到params前面加一个*表示该参数是一个可变参数,在调用dynamic函数时可以指定任意的、不同类型的多个参数。从输出结果看到,可变参数在函数内部是以元组的形式体现的。
使用可变参数需要考虑形参的位置,通常既有普通参数又有可变参数,可变参数如果放到前面或者中间,后面普通参数需要关键字传参。
def foo(*params,d1,d2): print(params)#('helloworld', 1, 2) foo("helloworld",1,2,d1="gh",d2="mk")
如果可变参数在中间位置的话,并且也不想关键字传参,那么需要指定默认值。
def foo(*params,d1="gh",d2="mk"): print(params)#('helloworld', 1, 2) foo("helloworld",1,2)
- 序列作为函数参数值
函数的参数值可以是任意的数据类型,包括元组、列表、字典等,那么如何将序列的单个元素传入函数呢?
def dynamic(p1,p2): print(p1,p2)#hello world list=['hello','world'] #将序列中的单个元素作为参数传入dynamic,需要在实参前加* dynamic(*list)
上述并没有使用可变参数传参,如果使用可变参数传参的话也是可以实现的
def dynamic(*params): for i in params:#注意params是以元组的形式存在 print(i)#hello world list=['hello','world'] dynamic(*list)
注意:*后加实参,表示传入的值是序列中的单个元素。
上面说的是列表,如果是字典的话,需要有一些改变,那么如何将字典中的单个元素传入函数呢?
def DictDynamic(name,age): print(name,age)#bright 27 user_info={"name":"bright","age":27} #将字典中的单个元素作为参数值,传入实参,前面必须加** DictDynamic(**user_info)
当然也可以通过可变参数:
def DictDynamic(**params): #注意params是一个字典 print(params)#{'name': 'bright', 'age': 27} user_info={"name":"bright","age":27} #将字典中的单个元素作为参数值,传入实参,前面必须加** DictDynamic(**user_info)
在这里,如果在定义函数时,形参未加**,那么在调用该函数时,也不能加两个**。
def DictDynamic(params): print(params)#{'name': 'bright', 'age': 27} user_info={"name":"bright","age":27} #传入字典 DictDynamic(user_info)
三、作用域
作用域是变量、函数、类等的生效范围,直接在模块中顶层定义的变量、函数都属于全局作用域,而在函数的局部定义的作用域属于函数本身的局部作用域,在局部作用域中定义的变量,上一层作用域是不可见的。
x=10 def Foo(): x=20 Foo() print(x)#10
在上面的代码中,全局变量x=10,以及局部变量x=20,这是两个不同的变量,在全局作用域中也只能看见x=10,而在函数的局部作用域中只能看见x=20。
当然局部作用域是可以访问上一层的作用域,除非它本身作用域不存在这个变量。
y=10 #全局变量 def Foo(): print(y)#10 Foo()
可以看到上述y=10在全局作用域中,而在函数内部访问这个变量,因为函数本身没有这个变量,所以它会向上一层查找。
- global
global是全局变量的关键字,如果在局部作用域中声明一个变量为全局变量可以使用这个关键字
NAME = "Bright" def func(): global NAME #声明NAME时全局变量 NAME = "tiny" #修改全局变量 print('hello', NAME)#hello tiny func()
上面的代码通过global实现了局部作用域中修改全局变量的值, 如果函数中有global关键字,变量本质上就是全局的那个变量,可读取可赋值 NAME=“XXX”nonlocal
- nonlocal
nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量
name = "bright" def func(): name = "tiny" def bar(): nonlocal name # nonlocal,指定上一级变量,如果没有就继续往上直到找到为止 name = "sandy" bar() print(name) print(name)#bright func()#sandy
- 函数即变量
先看下面两个实例:
实例一:
def func1(): name="bright" print(name) func2() func1() def func2(): print("我是func2")
实例二:
def func1(): name="bright" print(name) func2() def func2(): print("我是func2") func1()
上面两个实例你觉得哪一个会出错,没错,就是第一个会出错,为什么呢?
在实例一中,内存会把代码进行加载,func1作为变量名,函数题作为变量内容加载到内存,当执行func1时,func2还没加载到内存中去,所以毫无疑问:
NameError: name 'func2' is not defined
函数即变量意思就是,函数名相当于变量名,函数体相当于变量内容,而把每一个函数体加载到内存,标示就是函数名。
四、递归
递归简单的来说就是函数在内部调用自身。因为函数调用是通过栈(stack)这种数据结构实现的,所以,递归调用的次数过多,会导致栈溢出。这也就要求递归必须有一个明确的结束条件。
- 计算阶乘的递归函数:
def multiplication(n): ##终止条件 if n==0 or n==1: return 1 ##递归调用 return n*multiplication(n-1) res=multiplication(10) print(res)
- 斐波拉契数列的递归函数
斐波拉契数列指的是这样的一列数字:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,...
计算第n个值是什么?
def fibonacci(n): if n==1: return 0 elif n==2: return 1 else: return fibonacci(n-1)+fibonacci(n-2) print(fibonacci(5))