覆盖具有数据描述符

时间:2015-05-20 06:47:18

标签: python mongoengine

我有一堆MongoEngine模型的实例。分析器显示在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__)?

1 个答案:

答案 0 :(得分: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. 我替换了对象的类
  6. 速度增益约为600%(17秒,现在为3)。