Python Note -- Day 4. 模块化编程-函数,递归函数和高阶函数-回调函数,闭包函数,匿名函数,迭代器,生成器

模块化编程

指把程序进行封装(函数封装,面向对象,文件...)

10. 函数

  • 定义
    def 函数名([参数列表]):
    当前函数的具体功能代码...
  • 调用
    函数名([参数列表]):

系统将函数调用的参数和局部变量存放在内存的哪一部分:stack

def func(x):
    if not instance(x,(int,float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

10.1 函数的参数类型

普通参数,默认参数,收集参数,命名关键字参数

  • 普通参数

就是位置参数,也叫顺序参数,也是必须传递的参数 ——可把普通函数的普通参数按照关键字参数进行传递

def a(age, name):  
    print(age)  
    print(name)  
a(name='dsgs',age=3)
  • 默认参数

比如要求有三个参数,x,y,i,至少要传递两个
这是最后一个参数就是默认参数,但是默认参数,需要在定义形参时,指定默认值
def func(x,y,i=100):
print(x,y,i)
调用传递了默认的参数,那么里面使用的就是传递的实参
func(1,2,3)
调用没有传递默认的参数,使用默认值
func(1,2)

默认参数必须指向不变对象!

def add_end(L=[]):
    L.append('END')
    return L

# 修改后
def add_end(L=None):
    if L is None:
        L = []
    L.append('END')
    return L

>>> add_end()
>>> add_end()
  • 收集参数

——定义一个形参,专门收集多余的实参,又叫 可变参数
——或者理解为,不确定要传递多少个实参,直接用一个形参来接收
——定义函数时,在形参前面加一个 * 号,如:*args,*m
——接收的多余参数,会存储在args/m中,形成一个元组

def func(a,x="+",*args):  
    if x == "+":  
        print('add',args)  
    else:  
        print('minus',args)  
func(1.2,"-",2,3); minus (2, 3)
  • 命名关键字参数

——关键字参数定义在 收集参数 后面
——关键字参数必须通过形参的名字来进行传递

def lo(a,b,c=3,*args,name):  
    print(a,b,c)  
    print(args)  
    print(name)
lo(1,2,4,5,6,7,name="admin")  
  • 关键字参数收集

——普通收集参数,会把多余的参数收集为 元组
——#关键字参数收集,会把多余的关键字参数收集为 字典

def lo(a,b,c=3,*args,name,age,**kw):  
    print(a,b,c)  
    print(args)  
    print(kw)  
lo(1,2,6,112,1133,name='lim',age=44,sex='fel', wo='ha')  
  • 注意形参声明的位置:
  • 普通参数,默认参数,收集参数,关键字参数,关键字收集参数!!
  • print(a=11,b=22,c=4,55,name='hi',age=20,sex=1) 这样是错误的!!
  • 如果单独出现星号 *,则星号 * 后的参数必须用关键字传入:
    def one(a,b,*,c):
        return a+b+c
    
    one(1,2,3) #error
    one(1,2,c=3)

* 强制位置参数

Python3.8 新增了一个函数形参语法 / 用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式。

# 形参 a 和 b 必须使用指定位置参数,
# c 或 d 可以是位置形参或关键字形参,
# 而 e 和 f 要求为关键字形参:
def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

f(10, 20, 30, d=40, e=50, f=60)

f(10, b=20, c=30, d=40, e=50, f=60)   # b 不能使用关键字参数的形式
f(10, 20, 30, 40, 50, f=60)           # e 必须使用关键字参数的形式

10.2 函数返回值

调用带有返回值的函数时,函数中的返回值会返回到函数调用处
——函数可使用return进行数据的返回
——return可返回任意内容或数据
——return意味着函数的结束,return之后代码不再执行
——若没有使用return或return之后无内容,默认返回None

def lo(a,b):  
    res = f'{a},i love {b}'  
    ##print(11)
    return res  
lo("a",'bb');并不会输出结果  
print(lo("any",'banana')) 
r=lo('a','b')  
print(r)

def one(a,b):
    print('niceeee')
    return 

练习:设计函数可接收一个或多个数并计算乘积:

def func(x,*y):
    for i in y:
        print(i)
        x = x * i
    return x

def f(): pass

print(type(f()))

以上代码输出结果为?

<class 'function'>
<class 'tuple'>
<class 'NoneType'>
<class 'str'>
<class 'type'>

## type() 的参数是函数的返回值,该返回值为 None

10.3 变量的作用域

++ 函数内可以获取函数外部的变量-全局变量, 但不能更改
++ 函数内定义的变量-局部变量,函数外不能使用

++ 变量分两种:

  • 可变数据类型:函数外定义的变量,函数内可以使用:列表和字典
  • 不可变数据类型:函数外定义的,函数内只能访问,不能更改

++函数内部使用global关键字定义的变量就是全局变量
++函数外定义的变量,在函数内使用global关键字进行声明,也是全局变量

  • globals():获取全局数据,在任意位置都能获取
  • locals():只获取当前作用域的数据
  • nonlocal关键字
    变量和函数都有作用域
    在内函数中如何使用上一层函数中的局部变量
def outer():  
    n = 10  
    def inner():  
        #nonlocal n  
        n += 1  
        print(n)  
    inner()  
outer()  

//inner()错误的

  • 魔术变量
    __name__ 当前脚本若作为主程序,那么值是__main__,若是当做一个模块,在另一个脚本中引用,那么值是当前文件档
    __doc__   当前脚本的文档说明,在当前脚本中第一个 三引号注释,就是当前脚本的说明文档
    函数的说明文档
  • 函数中第一个三引号注释 def outer():
    '''
    这里就是写当前函数的文档说明的
    需要说明当前函数的作用
    若还有形参,也要一一说明
    name:
    age:表示当前的一个年龄
    :return:
    '''
    n = 10
    ——print(outer.__doc__);获取当前函数的说明文档

函数封装的练习题

  • 打印九九乘法表,矩形,十二生肖

——九九,正方向

def NineM(n=0):  
    '''  
    九九乘法表  
    n=0时输出正向,默认的,1为反向  
    :param n:  
    :return:  
    '''  
    if n = 0:  
        rs = range(1,10)
    else:  
        rs = range(9,0,-1)  
    for a in rs:  
        for b in range(1, a+1):  
            print(f'{a}x{b}={a*b}', end=' ')  
        print()  
NineM()

——矩形

def sq(n,x,y):  
    print('# '*x)  
    for t in range(1,y-1):  
        if n == 1:  
            print('# '*x)  
        elif n == 0:  
            print('# ',' '*2*(x-3),'#')  
            # print(f'# {bb}#')  
            # bb=' '*2*(x-3)  
    print('# '*x)  
sq(0,3,4)  
练习题:
def greetPerson(*name):
    print('Hello', name)
greetPerson('Runoob', 'Google')
以上代码输出结果为?
Hello Runoob
Hello Google
Hello ('Runoob', 'Google')
Hello Runoob
错误!函数只能接收一个参数。

加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。third

10.4 递归函数

  • 定义了一个函数,函数内自己调用自己
  • 递归函数必须要有结束,否则会一直调用下去,直到栈溢出
  • 递归函数是一层一层的调用,再一层一层的返回

就像坐在最后一排想要第一排妹子的电话,一个一个拍过去问,直到拍到第一排要到电话,最后一层层返回

def di(n):  
    print(n)  
    if n > 0:  
        di(n-1)  
    print(n)  
di(3) 

di(3)==> 3  
 di(3-1)==> 2  
  di(2-1)==> 1  
   di(1-1)==> 0  
   di(0)==> 0  
  di(1)==> 1  
 di(2)==> 2  
di(3)==> 3

练习题-斐波那契数列

def feibo(x):  
    if x == 1 or x == 2:  
        return 1  
    else:  
        print(x-1,x-2)
        s = feibo(x - 1) + feibo(x - 2)  
        return s  
print(feibo(1))  
print(feibo(6))

练习题-阶乘

def jie(n):  
    if n == 1:  
        return 1  
    else:  
        return n*jie(n-1)  
s = jie(9)  
print(s)  
  • 递归函数效率并不高,能不用就不用
  • 一个函数调用后若没有结束,在栈空间就一直存在,直到函数运算结束才销毁

练习:
汉诺塔问题 : 由一根针上移到另一根针上,并且始终保持上小下大的顺序。
编写move(n, a, b, c)函数,它接收参数n,表示3个柱子A、B、C中第1个柱子A的盘子数量,然后打印出把所有盘子从A借助B移动到C的方法

# tip:每次分成n-1和1两份
def move(n, a, b, c):
    if n == 1:
        print('move', a, '-->', c)
    else:
        move(n-1, a, c, b) #游戏要求移动过程中必须大盘在下,要把N-1个盘移动到b柱上必须得借助c柱,不可能一下子搬过去
        move(1, a, b, c)   #直接从a上把最大盘移动到c柱上了
        move(n-1, b, a, c) #把b柱上N-1个盘再通过a柱,一个一个的移动到c柱上

move(4, 'A', 'B', 'C')

10.5 高阶函数

回调函数,闭包函数,匿名函数,迭代器,生成器

10.5.1 回调函数

函数调用的参数为函数

//如果将函数指针作为参数被传递到函数中,当这个函数指针被用于调用其指向的函数时,我们就称这个函数为回调函数

回调函数的主要优势是解耦,即调用者和被调用者被分开,调用者无需了解谁是被调用者,它只需知道如果满足某条件之后,函数将被调用

def func(x, y, f):  
    '''  
    当前函数接受两个数值,并把这两个数值传递给第三个参数进行运算  
    x,y, int  ; f, function  
    :param x:  
    :param y:  
    :param f:  
    :return:  
    '''  
    #print(f([x,y]))  
    print(f(x, y))  
func(1,2,sum) #sum计算一个容器
func(2,2,pow) 幂函数

10.5.2 闭包函数

函数内返回一个内函数,且内函数中使用了外函数的局部变量
特点

  1. 在外函数中定义局部变量,内函数总是用了这个局部变量
  2. 外函数中返回内函数,返回的内函数就是闭包函数
  3. 主要在于保护了外函数中的局部变量,既可以被使用,又不会被破坏
def person():
    money = 0
    def work():
        nonlocal money
        money += 100
        print(money)
    #在外函数中返回内函数,闭包
    return work
res = person()
res() # = work()
res()
res()
  • work()中是print不是return!!
  • 此时不能在全局中对money进行任何此操作!
    如何检测是否为闭包函数:
    函数名.__closure__如果是闭包函数返回cell
    print(res.closure)

10.5.3 匿名函数 lambda表达式

可以不使用def定义,且函数没有名字;
注意:lambda表达式仅仅是一个一行代码,不是代码块,又称为一行代码的函数;
不能访问除了自己形参外的数据包含全局变量

lambda [arg1 [,arg2,.....argn]]:expression
#lambda 参数列表:返回值
def add(x, y):
    return x+y
print(add(3,4))
res = lambda a,b:a+b
print(res(4,4))
# lambda是一个表达式,不能写太复杂的逻辑,功能相对单一
# 可以使用分支结构
def func(sex):
    if sex == 'male':
        return 'handsome'
    else:
        return 'beauty'
res = func('male')
print(res)
# lambda 参数列表:真区间 if 表达式判断 else 假区间
ret = lambda sex:'nicce'if sex == 'male'else'good'
print(ret('man'))

# 可以将匿名函数封装在一个函数内,这样可以使用同样的代码来创建多个匿名函数
def test(n):
  return lambda a : a * n
doubler = test(2)
tripler = test(3)
print(doubler(11))
print(tripler(11))

10.5.4 迭代器

python中最具特色的功能之一,是访问集合元素的一种方式
迭代器是一个可以记住访问遍历的位置的对象
从集合第一个元素开始到左右元素被访问完毕
只能从前往后一个一个的遍历,不能后退
能被next()函数调用,并不断放回下一个值的对象称为iterator

迭代器:

range(10,3,-1) #返回一个可迭代对象
for i in range(10,2,-1):
    print(i)

arr = [99,8,'yy',1]
for i in arr:
    print(i)

iter()
功能:把可迭代的对象,转为一个迭代器对象
参数:可迭代的对象(str,list,tuple,dict,set,range...)
返回值:迭代器对象
注意:迭代器一定是一个可迭代的对象,但是可迭代对象不一定是迭代器

f4 = ['one','two','three','four']
# 可以使用for循环来遍历数据、
# 可以把可迭代对象转为迭代器使用
res = iter(f4)
print(res,type(res))

迭代器的取值方案:

  1. next()调用一次获取一次,直到数据被取完
  2. list()直接取出所有的数据
  3. for 使用for循环遍历迭代器的数据
# 1. 使用next()函数去调用迭代器对象
r = next(res)
print(r)
print(list(res))  #2.list()
r = next(res)      #超出可迭代的范围 StopIteration
print(r)
# print(next(res))
# print(next(res))
# print(next(res)) #超出可迭代的范围 StopIteration
# 3. for 循环
for i in res:
    print(i)
# print(next(res)) #超出可迭代的范围 StopIteration

迭代器取值特点:

  • 取出一个少一个,直到取完

检测迭代器和可迭代对象的方法

  1. from collections.abc import Iterator,Iterable
from collections.abc import Iterator,Iterable
varstr = '12345'
res = iter(varstr)
# type()函数返回当前数据的类型
# isinstance()检测一个数据是不是一个指定的类型
r1 = isinstance(varstr, Iterable)
r2 = isinstance(varstr, Iterator)
r3 = isinstance(res, Iterable)
r4 = isinstance(res, Iterator)
print(r1,r2,r3,r4)
#迭代器一定是一个可迭代对象,可迭代对象不一定是一个迭代器

2.next()函数检测

next(varstr) #TypeError: 'str' object is not an iterator

10.5.5 生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

调用一个生成器函数,返回的是一个迭代器对象

StopIteration

StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值