返回顶部
首页 > 资讯 > 后端开发 > Python >Python读取文件的四种方式的实例详解
  • 265
分享到

Python读取文件的四种方式的实例详解

2024-04-02 19:04:59 265人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录学生数量特别少的情况停车场空间不够时怎么办?怎么加快执行效率?怎么加快处理速度?结语故事背景:最近在处理Wikipedia的数据时发现由于数据量过大,之前的文件读取和数据处理方法

故事背景:最近在处理Wikipedia的数据时发现由于数据量过大,之前的文件读取和数据处理方法几乎不可用,或耗时非常久。今天学校安排统一核酸检查,刚好和文件读取的过程非常相似。正好借此机会和大家一起从头梳理一下几种文件读取方法。

故事设定:现在学校要求对所有同学进行核酸采集,每位同学先在宿舍内等候防护人员(以下简称“大白”)叫号,叫到自己时去停车场排队等候大白对自己进行采集,采集完之后的样本由大白统一有序收集并储存。

名词解释:

  • 学生:所有的学生是一个大文件,每个学生是其中的一行数据
  • 宿舍:硬盘
  • 停车场:内存
  • 核酸采集:数据处理
  • 样本:处理后的数据
  • 大白:程序

学生数量特别少的情况

当学生数量特别少时,可以考虑将所有学生统一叫到停车场等候,再依次进行核酸采集。

方法一:简单情况

此时的程序可以模拟为:

import time
from typing import List
 
 
def pick_all_students(dORM: str) -> List[str]:
    with open(dorm, "rt", encoding="utf8") as fin:
        students = fin.readlines()
        return students
 
 
def pick_sample(student: str) -> str:
    time.sleep(0.01)
    sample = f"{student.strip()}'s sample"
    return sample
 
 
def process(dorm: str, sample_storeroom: str) -> None:
    with open(sample_storeroom, "wt", encoding="utf8") as fout:
        students = pick_all_students(dorm)
        for student in students:
            sample = pick_sample(student)
            fout.write(f"{sample}\n")
            fout.flush()
 
 
if __name__ == "__main__":
    process(
        "student_names.txt",
        "sample_storeroom.txt"
    )

注意,在第19行中,大白一次性把所有同学都叫到了停车场中。这种做法在学生比较少时做起来很快,但是如果学生特别多,停车场装不下怎么办?

停车场空间不够时怎么办?

方法二:边读边处理

一般来说,由于停车场空间有限,我们不会采用一次性把所有学生都叫到停车场中,而是会一个一个地处理,这样可以节约内存空间。

import time
from typing import Iterator
 
 
def pick_one_student(dorm: str) -> Iterator[str]:
    with open(dorm, "rt", encoding="utf8") as fin:
        for student in fin:
            yield student
 
 
def pick_sample(student: str) -> str:
    time.sleep(0.01)
    sample = f"{student.strip()}'s sample"
    return sample
 
 
def process(dorm: str, sample_storeroom: str) -> None:
    with open(sample_storeroom, "wt", encoding="utf8") as fout:
        for student in pick_one_student(dorm):
            sample = pick_sample(student)
            fout.write(f"{sample}\n")
            fout.flush()
 
 
if __name__ == "__main__":
    process(
        "student_names.txt",
        "sample_storeroom.txt"
    )

这里pick_one_student函数中的返回值是用yield返回的,一次只会返回一名同学。

不过,这种做法虽然确保了停车场不会满员,但是这种做法在人数特别多的时候就不再适合了。虽然可以保证完成任务,但由于每次只能采集一个同学,程序的执行并不高。特别是当你的CPU有多个核时,会浪费机器性能,出现一核有难,其它围观的现象。

怎么加快执行效率?

大家可能也已经注意到了,刚刚我们的场景中,不论采用哪种方法,都只有一名大白在工作。那我们能不能加派人手,从而提高效率呢?

答案当然是可行的。我们现在先考虑增加两名大白,使得一名大白专注于叫号,安排学生进入停车场,另外一名大白专注于采集核酸,最后一名大白用于存储核酸样本。

方法三

import time
from multiprocessing import Queue, Process
from typing import Iterator
 
 
def pick_student(stu_queue: Queue, dorm: str) -> Iterator[str]:
    print("pick_student: started")
 
    picked_num = 0
    with open(dorm, "rt", encoding="utf8") as fin:
        for student in fin:
            stu_queue.put(student)
            picked_num += 1
            if picked_num % 500 == 0:
                print(f"pick_student: {picked_num}")
 
    # end signal
    stu_queue.put(None)
    print("pick_student: finished")
 
 
def pick_sample(student: str) -> str:
    time.sleep(0.01)
    sample = f"{student.strip()}'s sample"
    return sample
 
 
def process(stu_queue: Queue, store_queue: Queue) -> None:
    print("process: started")
 
    process_num = 0
    while True:
        student = stu_queue.get()
        if student is not None:
            sample = pick_sample(student)
            store_queue.put(sample)
            process_num += 1
            if process_num % 500 == 0:
                print(f"process: {process_num}")
        else:
            break
 
    # end signal
    store_queue.put(None)
    print("process: finished")
 
 
def store_sample(store_queue: Queue, sample_storeroom: str) -> None:
    print("store_sample: started")
 
    store_num = 0
    with open(sample_storeroom, "wt", encoding="utf8") as fout:
        while True:
            sample = store_queue.get()
            if sample is not None:
                fout.write(f"{sample}\n")
                fout.flush()
 
                store_num += 1
                if store_num % 500 == 0:
                    print(f"store_sample: {store_num}")
            else:
                break
 
    print("store_sample: finished")
 
 
if __name__ == "__main__":
    dorm = "student_names.txt"
    sample_storeroom = "sample_storeroom.txt"
 
    stu_queue = Queue()
    store_queue = Queue()
 
    store_p = Process(target=store_sample, args=(store_queue, sample_storeroom), daemon=True)
    store_p.start()
    process_p = Process(target=process, args=(stu_queue, store_queue), daemon=True)
    process_p.start()
    read_p = Process(target=pick_student, args=(stu_queue, dorm), daemon=True)
    read_p.start()
 
    store_p.join()

这份代码中,我们引入了多进程的思路,将每个大白看作一个进程,并使用了队列Queue作为进程间通信的媒介。stu_queue表示学生叫号进停车场的队列,store_queue表示已经采集过的待存储核酸样本的队列。

此外,为了控制进程的停止,我们在pick_student和 process函数的最后都向各自队列中添加了None作为结束标志符。

假设有1w名学生(student_names.txt文件有1w行),经过测试后发现上述方法的时间如下:

  • 方法一:1m40.716s
  • 方法二:1m40.717s
  • 方法三:1m41.097s

咦?不是做了分工吗?怎么速度还变慢了?经笔者观察,这是因为叫号的大白速度太快了(文件读取速度快)通常是TA已经齐活了,另外俩人还在吭哧吭哧干活呢,体现不出来分工的优势。如果这个时候我们对法二和法三的叫号做延时操作,每个学生叫号之后停滞10ms再叫下一位学生,则方法三的处理时间几乎不变,而方法二的时间则会延长至3m21.345s。

怎么加快处理速度?

上面提到,大白采核酸的时间较长,往往上一个人的核酸还没采完,下一个人就已经在后面等着了。我们能不能提高核酸采集这个动作(数据处理)的速度呢?其实一名大白执行一次核酸采集的时间我们几乎无法再缩短了,但是我们可以通过增加人手的方式,来达到这个目的。就像去银行办业务,如果开放的窗口越多,那么每个人等待的时间就会越短。这里我们也采取类似的策略,增加核酸采集的窗口。

import time
from multiprocessing import Queue, Process, cpu_count
from typing import Iterator
 
 
def pick_student(stu_queue: Queue, dorm: str, num_workers: int) -> Iterator[str]:
    print("pick_student: started")
 
    picked_num = 0
    with open(dorm, "rt", encoding="utf8") as fin:
        for student in fin:
            stu_queue.put(student)
            picked_num += 1
            if picked_num % 500 == 0:
                print(f"pick_student: {picked_num}")
 
    # end signal
    for _ in range(num_workers):
        stu_queue.put(None)
 
    print("pick_student: finished")
 
 
def pick_sample(student: str) -> str:
    time.sleep(0.01)
    sample = f"{student.strip()}'s sample"
    return sample
 
 
def process(stu_queue: Queue, store_queue: Queue) -> None:
    print("process: started")
 
    process_num = 0
    while True:
        student = stu_queue.get()
        if student is not None:
            sample = pick_sample(student)
            store_queue.put(sample)
            process_num += 1
            if process_num % 500 == 0:
                print(f"process: {process_num}")
        else:
            break
 
    print("process: finished")
 
 
def store_sample(store_queue: Queue, sample_storeroom: str) -> None:
    print("store_sample: started")
 
    store_num = 0
    with open(sample_storeroom, "wt", encoding="utf8") as fout:
        while True:
            sample = store_queue.get()
            if sample is not None:
                fout.write(f"{sample}\n")
                fout.flush()
 
                store_num += 1
                if store_num % 500 == 0:
                    print(f"store_sample: {store_num}")
            else:
                break
 
    print("store_sample: finished")
 
 
if __name__ == "__main__":
    dorm = "student_names.txt"
    sample_storeroom = "sample_storeroom.txt"
    num_process = max(1, cpu_count() - 1)
 
    maxsize = 10 * num_process
    stu_queue = Queue(maxsize=maxsize)
    store_queue = Queue(maxsize=maxsize)
 
    store_p = Process(target=store_sample, args=(store_queue, sample_storeroom), daemon=True)
    store_p.start()
    process_workers = []
    for _ in range(num_process):
        process_p = Process(target=process, args=(stu_queue, store_queue), daemon=True)
        process_p.start()
        process_workers.append(process_p)
    read_p = Process(target=pick_student, args=(stu_queue, dorm, num_process), daemon=True)
    read_p.start()
 
    for worker in process_workers:
        worker.join()
 
    # end signal
    store_queue.put(None)
    store_p.join()

总耗时 0m4.160s !我们来具体看看其中的细节部分:

首先我们将CPU核数 - 3作为采核酸的大白数量。这里减3是为其它工作进程保留了一些资源,你也可以根据自己的具体情况做调整

这次我们在 Queue中增加了 maxsize参数,这个参数是限制队列的最大长度,这个参数通常与你的实际内存情况有关。如果数据特别多时要考虑做些调整。这里我采用10倍的工作进程数目作为队列的长度

注意这里pick_student函数中要为每个后续的工作进程都添加一个结束标志,因此最后会有个for循环

我们把之前放在process函数中的结束标志提取出来,放在了最外侧,使得所有工作进程均结束之后再关闭最后的store_p进程

结语

总结来说,如果你的数据集特别小,用法一;通常情况下用法二;数据集特别大时用法四。

以上就是python读取文件的四种方式的实例详解的详细内容,更多关于Python读取文件的资料请关注编程网其它相关文章!

--结束END--

本文标题: Python读取文件的四种方式的实例详解

本文链接: https://lsjlt.com/news/143900.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python读取文件的四种方式的实例详解
    目录学生数量特别少的情况停车场空间不够时怎么办?怎么加快执行效率?怎么加快处理速度?结语故事背景:最近在处理Wikipedia的数据时发现由于数据量过大,之前的文件读取和数据处理方法...
    99+
    2024-04-02
  • C++读取文件的四种方式总结
    C++可以根据不同的目的来选取文件的读取方式,目前为止学习了C++中的四种文件读取方式。 C++文件读取的一般步骤: 1、包含头文件 #include<fstream> ...
    99+
    2023-05-15
    C++实现文件读取方式 C++文件读取 C++文件
  • Go语言读取文件的四种方式
    目录前言整个文件读取按行读取逐个单词读取以数据块的形式读取文件二进制读取总结前言 这篇文章将讨论如何在 Golang 中读取文件。我们将使用以下包来处理这些文件。 os 包提供了一个...
    99+
    2023-05-15
    Go 读取文件
  • 实例讲解python读取各种文件的方法
    目录1.yaml文件2.CSV文件3.ini文件总结1.yaml文件 # house.yaml--------------------------------------------...
    99+
    2024-04-02
  • Python读取文件的多种方式
    在Python编程中,读取文件是非常常见的操作。Python提供了多种读取文件的方式,本文将介绍其中的几种方式。 1. 使用open函数读取文件 使用Python内置函数open()可以打开一个文件,并返回一个文件对象。在文件对象上可以调用...
    99+
    2023-09-02
    python pandas 数据分析
  • Python实现单例模式的四种方式详解
    简介:单例模式可以保证一个类仅有一个实例,并提供一个访问它的全局访问点。适用性于当类只能有一个实例而且客户可以从一个众所周知的访问点访问它,例如访问数据库、MQ等。 实现方式: 1、...
    99+
    2024-04-02
  • Spring实例化bean的四种方式详解
    目录一、bean实例化——构造方法(常用)二、bean实例化——静态工厂(了解)三、bean实例化——实例工厂...
    99+
    2024-04-02
  • GoLang读取文件的10种方法实例
    目录一. 整个文件读入内存1.直接指定文化名读取1.1使用os.ReadFile函数读取文件2.先创建句柄再读取2.1使用os.OpenFile函数只读形式获取句柄2.2代码讲解二....
    99+
    2024-04-02
  • 利用Python读取文件的四种不同方法比对
    前言 大家都知道Python 读文件的方式多种多样,但是当需要读取一个大文件的时候,不同的读取方式会有不一样的效果。下面就来看看详细的介绍吧。 场景 逐行读取一个 2.9G 的大文件 CPU i7 ...
    99+
    2022-06-04
    四种 比对 文件
  • Node.js读取文件的三种方式
    本篇内容介绍了“Node.js读取文件的三种方式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!学习 Nod...
    99+
    2024-04-02
  • java 读取文件的几种方式
    在 Java 中有几种常用的方式来读取文件: 使用 FileInputStream 类以字节的方式读取文件。 使用 BufferedReader 在字符输入流上包装一个缓冲区,以行为单位读取文件。 使用 Scanner 类以分隔符为标志...
    99+
    2023-09-02
    java servlet 开发语言
  • Java读取文件的几种方式
    1. 使用流读取文件 public static void stream() { String fileName = "D:\\test.txt"; final String CHARSET_NAME = "UTF-8"; ...
    99+
    2023-09-09
    java 开发语言 servlet 前端
  • Excel文件读取的两种方式
    1、Pandas库的读取操作 from pandas import read_excel dr=read_excel(filename,header) dr#dataframe数据 dw=DataFrams(data=dict,colum...
    99+
    2023-01-31
    两种 方式 文件
  • 详解C++ 创建文件夹的四种方式
    在开头不得不吐槽一下,我要的是简单明了的创建文件夹的方式,看得那些文章给的都是复杂吧唧的一大坨代码,不仔细看鬼知道写的是啥。因此,为了方便以后自己阅读,这里自己写一下 C++ 创建文...
    99+
    2024-04-02
  • Java IO读取文件的实例详解
    Java中文件流的两个主要方式就是字符流和字节流,如下图:具体的使用方法可以参考官方文档,这里主要介绍四种常见的文件读取方式通过字节来读取文件(常用于二进制文件:图片、声音、视频等)2.通过字符来读取文件(常用于文本的读取)3.通过行来读取...
    99+
    2023-05-31
    java io 读取文件
  • Python读取.py文件的方法详解
    python读取.py文件的方法有三种:通过open()函数、pathlib模块以及importlib模块导入模块。这些方法允许读取.py文件的内容并将其用于各种目的,如执行模块或使用其...
    99+
    2024-04-03
    python 读取.py文件
  • java— 读取JSON文件的多种方式
    大部分内容参考自: https://blog.csdn.net/csdn_halon/article/details/120287992 在开发过程中有时会遇到需要读取本地.json文件的需求,通常会自己写Reader代码去读,但是...
    99+
    2023-09-01
    java json Powered by 金山文档
  • java 读取json文件的2种方式
    1 背景介绍 研发过程中,经常会涉及到读取配置文件等重复步骤,也行是.conf文件,也许是.json文件,但不管如何他们最终都需要进入到jave的inputStream里面。下面以读取.json文件为例 2 FileInputStream读...
    99+
    2023-08-18
    java json linux
  • 教你用Python读取CSV文件的5种方式
    目录第一招:简单的读取第二招:用nametuple第三招:用tuple类型转换第四招:用DictReader第五招:用字典转换在python里面,读取或写入csv文件时,首先要imp...
    99+
    2024-04-02
  • Python获取协程返回值的四种方式详解
    目录介绍源码依次执行结果介绍 获取协程返回值的四种方式: 1、通过ensure_future获取,本质是future对象中的result方 2、使用loop自带的create_tas...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作