【问题标题】:Instantiate an object with a property of custom type using dictionary使用字典实例化具有自定义类型属性的对象
【发布时间】:2020-12-19 00:51:14
【问题描述】:

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

我定义了以下类:

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'>!

当我使用以下行更新 Employee 类时:

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 对象时提供它们的值。这就是我选择不采用上述方法的原因。

【问题讨论】:

标签: python dictionary constructor inner-classes


【解决方案1】:

我认为以下代码说明了如何避免错误。它修改了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')])

【讨论】:

    猜你喜欢
    • 2021-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-05
    • 2011-12-15
    相关资源
    最近更新 更多