为什么JSONDecoder直观地将json String反序列化为Python对象?

时间:2016-04-28 14:27:19

标签: python json

我有一个根类,College包含许多Course

 class Course:

    def __init__(self, name, url, hidden, semester="SS16"):
        self.name = name
        self.url = url
        self.hidden = hidden
        self.semester = semester

    def __str__(self):
        return ('Course: (Name: %s, Semester: %s, url: %s, hidden: %s)' % (self.name, self.semester, self.url, str(self.hidden)))


class College:

    def __init__(self, url='http://dummy', courses=set()):
        self.url = url
        self.courses = courses

    def __str__(self):
        s = ('College: (url: %s, Courses:[' % (self.url))
        s += ', '.join(str(v) for v in self.courses)
        return s + "])"

现在我想将我的College Class保存/加载到JSON文件中。对于编码,我创建了一个自定义JSONEncoder类,它似乎工作正常:

from json import JSONEncoder
from json import JSONDecoder
class CollegeEncoder(JSONEncoder):

    def default(self, o):
        courses = list(map(lambda v: {'name': v.name, 'url': v.url,
                                      'hidden': v.hidden, 'semester': v.semester}, o.courses))
        return {'url': o.url, 'courses': courses}

为了解码,我写了一个简单的函数:

def from_json(dct):
    if 'url' in dct:
        return College(dct['url'])

现在,如果我像这样测试JSON编码/解码:

myCollege = College()
myCollege.courses.add(Course("Course1", "url1", False))
myCollege.courses.add(Course("Course2", "url2", False))
myCollege.courses.add(Course("Course3", "url3", False))

dump = CollegeEncoder().encode(myCollege)
college = JSONDecoder(object_hook=from_json).decode(dump)
print(college)

此时期待只获得一个简单的学院对象只有网址,但我得到了我的整个学院的所有课程和他们的属性

 College: (url: http://dummy, Courses:[Course: (Name: Course1, Semester: SS16,
 url: url1, hidden: False), Course: (Name: Course2, Semester: SS16, url: url2,
 hidden: False), Course: (Name: Course3, Semester: SS16, url: url3, hidden: False)])

但我不明白我的反对意见是怎样的。函数知道如何反序列化子类。 我应该保持这种方式还是扩展自定义反序列化?我怎么能阻止我的from_json函数默认以这种方式运行?

1 个答案:

答案 0 :(得分:4)

这不是JSON解码器;您在College.__init__方法中使用了可变默认参数

def __init__(self, url='http://dummy', courses=set()):

创建函数时,{strong>创建{strong>一次 courses默认值。然后,您可以为此添加值,基本上是全局集:

myCollege.courses.add(Course("Course1", "url1", False))

该集合永远不会被清空,因此当您创建新的College实例时,该集合仍然存在

>>> College.__init__.__defaults__
('http://dummy', {<__main__.Course object at 0x10f55cda0>, <__main__.Course object at 0x10f55cc50>, <__main__.Course object at 0x10f55ccf8>})
>>> foo = College()
>>> foo.courses
{<__main__.Course object at 0x10f55cda0>, <__main__.Course object at 0x10f55cc50>, <__main__.Course object at 0x10f55ccf8>}
>>> print(foo)
College: (url: http://dummy, Courses:[Course: (Name: Course3, Semester: SS16, url: url3, hidden: False), Course: (Name: Course1, Semester: SS16, url: url1, hidden: False), Course: (Name: Course2, Semester: SS16, url: url2, hidden: False)])

"Least Astonishment" and the Mutable Default Argument。改为将courses设置为哨兵,如果默认设置为哨兵,则在__init__方法中创建一个新集。 None在这里是个不错的选择:

class College:
    def __init__(self, url='http://dummy', courses=None):
        self.url = url
        self.courses = courses or set()