• 8.17 创建不调用init方法的实例
    • 问题
    • 解决方案
    • 讨论

    8.17 创建不调用init方法的实例

    问题

    你想创建一个实例,但是希望绕过执行 init() 方法。

    解决方案

    可以通过 new() 方法创建一个未初始化的实例。例如考虑如下这个类:

    1. class Date:
    2. def __init__(self, year, month, day):
    3. self.year = year
    4. self.month = month
    5. self.day = day

    下面演示如何不调用 init() 方法来创建这个Date实例:

    1. >>> d = Date.__new__(Date)
    2. >>> d
    3. <__main__.Date object at 0x1006716d0>
    4. >>> d.year
    5. Traceback (most recent call last):
    6. File "<stdin>", line 1, in <module>
    7. AttributeError: 'Date' object has no attribute 'year'
    8. >>>

    结果可以看到,这个Date实例的属性year还不存在,所以你需要手动初始化:

    1. >>> data = {'year':2012, 'month':8, 'day':29}
    2. >>> for key, value in data.items():
    3. ... setattr(d, key, value)
    4. ...
    5. >>> d.year
    6. 2012
    7. >>> d.month
    8. 8
    9. >>>

    讨论

    当我们在反序列对象或者实现某个类方法构造函数时需要绕过 init() 方法来创建对象。例如,对于上面的Date来讲,有时候你可能会像下面这样定义一个新的构造函数 today()

    1. from time import localtime
    2.  
    3. class Date:
    4. def __init__(self, year, month, day):
    5. self.year = year
    6. self.month = month
    7. self.day = day
    8.  
    9. @classmethod
    10. def today(cls):
    11. d = cls.__new__(cls)
    12. t = localtime()
    13. d.year = t.tm_year
    14. d.month = t.tm_mon
    15. d.day = t.tm_mday
    16. return d

    同样,在你反序列化JSON数据时产生一个如下的字典对象:

    1. data = { 'year': 2012, 'month': 8, 'day': 29 }

    如果你想将它转换成一个Date类型实例,可以使用上面的技术。

    当你通过这种非常规方式来创建实例的时候,最好不要直接去访问底层实例字典,除非你真的清楚所有细节。否则的话,如果这个类使用了 slots 、properties 、descriptors 或其他高级技术的时候代码就会失效。而这时候使用 setattr() 方法会让你的代码变得更加通用。

    原文:

    http://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p17_create_instance_without_invoking_init_method.html