ویژگی تصویر

معرفی کتابخانه pickle در پایتون

  /  پایتون   /  کتابخانه pickle در پایتون
بنر تبلیغاتی الف

کتابخانه pickle در پایتون ابزاری قدرتمند برای سریالایز کردن (قالب‌بندی/ذخیره) و دی‌سریالایز کردن اشیاء پایتون است. با استفاده از pickle می‌توان اشیاء پیچیده مانند لیست‌ها، دیکشنری‌ها، نمونه‌های کلاس‌ها و حتی توابع (در برخی شرایط) را به بایت‌استریم تبدیل کرد و بعدها آن‌ها را بازیابی نمود.

چرا از pickle استفاده کنیم؟

  • ذخیره و بارگذاری سریع اشیاء پایتون بدون نیاز به تبدیل دستی.
  • حفظ ساختار و نوع داده‌ها (برخلاف JSON که فقط انواع پایه را نگه می‌دارد).
  • انتقال داخلی بین فرایندها یا ذخیره‌سازی موقتی (caching) ساده.

مفاهیم پایه: dump, load, dumps, loads

import pickle

data = {'a': 1, 'b': [1, 2, 3]}

# ذخیره در فایل باینری
with open('data.pkl', 'wb') as f:
    pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)

# خواندن از فایل
with open('data.pkl', 'rb') as f:
    loaded = pickle.load(f)

# تبدیل به بایت (برای ارسال در شبکه یا کش)
b = pickle.dumps(data)
restored = pickle.loads(b)

در این قطعه کد، ابتدا یک دیکشنری ساده ساخته شده و سپس با استفاده از pickle.dump آن را در فایل باینری ذخیره می‌کنیم. پارامتر protocol=pickle.HIGHEST_PROTOCOL باعث می‌شود از جدیدترین فرمت سریالایز استفاده شود که معمولاً کاراتر است. pickle.dumps بایت‌ها را برمی‌گرداند و با pickle.loads می‌توان آن‌ها را بازیابی کرد.

پروتکل‌ها و سازگاری نسخه‌ها

pickle در طول زمان چندین پروتکل معرفی کرده است. نسخه‌های جدید پایتون پروتکل‌های بالاتری دارند که کارایی و فشرده‌سازی را بهبود می‌دهند. اما دقت کنید که فایل‌های pickle ایجادشده با پروتکل جدید ممکن است در پیاده‌سازی‌های قدیمی‌تر پایتون خوانده نشوند.

سریالایز کردن اشیاء سفارشی

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f'Person({self.name!r}, {self.age})'

p = Person('Ali', 30)

# ذخیره و بارگذاری نمونه کلاس
with open('person.pkl', 'wb') as f:
    pickle.dump(p, f)

with open('person.pkl', 'rb') as f:
    p2 = pickle.load(f)

نمونه‌ای از کلاس Person در فایل ذخیره و سپس بارگذاری می‌شود. pickle به‌صورت پیش‌فرض اعضای نمونه را ذخیره می‌کند. در صورت نیاز به کنترل دقیق‌تر (مثلاً حذف فیلدهای حساس یا نگهداری وضعیت ویژه)، می‌توان از متدهای جادویی __getstate__ و __setstate__ استفاده نمود.

class SecurePerson:
    def __init__(self, name, ssn):
        self.name = name
        self._ssn = ssn  # اطلاعات حساس

    def __getstate__(self):
        state = self.__dict__.copy()
        # حذف اطلاعات حساس هنگام ذخیره
        state.pop('_ssn', None)
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        # مقدار پیش‌فرض برای ssn پس از بارگذاری
        self._ssn = None

در این مثال، __getstate__ مانع ذخیره شمارهٔ حساس (ssn) می‌شود و __setstate__ مقدار پیش‌فرضی پس از بارگذاری قرار می‌دهد.

خطرات امنیتی و بهترین روش‌ها

نکته بسیار مهم: فایل‌های pickle مجاز به اجرای کد هنگام بارگذاری هستند. بنابراین هرگز دادهٔ pickle را از منبع غیرقابل‌اعتماد بارگذاری نکنید. حمله به وسیلهٔ pickle معمولاً با قرار دادن اشیاء خاص که هنگام unpickle اجرا می‌شوند صورت می‌گیرد.

بهترین راهکارها:

  • فقط از داده‌های pickle از منابع قابل اطمینان استفاده کنید.
  • برای تبادل با سیستم‌های ناهمگون یا داده‌های عمومی از فرمت‌های امن‌تر مثل JSON یا MessagePack استفاده کنید.
  • برای محدودسازی، می‌توانید Unpickler را subclass کنید و متد find_class را بازنویسی کرده و تنها کلاس‌های مجاز را بپذیرید.

نمونهٔ محدودسازی Unpickler (کنترل‌شده)

import io
import pickle

class RestrictedUnpickler(pickle.Unpickler):
    # لیست ماژول‌ها/کلاس‌های مجاز
    ALLOWED = {
        'builtins': {'list', 'dict', 'set', 'tuple', 'str', 'bytes', 'int', 'float', 'bool'}
    }

    def find_class(self, module, name):
        if module in self.ALLOWED and name in self.ALLOWED[module]:
            return super().find_class(module, name)
        # جلوگیری از بارگذاری هر کلاس دیگری
        raise pickle.UnpicklingError(f"Global '{module}.{name}' is forbidden")

def restricted_loads(s):
    return RestrictedUnpickler(io.BytesIO(s)).load()

این پیاده‌سازی نمونه‌ای ساده است که فقط انواع پایهٔ builtins را می‌پذیرد و از بارگذاری کلاس‌های دلخواه جلوگیری می‌کند. توجه داشته باشید که پیاده‌سازی‌های واقعی‌تر ممکن است پیچیده‌تر باشند و باید با دقت طراحی شوند؛ این مثال صرفاً جهت نشان دادن ایده است.

بهینه‌سازی و فشرده‌سازی

برای کاهش حجم فایل‌ها و افزایش سرعت I/O می‌توان data را با فشرده‌سازی ترکیب کرد:

import gzip
import pickle

with gzip.open('data.pkl.gz', 'wb') as f:
    pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)

with gzip.open('data.pkl.gz', 'rb') as f:
    loaded = pickle.load(f)

با gzip، فایل‌ها کوچک‌تر می‌شوند و اغلب هزینهٔ CPU برای فشرده‌سازی در برابر کاهش I/O و فضای ذخیره‌سازی جبران می‌گردد.

مقایسهٔ سریع با گزینه‌های دیگر

ویژگیpickleJSONMessagePackshelve
حفظ نوع دادهٔ پایتونخوب (تمام انواع پایتون)ضعیف (فقط انواع پایه)متوسط (باینری، نیاز به نگاشت)خوب (پایگاه کلیدی از اشیاء pickle)
امنیت برای دادهٔ ناشناسخطرناکایمن‌ترمعمولاً ایمن‌ترمانند pickle (به‌خاطر استفاده از pickle)
توافق بین زبان‌هاضعیفخوبخوبمحلی

موارد کاربرد عملی

  • ذخیره مدل‌های یادگیری ماشینی برای استفادهٔ مجدد در همان محیط پایتون.
  • کَش کردن نتایج محاسبات گران‌قیمت در دیسک.
  • تبادل داده بین فرایندهای اجرایی در سیستم‌های کنترل‌شده.

نکات تکمیلی و توصیه‌ها

  • از pickle برای ذخیره‌سازی پایداری (long-term persistence) که به سازگاری بین نسخه‌های پایتون وابسته است، محتاط باشید.
  • همیشه از HIGHEST_PROTOCOL برای کارایی بهتر استفاده کنید مگر اینکه نیاز به پشتیبانی از نسخهٔ قدیمی باشد.
  • برای جابجایی داده‌ها بین زبان‌ها یا برای APIهای عمومی، از JSON یا پروتکل‌های باینری استاندارد استفاده کنید.
  • در صورت نیاز به قابلیت سریالایز کردن توابع یا انواع پیشرفته‌تر، پروژه‌هایی مانند dill را بررسی کنید، ولی آن‌ها هم هشدارهای امنیتی مشابهی دارند.

به طور خلاصه، pickle ابزاری بسیار مفید و سریع برای کار با اشیاء پایتون است اما به خاطر مسائل امنیتی و سازگاری نسخه باید با احتیاط و در محیط‌های قابل‌اعتماد مورد استفاده قرار گیرد.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: