Python进阶知识(三)

这篇具有很好参考价值的文章主要介绍了Python进阶知识(三)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.Python 迭代器

在Python中,迭代器(Iterator)是一种用于遍历集合元素的对象。它是一个实现了迭代器协议(Iterator Protocol)的对象,该协议包含两个方法:__iter____next__

  1. __iter__方法:返回迭代器对象自身。它在迭代开始之前被调用,用于初始化迭代器的状态。
  2. __next__方法:返回迭代器中的下一个元素。如果没有更多的元素可供返回,则抛出StopIteration异常。

要创建一个迭代器,你可以使用一个类来定义它,实现上述两个方法。下面是一个简单的示例:

class MyIterator:
    def __init__(self, max_num):
        self.max_num = max_num
        self.current = 0

    def __iter__(self):
        return self

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

# 使用自定义迭代器
my_iter = MyIterator(5)
for num in my_iter:
    print(num)

输出结果将是:

Copy code
0
1
2
3
4

在上面的示例中,MyIterator类实现了迭代器协议。它通过__init__方法初始化迭代器的状态,在__next__方法中定义了迭代的逻辑,并在达到最大值时抛出StopIteration异常。__iter__方法返回迭代器对象自身,使得该对象可以在迭代过程中被使用。

除了自定义迭代器,Python还提供了一些内置的迭代器,例如rangeenumeratezip等。这些迭代器可以方便地用于不同的迭代场景,简化了迭代操作的编写。

2.Python 生成器

在Python中,生成器(Generator)是一种特殊类型的迭代器,可以通过函数来创建。它使用了一种称为"yield"的关键字,允许你暂停函数的执行并返回一个值,然后在需要时继续执行,从而实现了延迟计算。

生成器的特点是它们在每次迭代时生成一个值,而不是一次性生成所有值。这样可以减少内存占用,并且允许处理大量的数据流或无限序列。

要创建一个生成器,你可以使用函数来定义它,并在需要生成值时使用yield语句。下面是一个简单的示例:

def my_generator(max_num):
    for i in range(max_num):
        yield i

# 使用生成器
my_gen = my_generator(5)
for num in my_gen:
    print(num)

输出结果将是:

Copy code
0
1
2
3
4

在上面的示例中,my_generator函数是一个生成器函数。它使用yield语句在每次迭代时生成一个值,并在下一次迭代时继续执行。通过将生成器函数调用赋值给变量my_gen,我们得到了一个生成器对象。然后,我们可以像使用其他迭代器一样使用生成器进行迭代。

生成器非常适合处理大型数据集或需要逐个生成值的场景。由于生成器只在需要时生成值,所以可以大大减少内存消耗。此外,生成器还可以用于处理无限序列,因为它们可以无限地生成值而不会耗尽内存。

除了使用yield语句创建生成器函数之外,Python还提供了生成器表达式(Generator Expression)的语法。它类似于列表推导式,但使用圆括号而不是方括号,并返回一个生成器对象。生成器表达式的语法更简洁,适用于简单的生成器场景。

my_gen = (x for x in range(5))
for num in my_gen:
    print(num)

输出结果与前面的示例相同。在这里,我们使用生成器表达式(x for x in range(5))创建了一个生成器对象,并对其进行迭代。

总结来说,生成器是一种使用函数和yield语句创建的特殊类型的迭代器。它提供了一种延迟计算的方式,逐个生成值,并且可以有效地处理大量的数据流或无限序列。

当处理大量数据或无限序列时,生成器的内存效率可以通过以下几个示例进行说明:

示例 1: 生成大量数据 假设你需要生成一个包含一百万个整数的列表。使用生成器的方式可以显著减少内存消耗。比较以下两种实现方式:

使用列表生成式:

my_list = [x for x in range(1000000)]

使用生成器:

def my_generator():
    for x in range(1000000):
        yield x

my_gen = my_generator()

在第一种实现方式中,使用列表生成式一次性生成了包含一百万个整数的列表,并将其存储在内存中。而在第二种实现方式中,使用生成器函数每次迭代只生成一个整数,并且不会一次性加载所有数据到内存中。这样,使用生成器的方式可以大大减少内存消耗。

示例 2: 处理无限序列 生成器非常适合处理无限序列,因为它们可以无限地生成值而不会耗尽内存。考虑生成斐波那契数列的示例:

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib_gen = fibonacci()

在这个示例中,fibonacci函数是一个生成器函数,用于生成斐波那契数列。通过使用yield语句,每次迭代时生成一个斐波那契数,并且可以无限地生成下去。生成器fib_gen可以用于按需获取斐波那契数列的值,而不需要将整个序列加载到内存中。

通过以上示例,可以看出生成器在处理大量数据或无限序列时的内存效率。它们按需生成值,减少了内存消耗,并且能够处理比可用内存更大的数据集或无限序列。

3.Python 列表推导式

Python 中的列表推导式(List Comprehension)是一种简洁而强大的语法,用于创建新的列表,通常基于现有的列表或其他可迭代对象进行转换、过滤或组合操作。它的基本语法结构如下:

pythonCopy code
new_list = [expression for item in iterable if condition]

解释一下这个语法结构中的各部分:

  • expression 是一个用于对每个元素进行转换或操作的表达式。
  • item 是用于迭代的变量,代表可迭代对象中的每个元素。
  • iterable 是一个可迭代对象,如列表、元组、字符串或其他可迭代对象。
  • if condition(可选)是一个条件表达式,用于过滤元素。只有满足条件的元素才会被包含在新列表中。

下面是一些示例来说明列表推导式的使用:

示例 1: 创建新的列表

pythonCopy code
numbers = [1, 2, 3, 4, 5]
squared_numbers = [x**2 for x in numbers]
print(squared_numbers)  # 输出: [1, 4, 9, 16, 25]

在这个示例中,通过列表推导式将原始列表 numbers 中的每个元素平方,生成一个新的列表 squared_numbers

示例 2: 过滤元素

pythonCopy code
numbers = [1, 2, 3, 4, 5]
even_numbers = [x for x in numbers if x % 2 == 0]
print(even_numbers)  # 输出: [2, 4]

在这个示例中,通过列表推导式将原始列表 numbers 中的偶数元素提取出来,生成一个新的列表 even_numbers。只有满足条件 x % 2 == 0 的元素才会被包含在新列表中。

示例 3: 字符串处理

pythonCopy code
words = ['hello', 'world', 'python']
capitalized_words = [word.upper() for word in words]
print(capitalized_words)  # 输出: ['HELLO', 'WORLD', 'PYTHON']

在这个示例中,通过列表推导式将原始列表 words 中的每个字符串转换为大写形式,生成一个新的列表 capitalized_words

除了上述示例,列表推导式还可以嵌套、使用多个迭代变量和条件表达式,以及与函数等结合使用,从而实现更复杂的转换和过滤操作。

列表推导式是一种简洁而强大的语法,可以使代码更加清晰和高效。然而,当列表推导式变得过于复杂或难以理解时,应考虑使用传统的循环方式来替代,以提高代码的可读性和可维护性。

4.Python协程

在 Python 中,协程(Coroutines)是一种并发编程的技术,用于在单线程中实现异步操作和并发任务。协程允许在执行过程中暂停和恢复函数的执行,并在需要时交替执行多个任务,以实现更高效的异步编程。

Python 3.4 引入了 asyncio 模块,它提供了对协程的支持,使得在 Python 中使用协程变得更加简单和方便。以下是协程的基本概念和用法:

  1. 协程函数(Coroutine Function):协程函数使用 async def 声明,并在函数体中使用 await 关键字来暂停执行,让出控制权给事件循环(Event Loop)。例如:

    import asyncio
    
    async def my_coroutine():
        # 执行一些异步操作
        await asyncio.sleep(1)
        print("Coroutine executed")
    
  2. 事件循环(Event Loop):事件循环是协程的调度器,负责调度协程的执行顺序和管理异步任务。通过 asyncio.get_event_loop() 获取当前线程的事件循环对象,然后使用 loop.run_until_complete() 来运行协程。

    loop = asyncio.get_event_loop()
    loop.run_until_complete(my_coroutine())
    
  3. 异步任务(Task):可以通过 asyncio.create_task() 创建一个任务,将协程函数包装为一个可调度的对象。任务可以并发执行,可以使用 await 关键字等待任务的完成。

    async def main():
        task1 = asyncio.create_task(my_coroutine())
        task2 = asyncio.create_task(my_coroutine())
        await asyncio.gather(task1, task2)
    

通过使用协程和 asyncio 库,可以实现高效的并发编程。协程可以在遇到阻塞操作时暂停自己,切换到其他任务执行,从而充分利用系统资源并提高程序的响应性。它们在网络编程、IO密集型任务和并发处理等场景中非常有用。

需要注意的是,协程并不是多线程或多进程的替代方案。协程在单线程中运行,并使用事件循环调度任务的执行,因此适合处理 IO 密集型任务,而不是 CPU 密集型任务。此外,协程的性能和效果也取决于具体的应用场景和使用方式。

4.1 IO 密集型任务和 CPU 密集型任务

  1. IO 密集型任务(IO-bound tasks): IO 密集型任务是指任务的主要瓶颈在于输入/输出操作(IO 操作),例如从磁盘读取文件、网络请求、数据库查询等。这些任务通常涉及与外部资源的交互,需要等待IO操作完成,而任务本身在等待的过程中并不会占用大量的 CPU 资源。在执行 IO 操作时,CPU 大部分时间都处于空闲状态。

    示例:网页爬虫、文件读写、网络请求、图像处理等涉及到读写磁盘、网络传输或数据库查询的任务都属于 IO 密集型任务。

  2. CPU 密集型任务(CPU-bound tasks): CPU 密集型任务是指任务的主要瓶颈在于 CPU 计算能力,需要大量的 CPU 运算来完成任务。这些任务通常涉及复杂的数学计算、算法运算、图像处理、加密解密等,需要大量的 CPU 资源进行计算。在执行 CPU 密集型任务时,CPU 被长时间占用,而等待 IO 操作完成的时间相对较少。

    示例:科学计算、图像/视频处理、加密解密、大规模数据分析等需要大量计算的任务都属于 CPU 密集型任务。

区分 IO 密集型任务和 CPU 密集型任务的重要性在于对资源的合理利用。在 IO 密集型任务中,任务的执行时间主要花费在等待 IO 操作上,因此可以通过异步编程、并发处理或使用多线程/多进程来提高效率。而在 CPU 密集型任务中,任务的执行时间主要花费在 CPU 运算上,因此可以通过优化算法、并行计算、使用多线程/多进程甚至利用分布式计算来提高效率。

了解任务的性质(IO 密集型还是 CPU 密集型)有助于选择适当的编程模型、并发策略和资源分配方式,从而使程序能够充分利用可用的计算资源,提高执行效率和性能。

4.2 豆瓣近日推荐电影爬虫

import asyncio
import aiohttp
from bs4 import BeautifulSoup
async def fetch_content(url):
    async with aiohttp.ClientSession(
        headers=header, connector=aiohttp.TCPConnector(ssl=False)
    ) as session:
        async with session.get(url) as response:
            return await response.text()
async def main():
    url = "https://movie.douban.com/cinema/later/beijing/"
    init_page = await fetch_content(url)
    init_soup = BeautifulSoup(init_page, 'lxml')
    movie_names, urls_to_fetch, movie_dates = [], [], []
    all_movies = init_soup.find('div', id="showing-soon")
for
each_movie in all_movies.find_all('div', class_="item"):
all_a_tag = each_movie.find_all('a')
all_li_tag = each_movie.find_all('li')
movie_names.append(all_a_tag[1].text)
urls_to_fetch.append(all_a_tag[1]['href'])
movie_dates.append(all_li_tag[0].text)
tasks = [fetch_content(url) for url in urls_to_fetch]
pages = await asyncio.gather(*tasks)
    for movie_name, movie_date, page in zip(movie_names, movie_dates, pages):
        soup_item = BeautifulSoup(page, 'lxml')
        img_tag = soup_item.find('img')
        print('{} {} {}'.format(movie_name, movie_date, img_tag['src']))
%time asyncio.run(main())

这段代码展示了使用 asyncio、aiohttp 和 BeautifulSoup 库来进行异步网页爬取的示例。逐步解释代码的执行过程:

  1. 导入必要的模块:代码中导入了 asyncio、aiohttp 和 BeautifulSoup 模块,用于实现异步编程和网页解析。
  2. 定义异步函数 fetch_content(url):这是一个异步函数,用于获取给定 URL 的网页内容。它使用 aiohttp 库创建一个异步会话(ClientSession),发送 HTTP 请求并返回响应的文本内容。
  3. 定义异步函数 main():这是主要的异步函数,用于执行网页爬取的主要逻辑。首先,通过调用 fetch_content(url) 函数获取初始页面的内容,并使用 BeautifulSoup 进行解析。
  4. 解析页面内容:使用 BeautifulSoup 解析初始页面,获取显示即将上映电影信息的 div 元素(id=“showing-soon”)。
  5. 遍历电影信息:通过遍历每个电影的 div 元素,提取电影名称、URL 和上映日期,并将它们分别存储在 movie_names、urls_to_fetch 和 movie_dates 列表中。
  6. 创建任务列表:根据获取到的电影 URL 列表(urls_to_fetch),创建一个异步任务列表 tasks,其中每个任务都是调用 fetch_content(url) 函数获取网页内容的异步任务。
  7. 并发执行任务:使用 asyncio.gather() 函数并发执行任务列表中的所有任务,并使用 await 关键字等待所有任务完成,返回得到的网页内容列表 pages。
  8. 处理每个电影的网页内容:通过使用 zip() 函数将电影名称、上映日期和对应的网页内容进行配对,然后使用 BeautifulSoup 解析每个网页内容,并提取电影海报的 URL。
  9. 打印结果:将电影名称、上映日期和海报 URL 打印出来。
  10. 计算执行时间:使用 %time 计算程序的运行时间。
  11. 运行主函数:通过 asyncio.run() 运行主函数 main(),启动异步爬取过程。

该代码利用 asyncio 和 aiohttp 实现了异步的网页爬取过程,可以高效地同时请求多个网页并进行处理。通过异步编程,可以充分利用网络请求等IO操作的等待时间,提高爬取效率和程序的整体性能。文章来源地址https://www.toymoban.com/news/detail-498346.html

到了这里,关于Python进阶知识(三)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++迭代器(STL迭代器)iterator详解

    要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。 迭代器按照定义方式分

    2024年02月03日
    浏览(45)
  • 迭代器 Iterator

    迭代器是一种设计模式,它用于遍历集合或容器中的元素,能够访问集合的元素而无需关心集合的内部结构: 特点: 封装集合访问 :迭代器封装了对集合元素的访问,通过迭代器访问集合中的元素,而无需了解集合的内部结构或实现方式。 统一接口 :迭代器提供了一致的

    2024年02月08日
    浏览(47)
  • JS - iterator(迭代器)

    一、 概念        JavaScript原有表示“集合”的数据结构,主要是数组(\\\' Array \\\')和对象(\\\' Object \\\' ),ES6又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,

    2024年02月07日
    浏览(48)
  • 迭代器模式(Iterator Pattern)

    一、定义 提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。 二、优缺点 优点:  1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类

    2024年02月09日
    浏览(45)
  • 【Java杂谈】Iterator(迭代器)的使用

    迭代器是Java提供的一种访问集合的方法,Iterator 是 Java 迭代器最简单的实现,常用来访问ArrayList、HashMap等类的对象。 Iterator是一个接口,ListIterator是Collection API中的接口,它扩展了Iterator接口: 可以使用iterator() 方法: hasNext()方法的作用是没有遍历完成 next()方法有两个作用

    2024年02月10日
    浏览(58)
  • 【 java 集合】使用迭代器 Iterator 遍历集合

    💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜 📝 个人主页:馆主阿牛🔥 🎉 支持我:点赞👍+收藏⭐️+留言📝 📣 系列专栏:java 小白到高手的蜕变🍁 💬格言:要成为光,因为有怕黑的人!🔥 上两篇文章总结了集合体系以及Collection常用方法,本节我们来

    2024年02月07日
    浏览(53)
  • 设计模式--迭代器模式(Iterator Pattern)

    迭代器模式(Iterator Pattern)是一种行为型设计模式,用于提供一种统一的方式来访问一个聚合对象中的各个元素,而不需要暴露该聚合对象的内部结构。迭代器模式将遍历集合的责任从集合对象中分离出来,使得可以在不同的情况下使用不同的迭代方式。 迭代器模式通常包

    2024年02月10日
    浏览(49)
  • 设计模式第16讲——迭代器模式(Iterator)

    迭代器模式是一种 行为型 设计模式,它提供了一种 统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式 。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 抽象迭代器(Iterator):定义了遍历聚合对

    2024年02月11日
    浏览(46)
  • java -- 异常处理、Collection、Iterator迭代器、泛型

    Java异常处理的五个: try、catch、finally、throw、throws 在编写程序时,我们必须要考虑程序出现问题的情况 当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式

    2023年04月08日
    浏览(52)
  • 什么是迭代器(Iterator)?如何使用它遍历集合?

    迭代器(Iterator)是一种设计模式,它提供了一种遍历集合(如列表、数组、映射等)中元素的方法,而无需暴露集合内部的表示细节。它将遍历和集合分离,使得可以独立地操作和遍历集合。 在许多编程语言中,迭代器是一个对象,它包含了可以遍历集合的方法。常见的迭

    2024年02月15日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包