覆盖具有数据描述符的属性访问优先级 [英] Override attribute access precedence having a data descriptor
问题描述
我有一堆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
工作方式
- 我创建了一个其对象为实例的类的副本.
- 将原始模型放在基类中,以便
isinstance
和issubclass
可以工作. - 我将数据复制到对象的
__dict__
中.这些值暂时会被忽略,因为我们有同名的数据描述符,它们会覆盖对数据的访问. - 我将
None
分配给类中的描述符(在我的情况下为MongoEngine模型字段).None
不是描述符,所以现在可以看到对象的__dict__
中的值. - 我替换了对象的类
- I create a copy of the class whose instance the object is.
- Put the original model in the base classes, so that
isinstance
andissubclass
can work. - 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. - 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. - I replace object's class
速度增益约为600%(原为17秒,现在为3).
The speed gain is ~600% (was 17 seconds, now it's 3).
这篇关于覆盖具有数据描述符的属性访问优先级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!