Contents

วิธีจัดการทรัพยากรใน Python ด้วย Context Managers

สิ่งสำคัญคือต้องจัดการทรัพยากรอย่างเหมาะสมเมื่อสร้างแอปพลิเคชันเพื่อป้องกันหน่วยความจำรั่ว รับประกันการล้างข้อมูลที่เหมาะสม และรักษาเสถียรภาพของแอปพลิเคชันของคุณ ผู้จัดการบริบทนำเสนอโซลูชันที่ปรับปรุงแล้วสำหรับสถานการณ์นี้ ผู้จัดการบริบทปรับปรุงการจัดการทรัพยากรให้มีประสิทธิภาพโดยทำให้กระบวนการได้มาและเผยแพร่ทรัพยากรเป็นไปโดยอัตโนมัติ

ผู้จัดการบริบทคืออะไร?

ผู้จัดการบริบทโดยพื้นฐานแล้วคืออ็อบเจ็กต์ที่อธิบายกลยุทธ์ในการรักษาความปลอดภัยทรัพยากรและเผยแพร่เมื่อไม่ต้องการอีกต่อไป เอนทิตีเหล่านี้นำเสนอข้อได้เปรียบในการปรับปรุงการบริหารทรัพยากรโดยการนำเสนอในรูปแบบที่สอดคล้องกัน ตรงไปตรงมา และไม่คลุมเครือ ด้วยการจ้าง Context Manager เราสามารถลดโค้ดที่ซ้ำซ้อนและเพิ่มความชัดเจนของโค้ดเบสได้

แอปพลิเคชันซอฟต์แวร์มักต้องมีการบันทึกข้อมูลลงในไฟล์ ในกรณีที่ไม่ได้ใช้ตัวจัดการบริบท ผู้ใช้ต้องจัดการการเปิดและปิดไฟล์บันทึกด้วยตนเอง อย่างไรก็ตาม ด้วยการใช้ตัวจัดการบริบท กระบวนการสร้างและการแยกส่วนทรัพยากรการบันทึกสามารถทำให้ง่ายขึ้น ช่วยให้มั่นใจในการดำเนินการบันทึกอย่างมีประสิทธิภาพ ขณะเดียวกันก็รักษาการจัดการที่เหมาะสมของงานที่เกี่ยวข้องด้วย

ด้วยคำสั่ง

การใช้คำสั่ง with ภายในภาษาการเขียนโปรแกรม Python ช่วยให้นักพัฒนาสามารถใช้ประโยชน์จากความสามารถของตัวจัดการบริบทได้อย่างมีประสิทธิภาพ แม้ว่าจะมีเหตุการณ์พิเศษเกิดขึ้นระหว่างการดำเนินการบล็อกโค้ดที่เกี่ยวข้อง โครงสร้างนี้รับประกันว่าทรัพยากรที่ได้มาทั้งหมดจะถูกละทิ้งอย่างเหมาะสมและบรรลุวัตถุประสงค์ที่ตั้งใจไว้ตามนั้น

 with context_manager_expression as resource:
    # Code block that uses the resource
# Resource is automatically released when the block exits

ด้วยการใช้คำสั่ง with ผู้จัดการบริบทจะได้รับอำนาจในการบริหารทรัพยากร ช่วยให้คุณปลดปล่อยความสนใจของคุณไปสู่ความซับซ้อนของตรรกะของโปรแกรมของคุณ

การใช้ตัวจัดการบริบทในตัว

Python มีตัวจัดการบริบทในตัวที่ตอบสนองต่อสถานการณ์ที่แพร่หลาย เช่น การจัดการไฟล์ด้วยฟังก์ชัน open() และการควบคุมการเชื่อมต่อเครือข่ายผ่านโมดูล ซ็อกเก็ต

การจัดการไฟล์ด้วย open()

ฟังก์ชัน open() ทำหน้าที่เป็นตัวจัดการบริบทในตัวที่อำนวยความสะดวกในการโต้ตอบกับไฟล์ในการเขียนโปรแกรม Python ยูทิลิตี้นี้มักใช้สำหรับการอ่านหรือเขียนไฟล์และให้ผลออบเจ็กต์ไฟล์เมื่อดำเนินการ ข้อดีหลักประการหนึ่งอยู่ที่การปิดไฟล์โดยอัตโนมัติ ซึ่งช่วยป้องกันข้อมูลเสียหายโดยไม่ได้ตั้งใจเมื่อทำงานภายในบล็อกโค้ดที่ได้รับการจัดการ

 with open('file.txt', 'r') as file:
    content = file.read()
    # Do something with content
# File is automatically closed after exiting the block

การเชื่อมต่อเครือข่ายด้วยซ็อกเก็ต ()

โมดูล ซ็อกเก็ต นำเสนอตัวจัดการบริบทสำหรับซ็อกเก็ตเครือข่ายที่ช่วยให้มั่นใจได้ถึงการกำหนดค่าที่เหมาะสมและการกำจัดการเชื่อมต่อเครือข่าย ซึ่งช่วยลดความเสี่ยงด้านความปลอดภัยที่อาจเกิดขึ้นที่เกี่ยวข้องกับการเชื่อมต่อที่ถูกยกเลิกอย่างไม่เหมาะสม

 import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('localhost', 8080))
    # Send/receive data over the socket
# Socket is automatically closed after exiting the block

การใช้ตัวจัดการบริบทที่กำหนดเอง

Python อนุญาตให้สร้างตัวจัดการบริบทที่กำหนดเองผ่านทั้งแนวทางแบบคลาสและตามฟังก์ชัน ทางเลือกระหว่างแนวทางเหล่านี้ขึ้นอยู่กับข้อกำหนดเฉพาะของสถานการณ์ปัจจุบัน ในส่วนนี้ เราจะตรวจสอบวิธีการใช้ตัวจัดการบริบทที่กำหนดเองโดยใช้ทั้งสองวิธี

ผู้จัดการบริบทโดยใช้แนวทางแบบคลาส

ในแนวทางแบบอิงคลาส จะกำหนดคลาสที่รวบรวมแนวคิดของการจัดการทรัพยากรผ่านการใช้วิธีการพิเศษสองวิธีที่เรียกว่า \ \ enter\ \ และ \_\ exit\ วิธีการขีดเส้นใต้แบบมหัศจรรย์หรือแบบขีดคู่เหล่านี้มีจุดประสงค์ที่แตกต่างกัน ฝ่ายแรกมีหน้าที่รับผิดชอบในการตั้งค่าและให้ทรัพยากรที่ต้องการ ในขณะที่ฝ่ายหลังทำให้แน่ใจว่าขั้นตอนการล้างข้อมูลอย่างละเอียดได้รับการดำเนินการ โดยไม่คำนึงถึงข้อยกเว้นที่อาจเกิดขึ้นระหว่างการดำเนินการ

 class CustomContext:
    def __enter__(self):
        # Acquire the resource
        return resource

    def __exit__(self, exc_type, exc_value, traceback):
        # Release the resource
        pass

เพื่อที่จะดำเนินการหลายกระบวนการพร้อมกันในงานที่กำหนดได้อย่างมีประสิทธิภาพ จำเป็นต้องใช้ตัวจัดการบริบทซึ่งจะปรับปรุงกระบวนการนี้ให้มีประสิทธิภาพมากขึ้น ขณะเดียวกันก็จัดการกับปัญหาต่างๆ เช่น การจัดสรรทรัพยากร การประสานงาน และการแก้ไขข้อผิดพลาด โซลูชันที่เหมาะสมที่สุดควรสร้าง ดำเนินการ และบูรณาการกระบวนการเหล่านี้ได้อย่างราบรื่นโดยอัตโนมัติ เพื่อให้มั่นใจว่ามีการจัดการทรัพยากรและการซิงโครไนซ์ระหว่างกระบวนการอย่างเหมาะสม

 import multiprocessing
import queue

class ProcessPool:
    def __init__(self, num_processes):
        self.num_processes = num_processes
        self.processes = []

    def __enter__(self):
        self.queue = multiprocessing.Queue()

        for _ in range(self.num_processes):
            process = multiprocessing.Process(target=self._worker)
            self.processes.append(process)
            process.start()

        return self

    def __exit__(self, exc_type, exc_value, traceback):
        for process in self.processes:
            # Sending a sentinel value to signal worker processes to exit
            self.queue.put(None)
        for process in self.processes:
            process.join()

    def _worker(self):
        while True:
            number = self.queue.get()
            if number is None:
                break
            calculate_square(number)

def calculate_square(number):
    result = number * number
    print(f"The square of {number} is {result}")

if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5]

    # Usage
    with ProcessPool(3) as pool:
        for num in numbers:
            pool.queue.put(num)

    # Processes are automatically started and
     # joined when exiting the 'with' block

ตัวจัดการบริบท ProcessPool จัดการกลุ่มของกระบวนการของผู้ปฏิบัติงานอย่างมีประสิทธิภาพ โดยการจัดสรรการคำนวณ (การคำนวณกำลังสองของจำนวนเต็ม) ระหว่างกระบวนการเหล่านั้นเพื่อการประมวลผลพร้อมกัน ด้วยการควบคุมรูปแบบความเท่าเทียมนี้ จึงเป็นไปได้ที่จะบรรลุการจัดสรรหน่วยประมวลผลกลางที่มีอยู่อย่างรอบคอบมากขึ้น ตลอดจนเร่งการทำงานให้เสร็จสิ้นเมื่อเปรียบเทียบกับการดำเนินการตามลำดับภายในกระบวนการเดี่ยว

/th/images/output-of-class-based-context-manager.jpg

ผู้จัดการบริบทโดยใช้แนวทางตามฟังก์ชัน

โมดูล contextlib มีตัวตกแต่ง @contextmanager ซึ่งอำนวยความสะดวกในการสร้างตัวจัดการบริบทผ่านฟังก์ชันตัวสร้าง นักตกแต่งช่วยให้สามารถปรับปรุงพฤติกรรมของฟังก์ชันได้โดยการเพิ่มความสามารถใหม่ๆ โดยไม่ต้องเปลี่ยนรูปแบบดั้งเดิม

ภายในบริบทของแนวทางที่ใช้มัณฑนากร การใช้ฟังก์ชันตัวสร้างช่วยให้สามารถใช้ทั้งคำสั่ง ผลผลิต และ สุดท้าย เพื่อแบ่งเขตการได้มาและการปล่อยทรัพยากรตามลำดับ

 from contextlib import contextmanager

@contextmanager
def custom_context():
    # Code to acquire the resource
    resource = ...

    try:
        yield resource # Resource is provided to the with block
    finally:
        # Code to release the resource
        pass

ในการสร้างตัวจัดการบริบทสำหรับการวัดเวลาดำเนินการของบล็อคโค้ดที่กำหนด เราอาจใช้แนวทางเชิงฟังก์ชัน สิ่งนี้เกี่ยวข้องกับการรวมกลไกการจับเวลาภายในบล็อคโค้ดที่ระบุ และต่อมาคำนวณระยะเวลาของการดำเนินการเมื่อเสร็จสิ้น

 import time
from contextlib import contextmanager

@contextmanager
def timing_context():
    start_time = time.time()

    try:
        yield
    finally:
        end_time = time.time()
        elapsed_time = end_time - start_time
        print(f"Elapsed time: {elapsed_time} seconds")

# Usage
with timing_context():
    # Code block to measure execution time
    time.sleep(2)

บริบทเวลา\_

/th/images/output-of-function-based-context-manager.jpg

การใช้วิธีใดวิธีหนึ่งอาจสร้างตัวจัดการบริบทที่ได้รับการปรับแต่งซึ่งครอบคลุมความซับซ้อนของการจัดการทรัพยากรที่ซับซ้อนและงานที่เกิดซ้ำ ด้วยเหตุนี้จึงเพิ่มประสิทธิภาพการจัดระเบียบโค้ดและอำนวยความสะดวกในการบำรุงรักษา

การซ้อนผู้จัดการบริบท

การซ้อนตัวจัดการบริบทอาจมีข้อได้เปรียบในสถานการณ์ที่จำเป็นต้องมีการจัดการทรัพยากรหลายรายการพร้อมกัน ด้วยการใช้บริบทที่ซ้อนกัน เราสามารถรักษาลำดับการดำเนินงานได้อย่างต่อเนื่องและไร้ข้อผิดพลาด ในขณะเดียวกันก็รับและปล่อยทรัพยากรแต่ละรายการอย่างขยันขันแข็งตามที่ต้องการ

ในบางกรณี อาจจำเป็นสำหรับโปรแกรมในการดึงข้อมูลจากไฟล์และจัดเก็บไว้ในฐานข้อมูล การจัดการทรัพยากรที่แตกต่างกันทั้งสองนี้ถือเป็นความท้าทายที่สามารถแก้ไขได้ผ่านการใช้ตัวจัดการบริบทที่ซ้อนกันภายในกันและกัน

 import sqlite3

class DatabaseConnection:
    def __enter__(self):
        self.connection = sqlite3.connect('lite.db')
        return self.connection

    def __exit__(self, exc_type, exc_value, traceback):
        self.connection.close()

# Using nested context managers
with DatabaseConnection() as db_conn, open('data.txt', 'r') as file:
    cursor = db_conn.cursor()

    # Create the table if it doesn't exist
    cursor.execute("CREATE TABLE IF NOT EXISTS data_table (data TEXT)")

    # Read data from file and insert into the database
    for line in file:
        data = line.strip()
        cursor.execute("INSERT INTO data_table (data) VALUES (?)", (data,))

    db_conn.commit()

ในกรณีนี้ ตัวจัดการบริบท DatabaseConnection มีหน้าที่จัดการการเชื่อมต่อฐานข้อมูล ในขณะที่ตัวจัดการบริบท open() โดยธรรมชาติจะดูแลการจัดการไฟล์

เพื่อจัดการทั้งไฟล์และการเชื่อมต่อฐานข้อมูลอย่างมีประสิทธิภาพ สิ่งสำคัญคือต้องรวมบริบททั้งสองนี้ไว้ในคำสั่งเดียว สิ่งนี้ทำให้แน่ใจได้ว่าการปล่อยทรัพยากรทั้งสองอย่างเหมาะสมจะเกิดขึ้นหากมีข้อยกเว้นใดๆ เกิดขึ้นระหว่างกระบวนการอ่านไฟล์หรือการแทรกฐานข้อมูล

การปรับแต่งฟังก์ชั่นด้วยมัณฑนากร

การดูแลทรัพยากรอย่างมีประสิทธิผลเป็นข้อกำหนดเบื้องต้นขั้นพื้นฐานสำหรับประสิทธิภาพและความเสถียรสูงสุดในระบบซอฟต์แวร์ การเกิดการรั่วไหลของทรัพยากรอาจนำไปสู่การสะสมของหน่วยความจำที่มากเกินไป ส่งผลให้ประสิทธิภาพลดลงและช่องโหว่ที่อาจเกิดขึ้น ได้รับการพิสูจน์แล้วว่าการใช้ตัวจัดการบริบทเป็นแนวทางที่ได้รับการปรับปรุงในการจัดการกับความท้าทายที่เกี่ยวข้องกับการจัดการทรัพยากร