如何在运行时生成实例方法并将其添加到类中,但只编译一次方法?

时间:2015-04-23 06:59:11

标签: python django

我正在尝试实现一个mixin,它将一些方法添加到一个实例,该实例会破坏基类特定字段名称的名称。

代码看起来像这样

class TimeSeriesObjectRetrieverMixin(object):
"""Time Series Object Retriever Mixin.

Offers reverse related object retrieval methods to mixed in model where the
reverse relation contains time related fields(DateField, DateTimeField).
"""
# TODO: SERIOUS PERFORMANCE BOTTLENECK DUE TO FUNCTION CODE COMPILATION FOR EVERY
#       INSTANTIATION. FIX THIS.
def __init__(self, *args, **kwargs):
    self._n = {}

    for time_series_object_name, time_series_field_name in self.time_series:
        time_series_object = getattr(self, time_series_object_name)
        time_series_field = time_series_object.model._meta.get_field(time_series_field_name)

        # Time series field has to be one of DateField or DateTimeField.
        assert(isinstance(time_series_field, DateField) or
               isinstance(time_series_field, DateTimeField))

        self._add_get_in_last_delta(time_series_object_name,
                                    time_series_field_name)

 def time_series(self):
    """
    Time Series Property.

    You must implement this property in your base class to mix this
    mixin into your model.

    Example:
            time_seires =
            [
                ('reverse_related_object_name', 'created_time'),
                ('votes', 'time'),
                ('cash_flow_rate', 'time'),
            ]
    """
    return super(TimeSeriesObjectRetrieverMixin, self).time_series

因此,mixin将为每个实例化对象添加 get_in_last_delta 方法。

但这导致了很多开销,因为为每个对象实例化处理了编译( _get_in_last_delta 是用 exec <实现的/ strong>和 编译 )。

有没有什么好方法可以在程序的第一个类定义中向类中添加运行时生成的实例方法,而不是类实例化?

_add_get_in_last_delta():

def _add_method(self, fn):
    from types import MethodType
    setattr(self, fn, MethodType(self._n[fn], self))

def _add_get_in_last_delta(self,
                           time_series_object_name,
                           time_series_field_name):
    # Generate a function name.
    fn = 'get_{}_in_last_delta'.format(time_series_object_name)

    # Generate a name mangled function code.
    exec compile((
        'from django.utils import timezone\n'
        'def {}(self, delta):\n'
        '   return self.{}.filter({}__gte=(timezone.now()-delta))').
        format(fn, time_series_object_name, time_series_field_name),
        '<string>', 'exec') in self._n

    # Bind the method to the instance.
    self._add_method(fn)

P.S。我在django中尝试将这个类混合到我的模型中。

1 个答案:

答案 0 :(得分:0)

我认为没有必要使用该编译代码。你在动态定义的函数中所做的一切都可以通过普通的Python来完成。

from django.utils import timezone
from types import MethodType

def _add_get_in_last_delta(self,
                           time_series_object_name,
                           time_series_field_name):
    # Generate a function name.
    fn = 'get_{}_in_last_delta'.format(time_series_object_name)

    def dynamic_method(self, delta, obj=time_series_object_name, field=time_series_field_name):
        return getattr(self, time_series_object_name).filter(
            **{'{}__gte'.format(time_series_field_name): timezone.now()-delta})

    # Bind the method to the instance.
    setattr(self, fn, MethodType(dynamic_method, self))

该函数仍将在每次实例化时执行,但内部函数仅在首次导入时编译一次。 (对象/字段名称是通过默认的kwargs提供的,因为Python closures work的方式,但是任何调用结果方法的人都可以安全地忽略它们。)