函数
OK,终于在学习python的路上又迈出了一步,话不多说,开始冲刺吧。
函数的作用
先讲一下函数的功能,理解一下为什么要用函数。当我们在写代码的时候,难免要写很多重复的代码。用复制粘贴吧,看这个写的代码,自己内心深处难免会觉得Low.但是不写吧 ,功能又实现不了,那我们怎么办呢?这个时候,函数就有用武之地了。函数最主要的好处就是减少重复代码。在定义之后,只需要在需要用到的地方进行调用就好了。最大程度地减少了代码的重复率,还显得有B格。
函数的基础语法
万丈高楼平地起,老套路了,说一下函数的语法表现。
def 函数名(参数):
代码功能
return 值
函数在用def关键字来定义一个函数,在函数名后的括号里可以传递各种各样的参数。
函数在遇到return后,无论return后有什么代码,函数都会终止运行,并返回return后的值,当函数里没有return时,默认返回None。
函数内变量的作用域
变量分为全局变量(L)和局部变量(G)。
- 全局变量就是程序在刚开始运行的时候就定义好的量,一直到程序结束。作用域为整个程序。
- 局部变量是在函数内部定义的变量,当函数运行的时候生效,函数运行结束的时候变量就不在生效,作用域为函数内部。
当在函数内部出现变量的使用时,会先在内部变量里查找,然后才是去找全局变量。举个栗子,如果全局变量和局部变量都有name,在函数内部调用name时会优先使用局部变量的name。
name = "Jerry"
def test():
name = "Tom"
print("%s run" % name)
test()
# 输出结果为 Tom run
def test():
global name
name = "Tom"
print("%s run" % name)
test()
print(name)
# 此时输出的结果为 Tom run 和 Tom
global的作用是在函数内部声明一个全局变量,将一个局部变量变成全局变量。但是不推荐使用,对于程序的维护会造成困扰。
如果全局变量是一个列表、集合、字典等可变的数据类型,在函数内部对它进行操作会怎么样呢??答案是全局变量会跟着改变,至于为什么会产生这种变化,就要从变量的赋值上看了,在此不做过多说明。
函数的参数
函数的参数主要有实参、形参、关键字参数、位置参数、默认参数、非固定参数。
在函数定义的时候,函数名后面的括号里是定义形参、默认参数和非固定参数的,实参和关键字参数则是在函数调用时给函数传递的值。
def test(name,age=22,*args,**kwargs):
print(name,age,args,kwargs)
test("Tom",sex="M")
- 形参就是函数定义中的参数,如上述的name,必须传入实参。
- 默认参数是在函数定义的时候已经给与默认值,如age= 22。
- 实参,在函数调用的时候,传入的具有实际意义的参数,如“Tom”。
- 关键字参数,在函数调用的时候,利用赋值语句传入的参数:sex=“M”。
- 非固定参数,移动有两种,当在定义函数时候不知道这个函数将会输入多少个参数,就可以传入非固定参数,一般用*args,**kwargs表示,前者是一个元祖,输入的如果是单个变量则会添加到args。如果传入的是关键字参数,则会添加到kwargs字典中。注意,再函数内部使用时,不用加星号。
注意:在参数的使用上需要注意,定义函数时,是用形参、默认参数、非固定参数的顺序,传入参数时,关键字参数要在位置参数(按照位置赋值的参数)之后。
函数的类别
嵌套函数
在函数体的内部在定义一个函数,称为嵌套函数。
匿名函数
顾名思义,匿名函数就是没有定义函数名的函数,使用lambda关键字来定义的。最多只能进行简单的三元运算。语法格式如下。
sum_two = lambda x,y:x+y
print(sun_two(2,3))
高阶函数
这也很容易理解,高阶函数就是相当于把一个或多个函数作为参数接收,或者返回一个函数。
递归函数
如果函数在内部在调用自己呢?会发生什么现象?很明显,会无止境地进行调用,系统会报错,但是如果加一个判断条件呢?在满足条件以后就不进行调用了,这就是递归函数。部分算法会使用到。
内置函数
内置函数就是再安装的时候已经定义好了,可以直接使用的函数。不做多讲,自行领悟。
函数的闭包现象
正常情况下函数在运行完之后,该函数在内存中都会被释放。但是如果是这种情况呢?
def ex():
name = "Tom"
def inner():
print("hello,%s" % name)
return inner
mes = ex()
mes()
此时的mes是inner的内存地址,mes()就相当于调用了inner()的函数,当执行mes()的时候,需要调用ex里、inner外的变量,参数等,这种现象就成为闭包。
列表生成式
这是一个逼格很高的style。可以只使用一行实现多行代码的功能。这用到了匿名函数。
a = [1,2,3,4,5,6,7]
list1 = [i**2 for i in a]
很简单的一行代码就实现了让 列表a里各个元素进行了平方计算。结果生成了一个新的列表。
装饰器
如果对于一个已经实现运行的程序,在不改用调用方式、不改变函数原本代码,怎么实现新增功能呢?我是想破脑袋也没想到,最后揭秘才知道,可以通过装饰器来实现,这是一个很复杂的功能,运用了高阶函数、嵌套函数,还有闭包现象。
在定义外部函数时,需要接受一个函数名func1作为参数,在外部函数执行完毕后,返回内部定义函数名,在内部函数实现一些功能之后会执行func1()。
装饰器的使用是在需要装饰的函数头上利用@装饰器函数名来实现。
生成器(generator)
生成器在我看来相当于一个惰性运算,当把要计算的数和算法传入生成器之后,生成器不要我们来进行下一步运算的指令它才会继续计算,否则就不会计算。
当我们把列表生成式的[]改为()就是一个生成器,当然,只能进行简单的运算。对于生成器的运算可以使用next()让生成器进行下一步运算,也可以使用for循环进行遍历操作。
还有一种就是函数生成器。当对运算比较复杂时,就可以使用函数生成器,在函数内部使用yield关键字,函数就是一个生成器,当函数运行到yield时会停止继续运行下去,跳出函数,直到在外界使用函数生成器时,有两种方法进行下一步运算。
- next()
- send()。但是send()使用时要注意需要使用__next__()向生成器传送一个空值,唤醒生成器。然后利用send()向yield后的变量赋值。
作用是可以将函数内部的值在每一次yield的时候返回到外部程序中使用。
迭代器(iterator)
首先说一下可迭代对象,凡是可以用for循环遍历的都是可迭代对象(iterable)。可以使用next()进行下一步运算的是迭代器(iterator),可迭代对象的数据类型可以用iter()来变成迭代器,可以用next()方法。