函数
一.函数基础
1.什么是函数?
在程序中,函数就是具备某一功能的工具,事先将工具准备好就是函数的定义,遇到应用场景拿来就用就是函数的调用。
2.为什么使用函数
- 程序冗长
- 程序的扩展性差
- 程序的可读性差
3.如何使用函数
先定义后调用
- 定义阶段:本来报错的东西不会报错,只检查语法不执行函数代码
- 调用阶段:执行函数代码
二.定义函数的三种形式
第一种定义方式,无参函数
第二种方式:有参函数,有几个参数,就必须传入几个参数
第三种:空函数,函数里写入pass
三.函数的返回值
定义了函数,需要一个函数的返回值,也就是函数内部代码经过一些列逻辑处理获得的结果。类似于变量名用来接收变量值,函数名用来接收函数的返回值。
需要注意的是:
- return是一个函数结束的标志,函数内可以有多个return,只要执行到return,函数就会停止。
- return的返回值可以返回任意数据类型
- return的返回值无个数限制,即可以使用逗号隔开返回多个值
- 0个:返回None
- 1个:返回值是该值本身
- 多个:返回值是元组
四.函数参数的应用
1.形参和实参
- 在函数定义阶段括号内定义的参数,称之为形式参数,简称形参,本质就是变量名。
- 在函数调用阶段括号内传入的参数,称之为实际参数,简称实参,本质就是变量的值。
2.位置参数
-
位置形参:在函数定义阶段,按照从左到右的顺序依次定义的形参,称之为位置形参。
特点:按照位置定义的形参,都必须被传值,多一个不行,少一个也不行。 -
位置实参:在函数调用阶段,按照从左到右的顺序依次定义的实参,称之为位置实参。
特点:按照位置为对应的形参依次传值。
3.默认形参
在定义阶段,就已经被赋值。在调用时可以不用为其赋值。
注意:
- 默认形参必须放在位置形参的后面。
- 默认形参的值只在定义阶段赋值一次,也就是说默认参数的值在函数定义阶段就已经固定了。
4.关键字参数
在调用函数时,按照key=value的形式为指定的参数传值,称为关键字实参。
注意:
- 可以混用位置实参和关键字实参,但是位置实参必须在关键字实参的左边。
- 可以混用位置实参和关键字实参,但不能对一个形参重复赋值。
5.可变长参数
- 可变长形参:
- *args接受了所有位置实参,然后以元祖的形式保存下来,只接受位置参数,不接受关键字实参
- **kwargs接收所有的关键字实参,然后以字典的形式保存下来
- 可变长实参:
- *把这个列表内的元素一个一个取出来,然后一个一个传给这个函数
- **会把这个字典打散成键值对的形式作为实参传给函数
最后总结一句:Python中一切皆对象,不管字符串,列表,字典等数据类型在python中都是对象。
五.函数对象
python中一切皆对象,函数是第一类对象,即函数可以被当做数据处理。
1、不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不须等该函数执行完成
2、带括号(参数或者无参),调用的是函数的执行结果,须等该函数执行完成的结果
函数对象的四大功能:
- 引用,赋值:可以直接打印函数
- 可以当作参数传给一个函数
- 可以当作函数的返回值
- 可以当作容器类型的元素,比如将函数放在列表中,使用时取出来
六.函数的嵌套
在函数f1内部定义的函数f2,则无法在函数f1外部使用内部定义的函数f2。
七.名称空间和作用域
1.名称空间
名称空间(name spaces):在内存管理里,我们曾说到变量的创建其实就是在内存中开辟了一个新的空间。但是我们一直不清楚变量名的存储位置,其实在内存中有一块内存存储变量名与变量间的绑定关系的空间,而这个空间称为名称空间。
- 内置名称空间
- 内置名称空间:存放Pyhton解释器自带的名字,如
int、float、len
- 生命周期:在解释器启动时生效,在解释器关闭时失效
- 内置名称空间:存放Pyhton解释器自带的名字,如
- 全局名称空间
- 全局名称空间:除了内置和局部的名字之外,其余都存放在全局名称空间,如下面代码中的
x、func、l、z
- 生命周期:在文件执行时生效,在文件执行结束后失效
- 全局名称空间:除了内置和局部的名字之外,其余都存放在全局名称空间,如下面代码中的
- 局部名称空间
- 局部名称空间:用于存放函数调用期间函数体产生的名字,如下面代码的
f2
- 生命周期:在文件执行时函数调用期间时生效,在函数执行结束后失效
- 局部名称空间:用于存放函数调用期间函数体产生的名字,如下面代码的
三个名称空间的加载顺序为:内置--》全局--》局部。因为在Python解释器中的内置名称空间加载结束后,文件才开始打开,这个时候才会产生全局名称空间,如果文件内有某一个函数被调用的时候,才会开始产生局部名称空间。
由于名称空间是用来存放变量名与值之间的绑定关系的,所以但凡要查找名字,一定是从三者之一找到,查找顺序为:从当前的所在位置开始查找,如果当前所在的位置为局部名称空间,则查找顺序为:局部--》全局--》内置。
2.作用域
作用域即作用的区域,通常分为全局作用域和局部作用域。
- 全局作用域:全局有效,全局存活,包含内置名称空间和全局名称空间。
- 局部作用域:局部有效,临时存储,只包含局部名称空间。
- 需要注意的是:作用域关系在函数定义阶段就固定死了,与函数的调用无关。
global关键字可以将局部作用域的变量修改为全局作用域中的变量。
nonlocal关键字可以将内部函数里的变量修改为外部函数里的变量,不能修改全局的变量。
八.闭包函数
闭包是指由函数及其相关的作用域组合而成的实体,函数可以作为另一个函数的参数或返回值,可以赋给一个变量。函数可以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题。
所以可以总结为在闭包函数中,内部函数是外部函数的内嵌函数,并且是外部函数的返回值。
闭包函数打破了层级关系,可以把局部变量拿到全局使用,并且可以把外部的变量封装到内部函数中,然后下次直接调用内部函数就行了。
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
应用领域:延迟计算(原来我们是传参,现在我们是包起来)、爬虫领域。
九.装饰器
什么是装饰器?
程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰器对象添加额外功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外的功能。
为什么使用装饰器?
因为软件的维护应该遵循开放封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能指的是开放的。
装饰器的实现必须遵循两大原则:
- 不修改被装饰对象的源代码
- 不修改被装饰对象的调用方式
- 装饰器其实就是在遵循以上两个原则的前提下为被装饰对象添加新功能。
怎样使用装饰器?
若只使用一个函数来实现扩展功能,则会改变调用方式,与装饰器原则违背。
所以我们可以使用闭包的思想,将需要添加的功能在闭包函数的内部函数实现,然后将内部函数还原到闭包函数的外部,把内部函数赋值给源函数名,则可以实现功能的添加,也可以实现调用方式的不变,注意在内部函数里返回值与源函数返回值保持一致。
装饰器模板
def deco(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
在被装饰函数正上方,并且是单独一行写上@装饰器名
十.迭代器
迭代器:迭代的工具。
可迭代对象
python中一切皆对象,对于这一切的对象中,但凡有__iter__
方法的对象,都是可迭代对象。
可迭代的对象:Python内置str、list、tuple、dict、set、file都是可迭代对象。
迭代器对象
执行可迭代对象的__iter__
方法,拿到的返回值也是一个可迭代对象,并且还会会有一个__next__
方法。
__next__
可以遍历可迭代对象所有的元素,一旦遍历完在使用会报错
不但拥有__iter__
方法,此可迭代对象还拥有__next__
方法的才是迭代器对象,而文件本身就是迭代器对象
for循环称为迭代器循环,in后必须是可迭代的对象。Python3的for循环本质上就是通过不断使用__next__
方法实现的
十一.三元表达式,列表推导式
三元表达式:条件,条件成立,条件不成立。
列表推导式:列表生成式时,把要生成的元素放到前面,后面跟for
循环
lis=[i for i in range(10)]
会生成元素从0到9的列表。
十二.字典生成式
dic={key:value for key,value in lis}
zip()方法:通过解压缩函数生成一个字典
十三.生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
yield可以理解为return,返回后面的值给调用者。不同的是return返回后,函数会释放,而生成器则不会。在直接调用next方法或用for语句进行下一次迭代时,生成器会从yield下一句开始执行,直至遇到下一个yield。
十四.递归
函数的递归调用:它是一种特殊的嵌套调用,但是它在调用一个函数的过程中,又直接或间接地调用了它自身。函数调用函数自己,类似于循环,但是这个循环必须得有结束条件
递归的精髓在于通过不断地重复逼近一个最终的结果。
十五.二分查找
从一个按照从小到大排列的数字列表中找到指定的数字,遍历的效率太低,用二分法可以极大低缩小问题规模,也就是每次都选取中间的值作比较,则每次都可以淘汰一半的数字,这里可以通过递归和while循环实现,比如对于0到10亿数字的排列找一个数字,最多只需要32步。
十六.匿名函数
匿名就是没有名字,用关键字lambda表示,但是匿名意味着引用计数为0,使用一次就释放。
有名函数:循环使用,保存了名字,通过名字就可以重复引用函数功能
匿名函数:一次性使用,随时随时定
匿名函数一般不单独使用,通常与max,min,sorted,map,reduce,filter这些内置函数一起使用。
十七.内置函数
之前常用的大概有二十多个内置函数,比如int(), float(), list(), open(), print(), range(), len()
还有些常用的比如:
- enumerate() 获取索引和值:比如一个列表对其使用enumerate然后循环取值,可以将对应列表中元素的索引和其元素组成的元组的形式打印出来
- eval() 去掉字符串的引号,然后他是什么数据类型就可以得到什么数据类型
- abs() 取绝对值
- dir() 把模块所有的方法读出来
- sum()求和
- max()/min() 取最大和取最小
- sorted() 排序
- map() 映射
- filter() 过滤
十八.面向过程编程
按照一定的顺序,顺序中的每一步都可以看成函数,这个函数的输入是上一个函数的输出,这就叫面向过程编程,面向过程是一种编程思路、思想。
-
优点:a.复杂的问题流程化,进而简单化,使得逻辑清晰,简单明了
b.每个函数可以独立的写出来
-
缺点:a.相互之间会有一定的联系,上一步中断了,那下一步也会中断
b.若有一个功能改变了,其他的后面所有功能也要跟着修改
c.可扩展性差