如何使用实用程序模块而不重复不必要的函数调用?

时间:2018-03-09 12:27:50

标签: python class

我有一个实用程序模块,用于向其他脚本提供数据。我无法理解利用它的最佳方式,同时最大限度地减少函数调用的数量(为了论证,这些都是缓慢的)。

它看起来像这样:

helper.py

dataset1 = slow_process1()
dataset2 = slow_process2()

def get_specific_data1():
    data = #do stuff with dataset1
    return data

def get_specific_data2():
    data = #do stuff with dataset1
    return data

def get_specific_data3():
    data = #do stuff with dataset2
    return data

现在,我需要在脚本中运行get_specific_data1。在上面的设置中,我导入了模块,这意味着我在导入时不必要地调用slow_process2

如果我嵌套dataset1dataset2的作业,但需要在同一个脚本中调用get_specific_data1get_specific_data2,我会运行slow_process1两次,这也是不必要的。

如果我使用Helper函数的方法创建get_specific_data类,如果需要,它运行slow_process1slow_process2,存储数据,然后可以根据需要访问当调用方法时,我可以解决这个问题。这是合适的吗?

类似的东西:

class Helper:
    def __init__(self):
        self.dataset1 = None
        self.dataset2 = None

    def run_dataset1():
        self.dataset1 = slow_process1()

    def run_dataset2():
        self.dataset2 = slow_process2()

    def get_specific_data1():
       if dataset1 is None:
           self.rundataset1()
       data = #do stuff with dataset1
       return data

    etc

道歉,如果这是一个愚蠢的问题,但我对OOP的经验有限,并且不想在前面犯错误。

由于

2 个答案:

答案 0 :(得分:1)

您可以使用延迟加载技术解决此问题:

dataset1 = None
dataset2 = None

def ensureDataset1():
    global dataset1
    if dataset1 is None:
        dataset1 = slow_process1()

def ensureDataset2():
    global dataset2
    if dataset2 is None:
        dataset2 = slow_process2()

def get_specific_data1():
    ensureDataset1()
    data = #do stuff with dataset1
    return data

etc

这里的副作用是,如果你永远不会去检查dataset1dataset2,他们永远不会加载。

答案 1 :(得分:1)

这就是我使用properties类的意思,只是在这种情况下,我使用了名为Added support for more in depth searching的自定义版本。它被认为是"懒惰"因为只有当它被访问时才会被计算,就像常规的lazyproperty一样,但与它们不同的是,计算出的值有效地被高速缓存,从而将其变为实例属性 - 因此它赢得了每次都要重新计算。

警告:这样做假设无论何时计算该值都是相同的,并且在第一次访问后对其进行的任何更改将对同一实例的其他方法可见。使用它的类 - 即他们不会看到新重新计算的值。

完成此操作后,类中的方法可以引用propertyself.dataset1,就像它们是常规实例属性一样,然后,如果它是第一次,则数据将计算与之关联的值,否则将仅返回先前创建的值的值。您可以在生成的输出中看到这种情况(如下所示)。

self.dataset2

输出:

# From the book "Python Cookbook" 3rd Edition.
class lazyproperty:
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            return value

def slow_process1():
    print('slow_process1() running')
    return 13

def slow_process2():
    print('slow_process2() running')
    return 21

class Helper:
    def __init__(self):
        """ Does nothing - so not really needed. """
        pass

    @lazyproperty
    def dataset1(self):
        return slow_process1()

    @lazyproperty
    def dataset2(self):
        return slow_process2()

    def process_data1(self):
       print('self.dataset1:', self.dataset1)  # doing stuff with dataset1
       return self.dataset1 * 2

    def process_data2(self):
       print('self.dataset2:', self.dataset2)  # doing stuff with dataset2
       return self.dataset2 * 2

    def process_data3(self):
       print('self.dataset2:', self.dataset2)  # also does stuff with dataset2
       return self.dataset2 * 3

if __name__ == '__main__':

    helper = Helper()
    print(helper.process_data1())  # Will cause slow_process1() to be called
    print(helper.process_data2())  # Will cause slow_process2() to be called
    print(helper.process_data3())  # Won't call slow_process2() again