shelve --- ماندگاری اشیاء در پایتون

کد منبع: Lib/shelve.py


یک «قفسه» (shelf) شیء پایا و دیکشنری‌مانند است. تفاوت آن با پایگاه‌های داده «dbm» در این است که مقادیر (نه کلیدها!) در یک قفسه می‌توانند اساساً اشیاء پایتونی دلخواهی باشند — هر چیزی که ماژول pickle بتواند آن را مدیریت کند. این شامل بیشتر نمونه‌های کلاس، انواع داده بازگشتی و اشیایی می‌شود که حاوی تعداد زیادی زیرشیء مشترک هستند. کلیدها رشته‌های معمولی هستند.

shelve.open(filename, flag='c', protocol=None, writeback=False, *, serializer=None, deserializer=None)

یک دیکشنری پایا را باز می‌کند. نام پرونده مشخص‌شده، نام پرونده پایه برای پایگاه داده‌ی زیربنایی است. به‌عنوان اثر جانبی، ممکن است پسوندی به نام پرونده اضافه شود و بیش از یک پرونده ایجاد شود. به‌طور پیش‌فرض، پرونده پایگاه داده‌ی زیربنایی برای خواندن و نوشتن باز می‌شود. پارامتر اختیاری flag همان تفسیر پارامتر flag در dbm.open() را دارد.

به‌طور پیش‌فرض، پیکل‌های ایجادشده با pickle.DEFAULT_PROTOCOL برای سریال‌سازی مقادیر استفاده می‌شوند. نسخه‌ی پروتکل پیکل را می‌توان با پارامتر protocol مشخص کرد.

به دلیل معناشناسی پایتون، یک shelf نمی‌تواند بداند چه زمانی یک آیتم تغییرپذیر در دیکشنری پایا تغییر می‌کند. به‌طور پیش‌فرض، اشیای تغییرکرده فقط زمانی نوشته می‌شوند که به shelf انتساب داده شوند (به مثال مراجعه کنید). اگر پارامتر اختیاری writeback روی True تنظیم شود، همه آیتم‌های دسترسی‌یافته نیز در نهانگاه حافظه ذخیره می‌شوند و در sync() و close() بازنویسی می‌شوند؛ این موضوع می‌تواند تغییر دادن آیتم‌های تغییرپذیر در دیکشنری پایا را آسان‌تر کند، اما اگر به آیتم‌های زیادی دسترسی پیدا شود، می‌تواند حجم بسیار زیادی از حافظه را برای نهانگاه مصرف کند و عملیات close را بسیار کند سازد، زیرا همه آیتم‌های دسترسی‌یافته بازنویسی می‌شوند (هیچ راهی برای تشخیص اینکه کدام آیتم‌های دسترسی‌یافته تغییرپذیرند، و نه اینکه کدام‌یک واقعاً تغییر کرده‌اند، وجود ندارد).

به طور پیش‌فرض، shelve از pickle.dumps() و pickle.loads() برای سریال‌سازی و سریال‌زدایی استفاده می‌کند. این کار را می‌توان با ارائه‌ی serializer و deserializer به ترتیب تغییر داد.

آرگومان serializer باید یک شیء فراخوانی‌پذیر باشد که یک شیء obj و protocol را به عنوان ورودی دریافت کند و بازنمایی obj را به عنوان یک شیء شبه‌بایت برگرداند؛ ممکن است مقدار protocol توسط سریال‌ساز نادیده گرفته شود.

آرگومان deserializer باید یک شیء فراخوانی‌پذیر باشد که یک شیء سریال‌شده را که به عنوان یک شیء bytes داده شده است دریافت کند و شیء متناظر را برگرداند.

اگر serializer داده شود اما deserializer داده نشود یا برعکس، یک ShelveError بالا آورده می‌شود.

تغییر یافته در نسخه‌ی 3.10: pickle.DEFAULT_PROTOCOL اکنون به‌عنوان پروتکل پیش‌فرض pickle استفاده می‌شود.

تغییر یافته در نسخه‌ی 3.11: path-like object را برای نام پرونده می‌پذیرد.

تغییر یافته در نسخه‌ی 3.15: توابع serializer و deserializer سفارشی را به جای pickle.dumps() و pickle.loads() می‌پذیرد.

نکته

به بسته شدن خودکار قفسه (shelf) اتکا نکنید؛ همیشه وقتی دیگر به آن نیاز ندارید، close() را به‌صراحت فراخوانی کنید، یا از shelve.open() به‌عنوان مدیر زمینه استفاده کنید:

with shelve.open('spam') as db:
    db['eggs'] = 'eggs'

هشدار

از آن‌جا که ماژول shelve بر پایه pickle است، بارگذاری یک قفسه (shelf) از یک منبع غیرقابل‌اعتماد امن نیست. مانند pickle، بارگذاری یک قفسه (shelf) می‌تواند کد دلخواه را اجرا کند.

اشیای Shelf از بیشتر متدها و عملیات‌هایی که دیکشنری‌ها پشتیبانی می‌کنند، پشتیبانی می‌کنند (به‌جز کپی‌سازی، سازنده‌ها و عملگرهای | و |=). این کار گذار از اسکریپت‌های مبتنی بر دیکشنری به اسکریپت‌های نیازمند ذخیره‌سازی پایا را آسان می‌کند.

از دو متد اضافی پشتیبانی می‌شود:

Shelf.sync()

اگر قفسه با مقدار writeback تنظیم‌شده روی True باز شده باشد، تمام درایوهای موجود در نهانگاه را به عقب بازنویسی کنید. همچنین در صورت امکان، نهانگاه را خالی کرده و دیکشنری پایا را روی دیسک همگام‌سازی کنید. این کار به صورت خودکار هنگام فراخوانی reorganize() یا بسته شدن قفسه با close() انجام می‌شود.

Shelf.reorganize()

متد sync() را فراخوانی می‌کند و تلاش می‌کند با حذف فضای خالی ناشی از حذف‌ها، فضای استفاده‌شده روی دیسک را کاهش دهد.

اضافه شده در نسخه‌ی 3.15.

Shelf.close()

همگام‌سازی و بستن شیء dict پایا. عملیات روی یک shelf بسته با ValueError شکست خواهد خورد.

همچنین ببینید

دستور دیکشنری پایا با قالب‌های ذخیره‌سازی با پشتیبانی گسترده و دارای سرعت دیکشنری‌های بومی.

محدودیت‌ها

  • انتخاب بسته‌ی پایگاه داده‌ای که استفاده خواهد شد (مانند dbm.ndbm یا dbm.gnu) به این بستگی دارد که کدام رابط در دسترس باشد. بنابراین باز کردن پایگاه داده به‌صورت مستقیم با استفاده از dbm ایمن نیست. پایگاه داده نیز (متأسفانه) در صورت استفاده از dbm مشمول محدودیت‌های آن است — این بدان معناست که بازنمایی pickleشده (pickled representation) از اشیاء ذخیره‌شده در پایگاه داده باید نسبتاً کوچک باشد، و در موارد نادر ممکن است برخورد کلیدها باعث شود پایگاه داده از پذیرفتن به‌روزرسانی‌ها خودداری کند.

  • ماژول shelve از دسترسی همزمان خواندن/نوشتن به اشیای ذخیره‌شده در shelf پشتیبانی نمی‌کند. (چندین دسترسی خواندن همزمان بی‌خطر است.) هنگامی که برنامه‌ای یک shelf را برای نوشتن باز نگه داشته است، هیچ برنامه دیگری نباید آن را برای خواندن یا نوشتن باز نگه دارد. می‌توان از قفل‌گذاری پرونده در یونیکس برای حل این مسئله استفاده کرد، اما این رفتار در نسخه‌های مختلف یونیکس متفاوت است و به دانش درباره‌ی پیاده‌سازی پایگاه داده‌ی استفاده‌شده نیاز دارد.

  • در macOS، dbm.ndbm می‌تواند هنگام به‌روزرسانی‌ها بدون هشدار پرونده پایگاه داده را خراب کند، که این می‌تواند هنگام تلاش برای خواندن از پایگاه داده باعث فروپاشی‌های سخت شود.

  • ممکن است Shelf.reorganize() برای همه‌ی بسته‌های پایگاه داده در دسترس نباشد و هنگام فراخوانی ممکن است به طور موقت استفاده از منابع (به ویژه فضای دیسک) را افزایش دهد. علاوه بر این، هرگز به طور خودکار اجرا نمی‌شود و در عوض باید به طور صریح فراخوانی شود.

class shelve.Shelf(dict, protocol=None, writeback=False, keyencoding='utf-8', *, serializer=None, deserializer=None)

زیرکلاسی از collections.abc.MutableMapping که مقادیر پیکل‌شده را در شیء dict ذخیره می‌کند.

به‌طور پیش‌فرض، از پیکل‌های ایجادشده با pickle.DEFAULT_PROTOCOL برای سریال‌سازی مقادیر استفاده می‌شود. نسخه‌ی پروتکل پیکل را می‌توان با پارامتر protocol مشخص کرد. برای بحثی درباره‌ی پروتکل‌های پیکل، مستندات pickle را ببینید.

اگر پارامتر writeback برابر True باشد، شیء نهانگاهی از تمام آیتم‌های دسترسی‌شده را نگه می‌دارد و آن‌ها را در زمان sync و close به دیکشنری بازمی‌نویسد. این امر عملیات طبیعی روی آیتم‌های تغییرپذیر را ممکن می‌سازد، اما می‌تواند حافظه بسیار بیشتری مصرف کند و باعث شود sync و close زمان زیادی طول بکشند.

پارامتر keyencoding کدگذاری‌ای است که برای کدگذاری کلیدها پیش از استفاده از آن‌ها در دیکشنری زیرین به کار می‌رود.

پارامترهای serializer و deserializer همان تفسیری را دارند که در open() وجود دارد.

یک شیء Shelf همچنین می‌تواند به‌عنوان مدیر زمینه استفاده شود، که در این صورت با پایان یافتن بلوک with به‌طور خودکار بسته می‌شود.

تغییر یافته در نسخه‌ی 3.2: پارامتر keyencoding افزوده شد؛ پیش‌تر، کلیدها همیشه با UTF-8 کدگذاری می‌شدند.

تغییر یافته در نسخه‌ی 3.4: پشتیبانی از مدیر زمینه افزوده شد.

تغییر یافته در نسخه‌ی 3.10: pickle.DEFAULT_PROTOCOL اکنون به‌عنوان پروتکل پیش‌فرض pickle استفاده می‌شود.

تغییر یافته در نسخه‌ی 3.15: پارامترهای serializer و deserializer اضافه شدند.

class shelve.BsdDbShelf(dict, protocol=None, writeback=False, keyencoding='utf-8', *, serializer=None, deserializer=None)

زیرکلاسی از Shelf که متدهای first()، next()، previous()، last() و set_location() را ارائه می‌دهد. این موارد در ماژول شخص ثالث bsddb از pybsddb موجود هستند اما در سایر ماژول‌های پایگاه داده حضور ندارند. شیء dict ارسال‌شده به سازنده باید از این متدها پشتیبانی کند. این کار معمولاً با فراخوانی یکی از توابع bsddb.hashopen()، bsddb.btopen() یا bsddb.rnopen() انجام می‌شود. پارامترهای اختیاری protocol، writeback، keyencoding، serializer و deserializer همان تفسیری را دارند که در open() دیده می‌شود.

تغییر یافته در نسخه‌ی 3.15: پارامترهای serializer و deserializer اضافه شدند.

class shelve.DbfilenameShelf(filename, flag='c', protocol=None, writeback=False, *, serializer=None, deserializer=None)

زیرکلاسی از Shelf که به جای یک شیء دیکشنری‌مانند، یک filename را می‌پذیرد. پرونده‌ی زیربنایی با استفاده از dbm.open() باز خواهد شد. به طور پیش‌فرض، پرونده برای خواندن و نوشتن ایجاد و باز می‌شود. پارامتر اختیاری flag همان تفسیری را دارد که برای تابع open() در نظر گرفته شده است. پارامترهای اختیاری protocol، writeback، serializer و deserializer همان تفسیری را دارند که در open() وجود دارد.

تغییر یافته در نسخه‌ی 3.15: پارامترهای serializer و deserializer اضافه شدند.

مثال

برای جمع‌بندی رابط (key یک رشته است، data یک شیء دلخواه است):

import shelve

d = shelve.open(filename)  # open -- file may get suffix added by low-level
                           # library

d[key] = data              # store data at key (overwrites old data if
                           # using an existing key)
data = d[key]              # retrieve a COPY of data at key (raise KeyError
                           # if no such key)
del d[key]                 # delete data stored at key (raises KeyError
                           # if no such key)

flag = key in d            # true if the key exists
klist = list(d.keys())     # a list of all existing keys (slow!)

# as d was opened WITHOUT writeback=True, beware:
d['xx'] = [0, 1, 2]        # this works as expected, but...
d['xx'].append(3)          # *this doesn't!* -- d['xx'] is STILL [0, 1, 2]!

# having opened d without writeback=True, you need to code carefully:
temp = d['xx']             # extracts the copy
temp.append(5)             # mutates the copy
d['xx'] = temp             # stores the copy right back, to persist it

# or, d=shelve.open(filename,writeback=True) would let you just code
# d['xx'].append(5) and have it work as expected, BUT it would also
# consume more memory and make the d.close() operation slower.

d.close()                  # close it

استثناها

exception shelve.ShelveError

استثنایی که وقتی یکی از آرگومان‌های deserializer و serializer در open()، Shelf، BsdDbShelf و DbfilenameShelf وجود نداشته باشد، بالا آورده می‌شود.

آرگومان‌های deserializer و serializer باید با هم داده شوند.

اضافه شده در نسخه‌ی 3.15.

همچنین ببینید

ماژول dbm

رابط عام برای پایگاه‌های داده به سبک dbm.

ماژول pickle

سریال‌سازی اشیاء که shelve از آن استفاده می‌کند.