Override attribute access precedence having a data descriptor

天大地大妈咪最大 提交于 2019-12-12 01:24:34

问题


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__)

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

This is described in the docs:

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

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__)?


回答1:


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

How it works

  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

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



来源:https://stackoverflow.com/questions/30342212/override-attribute-access-precedence-having-a-data-descriptor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!