Python之Shelve使用详解

shelve 是一种持久化的类似字典的对象。

引用

shelve 是一种持久化的类似字典的对象。 与 dbm 数据库的区别在于 shelve中的值(不是键!)实际上可以为任意 Python 对象 --- 即pickle 模块能够处理的任何东西。 这包括大部分类实例、递归数据类型,以及包含大量共享子对象的对象。 键则为普通的字符串。

重点:shelvekey要求必须是字符串,value则可以是任意合法的python数据类型。

更多的详情,请查看官方文档:shelve

定义

Shelve是对象持久化保存方法,将对象保存到文件里面,缺省(即默认)的数据存储文件是二进制的。

用途

可以作为一个简单的数据存储方案。

用法

使用时,只需要使用open函数获取一个shelf对象,然后对数据进行增删改查操作,在完成工作、并且将内存存储到磁盘中,最后调用close函数变回将数据写入文件。其对象支持字典所支持的大多数方法和运算(除了拷贝、构造器以及 | 和 |= 运算符)。

方法

  1. shelve.open(filename, flag='c', protocol=None, writeback=False):创建或打开一个shelve对象。shelve默认打开方式支持同时读写操作。

    1. filename:是关联的文件路径

    2. flag:可选参数,默认为c,如果数据文件不存在,就创建,允许读写;可以是:

      r: 只读;

      w: 可读写;

      n: 每次调用open()都重新创建一个空的文件,可读写。

    3. writeback:默认为False。当设置为True以后,shelve对象将为所有访问过的条目保留缓存并在close()sync()时将它们写回到DB

      优点:减少了出错的概率,对持久化字典中可变条目的修改更方便。

      缺点:如果访问的条目很多,这会消耗大量内存作为缓存,并会使得关闭操作变得非常缓慢,因为所有被访问的条目都需要写回到字典(无法确定被访问的条目中哪个是可变的,也无法确定哪个被实际修改了)。

  2. shelve.close():同步并关闭shelve对象。注意:每次使用完毕,都必须确保shelve对象被安全关闭。同样可以使用 with shelve.open() 作为上下文管理器

代码示例

# -*- coding: utf-8 -*-
import shelve


def return_msg(code=1, msg="ok", data=None):
    """
    返回数据
    """
    return {
        "code": code,
        "msg": msg,
        "data": data,
    }


class ShelveHelp(object):
    """
    shelve简单的使用
    """
    def __init__(self, filename, flag="c"):
        self.filename = filename
        self.flag = flag

    def write(self, **kwargs):
        """
        写入数据
        执行代码之后会生成三个文件,分别是:{name}.bak     {name}.dat     {name}.dir
        """
        with shelve.open(filename=self.filename, flag=self.flag) as db:
            for k, v in kwargs.items():
                db[k] = v
        return return_msg()

    def get_data_keys(self):
        """
        获取数据key列表
        """
        with shelve.open(filename=self.filename, flag=self.flag) as db:
            keys = list(db.keys())
            return return_msg(data=keys)

    def read(self):
        """
        读取数据
        """
        with shelve.open(filename=self.filename, flag=self.flag) as db:
            keys = list(db.keys())
            result = {}
            for k in keys:
                result[k] = db[k]
            return return_msg(data=result)

    def trash(self, trash_key):
        """
        删除指定key数据
        """
        with shelve.open(filename=self.filename, flag=self.flag) as db:
            keys = list(db.keys())
            if trash_key not in keys:
                return return_msg(-1, trash_key + ":not exist")
            del db[trash_key]
            return return_msg()

    def update(self, clear_all=False, **kwargs):
        """
        更新数据
        clear_all=True, 清除前面数据再写入,False,追加数据,相同key时会进行覆盖旧数据
        """
        with shelve.open(filename=self.filename, flag=self.flag) as db:
            if clear_all:
                keys = list(db.keys())
                for k in keys:
                    del db[k]
            if len(kwargs) > 0:
                for k, v in kwargs.items():
                    db[k] = v
            return return_msg()


def run():
    cls = ShelveHelp("test", flag="c")

    def simple_write():
        return cls.write(**{'h': 8888888, 'name': 99999, 'list': [1, 2, 3, 4, 5]})

    def simple_read():
        return cls.read()

    def simple_trash(key):
        return cls.trash(key)

    def simple_update():
        d = {
            "test": "test",
            "trash": "trash",
            "dfdfdfd": "dfdfdfd",
            "list": [1, 3, 4]
        }
        return cls.update(False, **d)

    # 写入
    # res = simple_write()

    # 读取
    res = simple_read()

    # 废弃
    # res = simple_trash("name")

    # 更新
    # res = simple_update()
    print(res)


def test_writeback():
    """
    测试回写功能(writeback=True)
    shelve访问己有key时,实际上取出的是数据源给出的一份拷贝,所以对于拷贝做出的增加和删除等操作都需要用writeback=True参数才能实现写入回文件中进行修改
    """
    with shelve.open(filename="hello", flag="c", writeback=True) as db:
        db['list'] = [1, 2, 3]

        # append
        # 当writeback=False, 对数据进行修改只是在内存中的修改,修改后的数据并没有被真正写入到文件中。
        db['list'].append(4)

        # remove
        db['list'].pop(1)

        print(db['list'])


if __name__ == '__main__':
    run()