2016-04-28 126 views
2

我有一个根类,College至极拥有许多Course为什么JSONDecoder直观地将json字符串反序列化为Python对象?

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 + "])" 

现在我想保存/从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)]) 

但我不明白我的'object_hook'函数如何知道如何反序列化子类。 我应该保持这种方式还是扩大自定义反序列化?我怎么能阻止我的from_json函数默认行为?

回答

3

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

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

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() 
+0

谢谢!这很有帮助! –

相关问题