Python之旅.第三章.函数.总结
为什么要有函数:
1、组织结构不清晰,可读性差
2、代码冗余
3、管理维护的难度极大,扩展性
函数是什么:
具备某一个功能的工具就是程序中的函数
事先准备工具的过程----》函数的定义
拿来就用----》函数的调用
所以函数的使用必须遵循:先定义,再调用
函数的语法:
def 函数名(参数1,参数2,...):
"""
文档描述
"""
代码1
代码2
代码3
return 值
def:定义函数的关键字
函数名:是用来调用函数的,函数名的命名必须能反映出函数的功能
文档描述:推荐写上,来增强函数的可读性
代码块:函数的功能实现代码
return:函数的返回值
定义函数的三种类型:
有参函数:参数是函数体代码用来接收外部传入值的
无参函数:当函数体的代码逻辑不需要函数的调用者掺入值的情况下,就无参
空函数:函数体为pass
调用函数:
函数的使用必须遵循:先定义,后调用的原则
注意:没事先定义函数而直接调用,就相当于在引用一个不存在的变量名
定义阶段:在定义阶段只检测语法,不执行函数体代码
调用阶段:根据函数名找到函数的内存地址,然后执行函数体代码
函数名加括号即调用函数
调用函数的三种形式:
1、不带参数直接调用
2、带参数调用
3、调用且运算
返回值:
什么时候应该有返回值:
函数体代码运行完毕后需要有一个返回结果给调用者
返回值有三种形式:
1 没有return,返回值None
2 return后跟一个值,返回该值本身
3 return可以逗号分隔,返回多个值,会返回一个元组给调用者
return注意点:
1、return返回值的值,没有类型限制
2、return是函数结束的标志,函数内可以写多个return,但执行一次,函数就立刻结束,并把return后的值作为本次调用的返回值
形参与实参:
形参与实参是什么?
形参(形式参数):指的是
在定义函数时,括号内定义的参数,形参其实就变量名
实参(实际参数):指的是
在调用函数时,括号内传入的值,实参其实就变量的值
注意:
实参值(变量的值)与形参(变量名)的绑定关系只在函数调用时才会生效/绑定
在函数调用结束后就立刻解除绑定
位置参数:
1、位置参数
位置即顺序,位置参参数指的就是按照从左到右的顺序依次定义的参数
2、分两种
2.1 在定义函数时,按照位置定义的形参,称为位置形参
注意:
位置形参的特性是:在调用函数时必须为其传值,而且多一个不行,少一个也不行
2.2 在调用函数时,按照位置定义的实参,称为位置实参
注意:位置实参会与形参一一对应
关键字参数:
1、什么是关键字参数:
在调用函数时,按照key=value的形式定义的实参,称为关键字参数
在调用函数时,按照key=value的形式定义的实参,称为关键字参数
注意:1.相当于指名道姓地为形参传值,意味着即便是不按照顺序定义,仍然能为指定的参数传值
2.在调用函数时,位置实参与关键字实参可以混合使用,但必须
2.1:必须遵循形参的规则
2.2:不能为同一个形参重复传值
2.3 位置实参必须放到关键字实参的前面
默认参数:
默认参数
在定义阶段,已经为某个形参赋值,那么该形参就称为默认参数
注意:
1 定义阶段已经有值,意味着调用阶段可以不传值
2 位置形参必须在默认参数的前面
3 默认参数的值只在定义阶段赋值一次,也就是说默认参数的值再定义阶段就固定死了
4 记住:默认参数的值应该设置为不可变类型
应用:
对于经常需要变化的值,需要将对应的形参定义成位置形参
对于大多数情况值都一样的情况,需要将对应的形参定义成默认形参
可变参数:
什么是可变长度参数:
可变长度指的参数的个数可以不固定,实参有按位置定义的实参和按关键字定义的实参,
所以可变长的实参指的就是按照这两种形式定义的实参个数可以不固定,
然而实参终究是要给形参传值的
所以形参必须有两种对应的解决方案来分别处理以上两种形式可变长度的实参;
形参:
*
**
形参里包含*与**
*会将溢出的位置实参全部接收,然后保存成元组的形式赋值给args
**会将溢出的关键字实参全部接收,然后保存成字典的形式赋值给kwargs
实参里包含*与**
一旦碰到实参加*,就把该实参的值打散
一旦碰到实参加**,就把该实参的值打散
函数内调用别的函数,实参要根据被调用函数的形参规则进行输入
命名关键字参数:
什么是命名关键字参数?
格式:在*后面参数都是命名关键字参数
特点:
1 必须被传值
1 约束函数的调用者必须按照key=value的形式传值
2 约束函数的调用者必须用我们指定的key名
def foo(x,y,*,z): #创建foo函数,z为命名关键字参数
print(x,y,z)
#foo(1,2,aaa=3) #报错,z为命名关键字参数,只能用用关键字z=值
foo(1,2,z=3)
函数的嵌套:
函数的嵌套调用:在函数内又调用了其他函数
函数的嵌套定义:在函数内又定义其他函数
命名空间与作用域
名称空间:存放名字与值绑定关系的地方
名称空间分为三类
1 内置名称空间:存放Python解释器自带的名字,在解释器启动时就生效,解释器关闭则失效
2、全局名称空间:文件级别的名字,在执行文件的时候生效,在文件结束或者在文件执行期间被删除则失效
3、局部名称空间:存放函数内定义的名字(函数的参数以及函数内的名字都存放与局部名称空间),
在函数调用时临时生效,函数结束则失效
加载顺序:内置名称空间-》全局名称空间-》局部名称空间
查找名字:局部名称空间-》全局名称空间-》内置名称空间
作用域
全局作用域:包含的是内置名称空间与全局名称空间的名字,
特点
1在任何位置都能够访问的到
2该范围内的名字会伴随程序整个生命周期
局部作用域:包含的是局部名称空间的名字
特点:
1、只能在函数内使用
2、调用函数时生效,调用结束失效
函数对象:
1.可以被引用
2.可以当参数传入
3.可以当函数的返回值
4.可以当容器类型的元素
闭包函数:
1.定义在函数内部的函数
2.并且该函数包含对外部函数作用域中名字的引用,该函数就被称为闭包函数
def outter(): #定义函数outter,并定义变量name
name='egon'
def inner():
print('my name is %s' %name) #定义函数inner,输出name
return inner #返回函数inner
f=outter() #把嵌套定义函数inner赋值给f
无参装饰器
1 开放封闭原则
软件一旦上线后,就应该遵循开放封闭原则,即对修改源代码是封闭的,对功能的扩展是开放的
也就是说我们必须找到一种解决方案:
能够在不修改一个功能源代码以及调用方式的前提下,为其加上新功能
总结,
原则如下:
1、不修改源代码
2、不修改调用方式
目的:
在遵循1和2原则的基础上扩展新功能
2、什么是装饰器?
器指的工具,装饰指的是为被装饰对象添加新功能
完整含义:
装饰器即在不修改被装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能
装饰器与被装饰的对象均可以是任意可调用的对象
装饰器=》函数
被装饰的对象=》函数
#无参装饰器模板
def outer(func):
def inner(*args,**kwargs):
res=func(*args,**kwargs)
return res
return inner
装饰器魔法糖
关键字@,@后面跟装饰器函数名,正下面为需要用到装饰器的函数,@必须单独一行
有参装饰器
在装饰器上再加一层包装,也就是再套一套函数,
魔法糖调用时,@最外层函数(参数),@带参数最外层函数
迭代器
1、 迭代是一个重复的过程,并且每次重复都是基于上一次的结果而来 #仅仅循环,不是迭代
2、 可迭代对象:在python中,但凡内置有__iter__方法的对象,都是可迭代的对象
3、 迭代器:迭代取值工具,可迭代的对象执行__iter__方法得到的返回值就是迭代器对象
4、 可迭代的对象vs迭代器对象
可迭代的对象:str,list,tuple,dict,set,file(文件本身就是迭代器)
a、获取可迭代对象的方式:无须获取,python内置str,list,tuple,dict,set,file都是可迭代对象
b、特点:
内置有__iter__方法的都叫可迭代的对象,执行该方法会拿到一个迭代器对象
迭代器对象:文件对象本身就是迭代器对象 #迭代器对象一定是可迭代对象
a、获取迭代器对象的方式:
执行可迭代对象的__iter__方法,拿到的返回值就是迭代器对象
b、特点:
内置有__next__方法,执行该方法会拿到迭代器对象中的一个值
内置有__iter__方法,执行该方法会拿到迭代器本身
5、 迭代器的优缺点分析
a. 迭代器提供了一种可不依赖于索引的取值方式(优点)
#字典无序,没有办法通过索引取值,可通过迭代器取值
b. 迭代器更加节省内存(优点)#迭代器本身是一个内存地址
#range在python2中是一个列表,在python3中是一个迭代器
c. 取值麻烦,只能一个一个取,只能往后取(缺点)
d. 并且是一次性的,无法用len获取长度(缺点)
6、 for循环原理分析
a、for 循环称之为迭代器循环,in后跟的必须是可迭代的对象 #while 循环称为条件循环
b、for循环会执行in后对象的__iter__方法,拿到迭代器对象
c、然后调用迭代器对象的__next__方法,拿到一个返回值赋值给line,执行一次循环体
d、周而复始,直到取值完毕,for循环会检测到异常自动结束循环
生成器:函数内包含有yield关键字,再调用函数,就不会执行函数体代码,拿到的返回值就是一个生成器对象
1、iter_obj=obj.__iter__(),拿到迭代器
2、触发iter_obj.__next__(),拿到该方法的返回值,赋值给item
3、周而复始,直到函数内不在有yield,即取值完毕
4、for会检测到StopIteration异常,结束循环
总结yield:
1、为我们提供了一种自定义迭代器的方式,可以在函数内用yield关键字,调用函数拿到的结果就是一个生成器,生成器就是迭代器
2、yield可以像return一样用于返回值,区别是return只能返回一次值,而yield可返回多次。因为yield可以保存函数执行的状态
面向过程的编程思想
1 面向过程的编程思想
核心是'过程'二字,过程即解决问题的步骤,即先干什么,再干什么。。。。
基于面向过程编写程序就好比在设计一条流水线,是一种机械式的思维方式。
2、总结优缺点:
优点:复杂的问题流程化,进而简单化
缺点:修改一个阶段,其他阶段都有可能需要做出修改,牵一发而动全身,即扩展性极差
应用:用于扩展性要求低的场景
三元表达式仅应用于:
1、条件成立返回 一个值
2、条件不成立返回 一个值
语法:
return 成立得到的值 if 表达式 else 不成立得到值
函数递归
函数递归:函数的递归调用,即在函数调用的过程中,又直接或间接地调用了函数本身
递归分为两个阶段
1、回溯:
注意:一定要在满足某种条件结束回溯,否则的无限递归
2、递推
总结:
1、递归一定要有一个明确地结束条件
2、没进入下一次递归,问题的规模都应该减少
3、在python中没有尾递归优化
三、匿名函数
普通定义函数
def foo(x,n): #foo=函数的内存地址
return x ** n
无函数名定义函数
lambda
=lambda x,n:x ** n
强调:
1 匿名的目的就是要没有名字,给匿名函数赋给一个名字是没有意义的
2 匿名函数的参数规则、作用域关系与有名函数是一样的
3 匿名函数的函数体通常应该是 一个表达式,该表达式必须要有一个返回值
列表生成式
定义:
#值 for i in 可迭代数据 if 判断表达式
[expression for item1 in iterable1 if condition1]
生成器表达式
定义:#值 for i in 可迭代数据 if 判断表达式
(expression for item1 in iterable1 if condition1)
常用的内置函数
# a=bytes(3) #b'\x00\x00\x00'
# b=bytes('a'.encode('utf-8')) #'a'
# c=bytes([1,2,3]) #b'\x01\x02\x03'
# print(a,b,c)
# bytes()
# 如果 source 为整数,则返回一个长度为 source 的初始化数组;
# 如果 source 为字符串,则按照指定的 encoding 将字符串转换为字节序列;
# 如果 source 为可迭代类型,则元素必须为[0 ,255] 中的整数;
#——————————————————————————————————————————————————————————————————————————
# print(chr(88)) #输出为X
# chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。
#——————————————————————————————————————————————————————————————————————————
# print(divmod(5,3)) #输出为(1,2)
# python divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。
#——————————————————————————————————————————————————————————————————————————
# a=['asdf','dshgsd','sfdsjlkf','sdfjlksd']
# for i in enumerate(a):
# print(i)
# 输出结果
# (0, 'asdf')
# (1, 'dshgsd')
# (2, 'sfdsjlkf')
# (3, 'sdfjlksd')
# enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
#___________________________________________________________________________
# print(eval('9*9'))#输出为81
# print(eval("{'a':1,'b':2}")) #输出结果为{'a': 1, 'b': 2},可以用于直接把字符串内的列表字典之类直接取出
# eval() 函数用来执行一个字符串表达式,并返回表达式的值。
#——————————————————————————————————————————————————————————————————————————————
# a=[88,222,3434,2,4343,5,32,292]
# print(list(filter(lambda x: True if x>100 else False ,a)))
# filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
#该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
#-------------------------------------------------------------------------------
# a=[1,2,4]
# b=iter(a)
# print(b)
# print(b.__next__())
#iter() 函数用来生成迭代器。把列表变成迭代器;
#——————————————————————————————————————————————————————————————————————————————————
# a=map(lambda x:x**2,[1,2,3,4])
# print(list(a))
# #输出结果为
# [1, 4, 9, 16]
#map() 会根据提供的函数对指定序列做映射。
#第一个参数 为函数,第二个参数为序列,得到的值是个迭代器
#——————————————————————————————————————————————————————————————————————————————————
# a=[3,545,2,645,46,345,46754,2]
# b=sorted(a)
# print(b)
# #输出结果为
# [2, 2, 3, 46, 345, 545, 645, 46754]
#sorted() 函数对所有可迭代的对象进行排序操作。排序,把对象从小到大的排序