【问题标题】:Copy a slot object into a non-slot将槽对象复制到非槽中
【发布时间】:2019-03-29 10:32:00
【问题描述】:

一些python标准类是槽,比如datetime.datetime。这不是我能改变的,很多库都期望datetime 对象。

我想更改现有datetime 对象的默认__format__ 方法,但不幸的是,由于这是一个插槽类,因此被禁止:

In [10]: import datetime

In [11]: datetime.datetime.now().__format__ = lambda s, f: ''
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-11-c98141136d9d> in <module>()
----> 1 datetime.datetime.now().__format__ = lambda s, f: ''

AttributeError: 'datetime.datetime' object attribute '__format__' is read-only

是否可以滥用 python 的动态特性来实现这一点?我想是的。

【问题讨论】:

    标签: python slots


    【解决方案1】:

    这是我的解决方案:

    def make_extendable(o):
        """
        Return an object that can be extended via its __dict__
        If it is a slot, the object type is copied and the object is pickled through
        this new type, before returning it.
    
        If there is already a __dict__, then the object is returned.
        """
        if getattr(o, "__dict__", None) is not None:
            return o
    
        # Now for fun
        # Don't take care of immutable types or constant for now
        import copy
        import copyreg
    
        cls = o.__class__
        new_cls = type(cls.__name__, (cls,), {"__module__": cls.__module__})
        # Support only Python >= 3.4
        pick = o.__reduce_ex__(4)
        if pick[0] == cls:
            # This is the case for datetime objects
            pick = (new_cls, *pick[1:])
        elif pick[0] in (copyreg.__newobj__, copyreg.__newobj_ex__):
            # Now the second item in pick is (cls, )
            # It should be rare though, it's only for slots
            pick = (pick[0], (new_cls,), *pick[2:])
        else:
            return ValueError(f"Unable to extend {o} of type {type(o)}")
    
        # Build new type
        return copy._reconstruct(o, None, *pick)
    

    它基本上做了以下事情:

    1. 测试对象是否已经有__dict__。在这种情况下,没有什么可做的。
    2. 根据提供的对象类型创建一个新类型。这个新类型不是槽类,尽量模仿基类。
    3. copy.copy 中所做的那样减少提供的对象,但为简单起见仅支持__reduce_ex__(4)
    4. 修改缩减版以使用新创建的类型。
    5. 使用修改后的简化版本取消腌制新对象。

    datetime 的结果:

    In [13]: d = make_extendable(datetime.datetime.now())
    
    In [14]: d
    Out[14]: datetime(2019, 3, 29, 11, 24, 23, 285875)
    
    In [15]: d.__class__.__mro__
    Out[15]: (datetime.datetime, datetime.datetime, datetime.date, object)
    
    In [16]: d.__str__ = lambda: 'Hello, world'
    
    In [17]: d.__str__()
    Out[17]: 'Hello, world'
    

    注意事项

    随机顺序:

    • 某些类型可能无法减少。
    • 返回的对象是一个副本,而不是初始对象。
    • 类不一样,但isinstance(d, datetime.datetime) 将是True
    • 类层次结构将背叛黑客。
    • 它可能非常慢。
    • __format__ 有点特殊,因为how format works需要修改类实例,而不是绑定方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-05
      • 1970-01-01
      • 2019-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多