python进阶语法day10

Python迭代器与生成器

1. 迭代器 Iterator

什么是迭代器

  • 迭代器是访问可迭代对象的工具

  • 迭代器是指用 iter(obj) 函数返回的对象(实例)

  • 迭代器可以用next(it)函数获取可迭代对象的数据

迭代器函数iter和next

函数说明
iter(iterable)从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象
next(iterator)从迭代器iterator中获取下一个记录,如果无法获取一下条记录,则触发 StopIteration 异常

迭代器说明

  • 迭代器只能往前取值,不会后退

  • 用iter函数可以返回一个可迭代对象的迭代器

迭代器示例:

 # 示例 可迭代对象
    L = [1, 3, 5, 7]
    it = iter(L)  # 从L对象中获取迭代器
    next(it)  # 1  从迭代器中提取一个数据
    next(it)  # 3
    next(it)  # 5
    next(it)  # 7
    next(it)  # StopIteration 异常
    # 示例2 生成器函数
    It = iter(range(1, 10, 3))
    next(It)  # 1
    next(It)  # 4
    next(It)  # 7
    next(It)  # StopIteration

for x in L 的工作原理

当使用 for x in L 时,Python 内部会执行以下步骤:

  1. 获取迭代器:调用 L.__iter__(),获取一个迭代器对象。

  2. 迭代元素:通过迭代器的 __next__() 方法逐个获取元素,直到抛出 StopIteration 异常。

  3. 赋值和循环:将每次获取的元素赋值给变量 x,并执行循环体。

因此,for x in L 本身并不是迭代器,而是利用了迭代器的机制来实现循环。

class MyIterator:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.end:
            self.current += 1
            return self.current - 1
        else:
            raise StopIteration

# 使用迭代器
my_iter = MyIterator(1, 5)
for num in my_iter:

2. 生成器

生成器是在程序运行时生成数据,与容器不同,它通常不会在内存中保留大量的数据,而是现用现生成。

  • yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。

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

  • 每次使用 yield 语句生产一个值后,函数都将暂停执行,等待被重新唤醒。

  • yield 语句相比于 return 语句,差别就在于 yield 语句返回的是可迭代对象,而 return 返回的为不可迭代对象。

  • 然后,每次调用生成器的 next() 方法或使用 for 循环进行迭代时,函数会从上次暂停的地方继续执行,直到再次遇到 yield 语句。

生成器可以用算法动态的生成数据

主要特点

  1. 惰性求值:生成器不会一次性计算所有值,而是逐个生成值,节省内存。

  2. yield 语句:生成器通过 yield 语句返回值,每次调用 next() 时,生成器会从上次 yield 的位置继续执行。

  3. 自动实现迭代器协议:生成器自动实现了 __iter__()__next__() 方法,因此可以直接用于 for 循环或其他迭代工具。

生成器有两种

  1. 生成器函数

  2. 生成器表达式

生成器函数

生成器函数通过 yield 语句返回值。每次调用 next() 时,生成器会从上次 yield 的位置继续执行,直到遇到下一个 yield 或抛出 StopIteration 异常。

yield 语句的语法

yield 表达式

生成器函数示例1:

## 定义一个生成器函数, 有 yield 的函数调用后回返回生成器对象
def myrange(stop):
    i = 0
    while i < stop:
        yield i    # 为 遍历次生产器的for 语句提供数据
        i += 1

for x in myrange(5):
    print('x=', x)
    
# 创建一个生成器对象
gen = myrange(5)

# 使用 next() 函数迭代生成器
print(next(gen)) 
print(next(gen))  
print(next(gen)) 
print(next(gen))  
print(next(gen))  

生成器表达式

生成器表达式类似于列表推导式,但使用圆括号 () 而不是方括号 []。生成器表达式会逐个生成值,而不是一次性生成所有值。

  • 语法:

( 表达式 for 变量 in 可迭代对象 [if 真值表达式])

[] 内容代表可以省略

  • 作用

    用推导式的形式创建一个生成器

  • 示例

>>> [x ** 2 for x in range(1, 5)]   # 列表解析(列表推导式)
[1, 4, 9, 16]
>>> 
>>> (x ** 2 for x in range(1, 5))  # 生成器表达式
<generator object <genexpr> at 0x7f41dcd30a40>
>>> for y in (x ** 2 for x in range(1, 5)):
...     print(y)
... 
1
4
9
16

代码实现斐波那契数列(最少十个数)

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

fib_seq = fibonacci(10)    #10个数列的斐波那契的生成器,相当于自创的range。

print(fib_seq)
for i in fib_seq:         取生成器里的数列
    print(i, end="\t")





<generator object fb at 0x0000027237E5D460>   #fib_seq是一个生成器
0	1	1	2	3	5	8	13	21	34	

python 函数式编程

函数式编程(Functional Programming,FP)是一种编程范式,它将计算视为数学函数的求值,并强调使用纯函数(Pure Functions)和不可变数据(Immutable Data)。函数式编程的核心思想是将程序分解为一系列可组合的函数,通过函数的组合来实现复杂的逻辑。

在函数式编程中,函数被视为一等公民(First-Class Citizens),可以像普通变量一样被传递、返回或存储。

函数式编程的核心在于三个基本原则:纯函数、不可变数据和高阶函数。

1.纯函数

纯函数是指 对于相同的输入,总是返回相同的输出,并且没有副作用 的函数。副作用是指函数修改了函数外部的状态,例如修改全局变量、写入文件等。

纯函数的优点:

可测试性: 纯函数易于测试,因为它们的输出只取决于输入,不受外部状态的影响。

可缓存性: 纯函数的结果可以缓存,因为相同的输入总是产生相同的输出。

可并行性: 纯函数可以安全地并行执行,因为它们没有副作用,不会相互干扰。

# 纯函数示例
def sum(a, b):
  return a + b

# 非纯函数示例
total = 0
def add_to_total(x):
  global total
  total += x
  return total 

sum 函数是纯函数,因为它不依赖于任何外部状态,并且对于相同的输入总是返回相同的输出。而 add_to_total 函数不是纯函数,因为它修改了全局变量 total,产生了副作用。

2.不可变数据

不可变数据是指数据一旦创建就不能被修改。任何对数据的操作都会返回一个新的数据,而不是修改原数据。

Python 中的不可变数据类型包括:整数、字符串、元组等。

使用不可变数据可以提高代码的可靠性,因为它可以防止数据被意外修改。

3.高阶函数

高阶函数是指可以接受函数作为参数或返回函数作为结果的函数。

高阶函数是函数式编程的核心特性之一,它允许函数之间的组合和抽象。

(1)变量可以指向函数

以 Python 内置的求绝对值的函数abs()为例,调用该函数用以下代码:

print(abs(-10)) # 输出:10
print(abs)

abs()是函数的调用,而abs是函数本身。

如果把函数本身赋值给变量呢?

# 函数本身赋给变量,即f指向abs所指向的函数
f = abs
a = f(-10)
print(a)

说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。

(2) 函数名也是变量

函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!

把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数而是指向一个整数10!当然实际代码绝对不能这么写,这里是为了说明函数名也是变量。

既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

示例:

def add(x, y, f):
    return f(x) + f(y)

print(add(-5,6,abs))

把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式

3.1 内置高阶函数

定义:将函数作为参数或返回值的函数。

常用:

(1)map(函数,可迭代对象)

  • map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

(2)reduce(函数,可迭代对象)

  • reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

(2)filter(函数,可迭代对象)

  • 根据条件筛选可迭代对象中的元素,返回值为新可迭代对象。

(3)sorted(可迭代对象, key=函数, reverse=True)

  • 排序,返回值为排序后的列表结果。

(4)max(可迭代对象, key = 函数)

  • 根据函数获取可迭代对象的最大值。

(5)min(可迭代对象,key = 函数)

  • 根据函数获取可迭代对象的最小值。

示例

def f(x):
    return x*x
r = map(f,[1,2,3,4,5])
print(list(r))

# 把序列[1, 3, 5, 7, 9]变换成整数13579
from functools import reduce
def f2(x,y):
    return x*10+y
r1 = reduce(f2,[1,2,3,4,5])
print(r1)

# 在一个 list 中,删掉偶数,只保留奇数
def f3(x):
    return x%2 != 0
it = filter(f3,[1,2,3,4,5])
print(list(it))
# 按绝对值大小排序
print(sorted([36, 5, -12, 9, -21], key=abs))

# 求列表中的最大元素值
numbers = [3, 1, 4, 1, 5, -9, 2]
print(max(numbers,key=abs))  # 输出: 9

3.2 lambda 表达式

  • 定义:是一种匿名函数

作用:

-- 作为参数传递时语法简洁,优雅,代码可读性强。

-- 随时创建和销毁,减少程序耦合度。

语法

# 定义:
变量 = lambda 形参: 方法体
​
# 调用:
变量(实参)

说明:

-- 形参没有可以不填

-- 方法体只能有一条语句,且不支持赋值语句。

it = map(lambda x: x * x, range(10))
print(tuple(it))

4. 闭包 closure

什么是闭包?

  • 闭包是指引用了此函数外部嵌套函数的变量的函数 闭包就是能够读取其他函数内部变量的函数。只有函数内部的嵌套函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数,同时这个函数又引用了外部的变量“。

  • 在本质上,闭包是将内部嵌套函数和函数外部的执行环境绑定在一起的对象。

闭包必须满足以下三个条件:

缺点

装饰器的作用:

函数装饰器的语法:

def 装饰器函数名(fn):
    语句块
    return 函数对象
​
@装饰器函数名 <换行>
def 被装饰函数名(形参列表):
    语句块

5.1 基本装饰器

  • 必须有一个内嵌函数

  • 内嵌函数必须引用外部函数中变量

  • 外部函数返回值必须是内嵌函数。

  • def factorial(fun):
        def inner(x,y):
            return fun(x,y)
        return inner
    def add(x,y):
        return x+y
    def multiply(x,y):
        return x*y
    fa = factorial(add)
    fa(1,2)
    print(fa(2,3))

    闭包的优缺点

    优点

  • 提供数据封装,避免全局变量污染。

  • 支持函数式编程的特性,如高阶函数和柯里化。

  • 可能导致内存泄漏,因为闭包会保留外部函数的变量引用,无法被垃圾回收。

  • 过度使用闭包可能使代码难以理解和调试。

    • 可以创建私有变量和方法。

    • 5. 装饰器 decorators(专业提高篇)

      什么是装饰器

    • 装饰器是一个函数,主要作用是来用包装另一个函数或类

  • 在不修改被装饰的函数的源代码,不改变被装饰的函数的调用方式的情况下添加或改变原函数的功能。

  • # 日志打印函数
    def appendLog(func):
        print(f"{func.__name__}打印日志")
    
    # 闭包
    def funcOut(func):
        def funcIn():
            appendLog(func)
            func()
    
        return funcIn
    
    # 闭包函数名作为装饰器
    @funcOut
    def func1():
        print("func1正在运行...")
    
    @funcOut
    def func2():
        print("func2正在运行...")
    
    # 添加装饰器后,不需要显式调用闭包的外函数
    # func1 = funcOut(func1)
    func1()
    
    # func2 = funcOut(func2)
    func2()

    5.2 带参数的装饰器

    假设有一个函数:

    def sum(a,b):
        return a+b

    使用基本装饰器实现:

    def outer(func):
        def inner():
            return func()
        return inner
    ​
    @outer
    def sum(a,b):
        return a+b
    ​
    print(sum(1,2))
    ​
    # 报错:TypeError: inner() takes 0 positional arguments but 2 were given

    修改代码,给内部函数添加参数:

    def outer(func):
        def inner(x,y):
            return func(x,y)
        return inner
    ​
    @outer
    def sum(a,b):
        return a+b
    ​
    print(sum(1,2))
    # 输出3

    如果再添加一个add函数:

    def add(a, b, c):
        return a + b + c

    那么还需要定义一个闭包来作为装饰器:

    def outer1(func):
        def inner(x,y,z):
            return func(x,y,z)
        return inner
    @outer1   
    def add(a, b, c):
        return a + b + c
        
    print(add(1,2,3))

    从上边的代码来看,两个参数相加,三个参数相加都要相应的添加对应的装饰器,能定义一个通用的装饰器吗?

    可以考虑将闭包的内部函数中的参数换成*args

    def outer(func):
        def inner(*args):
            return func(*args)
        return inner
    @outer   
    def add(a, b, c):
        return a + b + c
        
    @outer
    def sum(a,b):
        return a+b
        
    print(add(1,2,3))
    print(sum(1,2))

    为了后续方便扩展,在内部方法中再添加**kwargs:

    def outer(func):
        def inner(*args, **kwargs):
            return func(*args,**kwargs)
        return inner

    至此带参数装饰器完成。

  • 5.3 装饰器链

    def uppercase(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            return result.upper()
        return wrapper
    
    def exclamation(func):
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            return result + "!"
        return wrapper
    
    @exclamation
    @uppercase
    def say_hello(name):
        return f"Hello, {name}"
    
    greeting = say_hello("Bob")
    print(greeting)  # 输出 "HELLO, BOB!"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值