如何使Python反序列化功能可用于其他模块?

时间:2016-02-06 13:26:07

标签: python serialization scope

我有一些序列化和反序列化代码,它们在一个模块中很好地工作(并且在我关心的类的范围内)。 E.g。

class A(...):
    ...

class B(...):
    ...

def serialize( obj ):
    ...

def deserialize( string ):
    ...

assert( obj == deserialize( serialize( obj ) ) )

但是,如果我在另一个模块中定义一个类并在那里导入serializedeserialize,序列化可以正常工作,但我遇到反序列化问题。反序列化功能不会知道"关于其他模块中的类定义,并且不能实例化实例。

到目前为止,我已经使用了两种不同的方法来解决这个问题。一种方法是将globals()locals()传递给deserialize,然后将eval与他们联系起来。另一个人在__name__deserialize致电inspect.getmembers( sys.modules[ passedInName ] )时传递来电。我还需要在deserialize可见的某个列表中注册每个可序列化的类。

但是,我不喜欢这些变通办法。还有更好的方法吗?

(如果重要的话我需要使用Python 2.7,因为这是我们工作的原因。)

ADDED:我已经编写了一个版本的"注册某些列表中的每个可序列化类"基于ABCMeta元类的策略似乎并不太可怕。它确实涉及查看感觉有点不对的_abc_registry

1 个答案:

答案 0 :(得分:0)

最后一次测试可能存在一个问题:

assert( obj == deserialize( serialize( obj ) ) )

对象将实际上相等"但是会有两个不同的实例 所有成员都一样。

您可以通过询问id(obj)值的对象来检查它,您将获得不同的值。

要解决此问题(如果您希望允许两个不同的实例相同),请将__eq__添加到您的 类。

我尝试使用JSON定义此类以及添加serializedeserialize 串。要表明,serializedeserialize函数可能存在于类之外的其他模块中, 它们被放入不同的模块中:

档案classroom.py

class A(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __repr__(self):
        templ = "<A: a:{self.a} b: {self.b}>"
        return templ.format(self=self)

    def __eq__(self, other):
        return (self.a == other.a) and (self.b == other.b)


class B(object):
    def __init__(self, c, d):
        self.c = c
        self.d = d

    def __repr__(self):
        templ = "<B: c:{self.c} d: {self.c}>"
        return templ.format(self=self)

    def __eq__(self, other):
        return (self.c == other.c) and (self.d == other.d)

档案serdeser.py

import json


def serialize(obj):
    if isinstance(obj, A):
        return json.dumps({"type": "A", "args": {"a": obj.a, "b": obj.b}})
    elif isinstance(obj, B):
        return json.dumps({"type": "B", "args": {"c": obj.c, "d": obj.d}})
    else:
        raise ValueError()


def deserialize(string):
    dct = json.loads(string)
    objtype = dct.get("type")
    if objtype == "A":
        return A(**dct["args"])
    elif objtype == "B":
        return B(**dct["args"])
    else:
        raise ValueError("Unknown type of data")

测试代码:test_ser.py

以下是我的测试(使用pytest)。我把它放到档案test_ser.py

import pytest


@pytest.fixture
def a_instance():
    from serdeser import A
    return A(11, 12)


@pytest.fixture
def a_serialized(a_instance):
    from serdeser import serialize
    return serialize(a_instance)


@pytest.fixture
def b_instance():
    from serdeser import B
    return B(88, 99)


@pytest.fixture
def b_serialized(b_instance):
    from serdeser import serialize
    return serialize(b_instance)


def test_A(a_instance, a_serialized):
    from serdeser import deserialize
    assert a_instance == deserialize(a_serialized)


def test_B(b_instance, b_serialized):
    from serdeser import deserialize
    assert b_instance == deserialize(b_serialized)

您应安装pytest

$ pip install pytest

运行测试(并假设文件serdeser.pyclassroom.py在 同一目录:

$ py.test -sv test_ser.py                                                              (env: stack) 
========================================= test session starts =========================================
platform linux2 -- Python 2.7.9 -- py-1.4.30 -- pytest-2.7.2 -- /home/javl/.virtualenvs/stack/bin/python2
rootdir: /home/javl/sandbox/stack, inifile: 
collected 2 items 

test_ser.py::test_A PASSED
test_ser.py::test_B PASSED

====================================== 2 passed in 0.01 seconds =======================================

一切都在传递给我。

如果从当前目录导入serdeser模块时遇到问题,请使用:

$ EXPORT PYTHONPATH="."

在我的测试中,我从不会将serdeser中的任何内容导入全局范围,只会导入测试用例范围 功能

对于具有类deserialize知识的导入函数A应该没有问题 在同一模块中定义。