使用字典实例化具有 属性 自定义类型的对象

Instantiate an object with a property of custom type using dictionary

我的设计中还有其他一些嵌套的 classes,我正在使用字典实例化一个对象,该字典本身是从 JSON 文件转换而来的。在 JSON 文件中,我有嵌套关系,例如 EmployeeEducation 列表(1 到多)。为了简单起见,我用下面的例子来说明我的问题:

我定义了以下 classes:

def ensure_type(value, types):
    if (isinstance(value, list)): # when value is a list
        for element in value:
            ensure_type(element, types)
        return value
    elif isinstance(value, dict):
        for k,v in value.items(): # when value is a dict
            ensure_type(v, types)
        return value
    elif isinstance(value, types):
        return value
    else:
        raise TypeError('Value {value} is {value_type}, but should be {types}!'.format(value=value, value_type=type(value), types=types))


class Education:
    def __init__(self, **kwargs):
        print('im here')
        self.school_name = ensure_type(kwargs.get('school_name'), str)


class Employee:
    def __init__(self, **kwargs):
        self.fname = ensure_type(kwargs.get('fname'), str)
        self.education = ensure_type(kwargs.get('education'), Education)

我使用我的自定义函数 ensure_type.

验证 type

我想使用以下方法实例化一个员工:

if __name__ == "__main__":

    emp_dict = {'fname': 'Bob', 'education': [{'school_name':'foo'}, {'school_name':'bar'}]}
    
    employee1 = Employee(**emp_dict)

当我尝试上述方法时,出现以下错误:

  File "test.py", line 32, in <module>
    employee1 = Employee(**emp_dict)
  File "test.py", line 26, in __init__
    self.education = ensure_type(kwargs.get('education'), Education)
  File "test.py", line 5, in ensure_type
    ensure_type(element, types)
  File "test.py", line 9, in ensure_type
    ensure_type(v, types)
  File "test.py", line 14, in ensure_type
    raise TypeError('Value {value} is {value_type}, but should be {types}!'.format(value=value, value_type=type(value), types=types))
TypeError: Value foo is <class 'str'>, but should be <class '__main__.Education'>!

当我使用以下行更新员工 class 时:

self.education = Education(kwargs.get('education'))

我收到以下错误:

Traceback (most recent call last):
  File "testt.py", line 32, in <module>
    employee1 = Employee(**emp_dict)
  File "testt.py", line 26, in __init__
    self.education = Education(kwargs.get('education'))
TypeError: __init__() takes 1 positional argument but 2 were given

如果你能指导我如何解决这个问题,我将不胜感激。

注意: 最初我定义了类似于以下的构造函数:

    def __init__(self, iterable=(), **kwargs):
        self.__dict__.update(iterable, **kwargs)

这是一种非常强大和灵活的方法,但是 我需要一些(或全部)属性是 required 即,用户必须在实例化时提供它们的值一个 Employee 对象。这就是我选择不采用上述方法的原因。

我认为以下代码说明了如何避免该错误。它修改 ensure_type() 函数,因此如果列表的元素不是正确的类型 (kind),它会执行进一步检查以查看该元素是否是一个 dict 可以用于构造类型的实例。它还会用创建的实例替换字典,但您是否希望发生这种情况尚不清楚。

如果您希望字典发生类似的事情,您需要对字典中的每个值做一些非常相似的事情。

注意: 代码至少需要 Python 3.8,因为它使用了 := assignment expression(又名“海象运算符”).

def ensure_type(value, kind):

    if (isinstance(value, list)):
        # Make sure elements of list are instances of kind or can be
        # used to create an instance of one.
        for i, element in enumerate(value):
            try:
                ensure_type(element, kind)
            except TypeError:
                # Unless element is dict that can be used to create an
                # instance of kind.
                if(not isinstance(element, dict) or
                   not isinstance(inst := kind(**element), kind)):
                    raise
                else:
                    value[i] = inst  # Replace element with instance (OPTIONAL)
        return value

    elif isinstance(value, dict):
        for k,v in value.items():
            # Make sure the value of each item in dict is an instance of kind.
            ensure_type(v, kind)
        return value

    elif isinstance(value, kind):
        return value

    else:
        raise TypeError(
            'Value {value} is {value_type}, but should be {kind}!'.format(
                value=value, value_type=type(value), kind=kind))


class Printable:  # Added to print test results.
    """ Class which can print a represenation of itself. """
    def __repr__(self):
        typename = type(self).__name__
        args = ', '.join("%s=%r" % item for item in vars(self).items())
        return '{typename}({args})'.format(typename=typename, args=args)


class Education(Printable):
    def __init__(self, **kwargs):
#        print("I'm here")
        self.school_name = ensure_type(kwargs.get('school_name'), str)


class Employee(Printable):
    def __init__(self, **kwargs):
        self.fname = ensure_type(kwargs.get('fname'), str)
        self.education = ensure_type(kwargs.get('education'), Education)


if __name__ == "__main__":

    emp_dict = {'fname': 'Bob',
                'education': [{'school_name':'foo'}, {'school_name':'bar'}]}
    employee1 = Employee(**emp_dict)
    print(employee1)

输出:

Employee(fname='Bob', education=[Education(school_name='foo'), Education(school_name='bar')])