python - How to generate and add an instance method to a class in runtime but compiling a method only once? -
i'm trying implement mixin adds method instance mangles name of base class's specific field name.
code looks this
class timeseriesobjectretrievermixin(object): """time series object retriever mixin. offers reverse related object retrieval methods mixed in model reverse relation contains time related fields(datefield, datetimefield). """ # todo: serious performance bottleneck due function code compilation every # instantiation. fix this. def __init__(self, *args, **kwargs): self._n = {} 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 1 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. must implement property in base class mix mixin model. example: time_seires = [ ('reverse_related_object_name', 'created_time'), ('votes', 'time'), ('cash_flow_rate', 'time'), ] """ return super(timeseriesobjectretrievermixin, self).time_series
so mixin add get_in_last_delta method every instantiated objects.
but causes lot of overhead since compilation processed every object instantiation (_add_get_in_last_delta implemented exec , compile).
is there nice way add runtime generated instance method class in program's first class definition rather class instantiation?
_add_get_in_last_delta() :
def _add_method(self, fn): 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 function name. fn = 'get_{}_in_last_delta'.format(time_series_object_name) # generate 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 method instance. self._add_method(fn)
p.s. i'm trying in django mixin class model.
i don't see need compile code. you're doing in dynamically-defined function can done via normal python.
from django.utils import timezone types import methodtype def _add_get_in_last_delta(self, time_series_object_name, time_series_field_name): # generate 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 method instance. setattr(self, fn, methodtype(dynamic_method, self))
the function still executed every instantiation, inner func compiled once @ first import. (the object/field names provided via default kwargs, because of way python closures work, can safely ignored calling resulting method.)