覆盖具有数据描述符的属性访问优先级 [英] Override attribute access precedence having a data descriptor

查看:86
本文介绍了覆盖具有数据描述符的属性访问优先级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆MongoEngine模型的实例.分析器显示,MongoEngine模型字段的__get__方法中花费了大量时间:

I have a bunch of instances of a MongoEngine model. And the profiler shows that a lot of time is spent in __get__ method of MongoEngine model fields:

   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数据类型.但是我无法替换属性:

As these model instances are read-only I want to make them to use simple Python data types. But I am not able to replace attributes:

> .../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

这在文档中进行了描述:

如果实例的词典中的条目与数据名称相同 描述符,数据描述符优先.

If an instance’s dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence.

但是有没有一种方法可以覆盖数据描述符的属性优先级,而无需修补模型(删除描述符或添加__getattribute__)?

But is there a way to override precedence for the attributes which are data descriptors without patching the model (removing the descriptor or adding __getattribute__)?

推荐答案

这是我的可行的解决方案:

Here is my solution which seems to work:

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. 我替换了对象的类
  1. I create a copy of the class whose instance the object is.
  2. Put the original model in the base classes, so that isinstance and issubclass can work.
  3. I copy the data into the object's __dict__. The values are ignored for now, because we have data descriptors with the same name which override access to the data.
  4. I assign None to the descriptors in the class (MongoEngine model fields in my case). None is not a descriptor, so values from __dict__ of the object are seen now.
  5. I replace object's class

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

The speed gain is ~600% (was 17 seconds, now it's 3).

这篇关于覆盖具有数据描述符的属性访问优先级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆