【问题标题】:Override attribute access precedence having a data descriptor覆盖具有数据描述符的属性访问优先级
【发布时间】:2015-08-01 06:14:49
【问题描述】:

我有一堆 MongoEngine 模型的实例。并且profiler显示很多时间都花在了MongoEngine模型字段的__get__方法上:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.066    0.066   26.525   26.525 entity.py:60(list)
     2198    0.277    0.000   25.260    0.011 ***.py:96(***)
    45603    0.822    0.000   24.832    0.001 ***.py:105(***)
   285055    2.732    0.000   21.417    0.000 fields.py:189(__get__)
   444491    2.643    0.000   17.476    0.000 dereference.py:12(__call__)

由于这些模型实例是只读的,我想让它们使用简单的 Python 数据类型。但我无法替换属性:

> .../course_cache.py(339)_patch_me_model_instance()
    338         import ipdb; ipdb.set_trace()
--> 339         return obj
    340 

{1890: 0.6, 1891: 0.4, 1892: 0.6, 1893: 0.4, 1894: 0.2, 1895: 0.8}
ipdb> pinfo obj.tasks
Type:        BaseDict
String form: {1890: 0.6, 1891: 0.4, 1892: 0.6, 1893: 0.4, 1894: 0.2, 1895: 0.8}
Namespace:   Locals
Length:      6
File:        .../local/lib/python2.7/site-packages/mongoengine/base/datastructures.py
Docstring:
A special dict so we can watch any changes

ipdb> obj.__dict__['tasks'] = dict(obj.tasks)
ipdb> pinfo obj.tasks
Type:        BaseDict
String form: {1890: 0.6, 1891: 0.4, 1892: 0.6, 1893: 0.4, 1894: 0.2, 1895: 0.8}
Namespace:   Locals
Length:      6
File:        .../local/lib/python2.7/site-packages/mongoengine/base/datastructures.py
Docstring:
A special dict so we can watch any changes

这在docs中有所描述:

如果一个实例的字典有一个与数据同名的条目 描述符,数据描述符优先。

但是有没有办法在不修补模型的情况下覆盖作为数据描述符的属性的优先级(删除描述符或添加__getattribute__)?

【问题讨论】:

    标签: python mongoengine


    【解决方案1】:

    这是我的解决方案,似乎可行:

    def _mock_me_instance(self, obj):
        """Patch a MongoEngine model instance to not use descriptors
        to access field values.
        """
        # copy class
        Model = type(obj.__class__.__name__, obj.__class__.__bases__,
                     dict(obj.__class__.__dict__))
        # add the original model to the base classes, so that isinstance() can work
        Model.__bases__ = (self.__class__,) + Model.__bases__
        # replace descriptor so that values from __dict__ can be seen
        for field_name in obj._fields:
            setattr(Model, field_name, None)
    
        obj.__dict__.update(obj.__dict__['_data'])
        obj.__class__ = Model  # replace the class
        return obj
    

    工作原理

    1. 我创建了该对象所在的类的副本。
    2. 将原始模型放在基类中,这样isinstanceissubclass就可以工作了。
    3. 我将数据复制到对象的__dict__。现在忽略这些值,因为我们有同名的数据描述符,它们覆盖了对数据的访问。
    4. 我将None 分配给类中的描述符(在我的例子中是MongoEngine 模型字段)。 None 不是描述符,因此现在可以看到来自对象的 __dict__ 的值。
    5. 我替换了对象的类

    速度增益约为 600%(原为 17 秒,现在为 3)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多