【问题标题】:Pythonic way of computing optional instance variables when first called首次调用时计算可选实例变量的 Pythonic 方式
【发布时间】:2021-06-07 08:40:30
【问题描述】:

我正在编写一个类来表示来自航天器的一些数据,这些数据可以提供许多不同的数据产品,这些产品可用于计算其他变量。加载每个变量的原始数据需要几秒钟的时间,因此每个变量只应在需要时加载,因为并非总是需要所有变量。

这是我的意思的最小工作示例:

class Spacecraft:
    def __init__(self, time_range, configuration='default'):
        self.time_range = time_range

        # Initialise all possible variables to None
        self.magnetometer = None
        self.temperature = None
        self.complicated_thing = None
        # There are 30+ of these

    def get_magnetometer(self):
        self.magnetometer = self.load_data('magnetometer')
        return self.magnetometer

    def get_temperature(self):
        self.temperature = self.load_data('temperature')
        return self.temperature

    def get_complicated_thing(self):
        # Check each variable has a value
        if self.temperature is None:
            get_temperature()
        if self.magnetometer is None:
            get_magnetometer()
        
        self.complicated_thing = self.temperature * self.magnetometer
        return self.complicated_thing


    # Plus more get_ functions for each instance variable

    def load_data(self, product_name):
        """Loads raw 'product_name' data as np array.
        """
        pass


# Use case 1: Only need limited number of parameters
only_need_magnetometer = Spacecraft('10:30-10:45')
plot(only_need_magnetometer.get_magnetometer())


# Use case 2: Need a different set of parameters
complicated_analysis = Spacecraft('10:45-11:00')
result = analysis(complicated_analysis.get_complicated_thing())

我不确定将__init__() 中的每个变量初始化为None。我也不喜欢在计算派生变量的每个函数的开头进行冗长的检查。

有没有更好/更pythonic的方式?

理想情况下,我想跳过对None 的初始化并能够将get_complicated_thing() 定义为:

def get_complicated_thing(self):
    self.complicated_thing = self.temperature * self.magnetometer
    return self.complicated_thing

如果self.temperatureself.magnetometer 尚不存在,则会自动调用getter,例如get_magnetometer()。 然后可以将用例简化为:

# Use case 1: Only need limited number of parameters
only_need_magnetometer = Spacecraft('10:30-10:45')
plot(only_need_magnetometer.magnetometer)


# Use case 2: Need a different set of parameters
complicated_analysis = Spacecraft('10:45-11:00')
result = analysis(complicated_analysis.complicated_thing)

至少对我来说,这更容易阅读。

【问题讨论】:

  • 为什么在初始化类实例时不想将所有变量加载到属性中?

标签: python python-3.x class oop properties


【解决方案1】:

确实有一个可以使用的构造:property。有一些装饰器允许为托管属性定义getterssetters和“析构函数”:

在你的情况下,你只需要吸气剂:

class MyClass:
    def __init__(self, *args, **kwargs):
        # do stuff

    @property
    def magnometer(self):
        # do heavy calculation/loading
        return result


    @property
    def complicated_thing(self):
        # we can access self.mangometer here (it will just run the magnometer bound function)
        return self.magnometer*2

现在您说计算/加载操作很繁重。缓存计算值很容易,如下所示:

from functools import cache
...
    @property
    @cache
    def myproperty(self):
        return 42
...

但是请注意,cache 是在 3.9 中引入的,Python 3.8 中有一个 cached_property 可以用作链式装饰器的替代品。或者,您可以按照this thread 的行编写自己的缓存。

补充说明:在__init__中设置属性为None原则上没有错。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多