Python 中的懒加载模式

2 投票
4 回答
2614 浏览
提问于 2025-04-16 16:31

我有一些数据,格式是字典(来自MongoDB数据库,通过PyMongo获取),像这样:

car = {_id: "1", "color": "silver", "steering_wheel":"2"}

其中“steering_wheel”的值是我数据库中另一个文档的ID,这个文档代表了一个方向盘的实例。从数据库加载到Python后,会变成:

steering_wheel = {_id: "2", "cover": "plastic"}

为了处理这些数据,我使用Python类。现在,我的问题是关于懒加载的。我能想到两种方法:

1) 保留引用的ID,并创建一个只在运行时使用的属性,以便直接访问引用的对象:

class Car(object):
    def __init__(self, _id, color, steering_wheel_ref_id, session):
        self._id = _id
        self.color = color
        self.steering_wheel_ref_id = steering_wheel_ref_id
        self.session = session

    @property
    def steering_wheel(self):
        try:
            return self.steering_wheel
        except AttributeError:
            # Get the referecend object from the session 
            self.steering_wheel = self.session.resolve_item_refs(self.steering_wheel_ref_id)
            return self.steering_wheel

2) 另一种选择是进行类型检查:

class Car(object):
    def __init__(self, _id, color, steering_wheel, session):
        self._id = _id
        self.color = color
        self.steering_wheel = steering_wheel
        self.session = session

    @property
    def steering_wheel(self):
        if isinstance(self.steering_wheel, SteeringWheel):
            return self.steering_wheel
        else:
            # Get the referecend object from the session 
            self.steering_wheel = self.session.resolve_item_refs(self.steering_wheel_ref_id)
            return self.steering_wheel

你更喜欢哪种方式?或者有没有更好的方法或最佳实践来通过ID解决对引用的访问?

4 个回答

1

一般来说,我们是采用“先尝试后处理”的方式,但在某些情况下,这样做会造成重复,我觉得不太合适。

所以:

@property
def steering_wheel(self):
if not hasattr(self, 'steering_wheel'):
    self.steering_wheel = self.session.resolve_item_refs(self.steering_wheel_ref_id)
    # And while we're at it, maybe a bit of housekeeping?
    del self.steering_wheel_ref_id
return self.steering_wheel

说到重复... 如果我们要经常这样做,或许可以把这个逻辑放到一个单独的代理类里,这样更好:

class DatabaseProxy(object):
    def __init__(self, session, id):
        self.session = session
        self.id = id

    def __getattr__(self, what):
        if what == 'value':
            self.value = self.session.resolve_item_refs(self.id) # cache for next time
            return self.value
        raise AttributeError

class Car(object):
    def __init__(self, _id, color, steering_wheel_ref_id, session):
        self._id = _id
        self.color = color
        self.steering_wheel_proxy = DatabaseProxy(session, steering_wheel_ref_id)
        self.session = session

    @property
    def steering_wheel(self): return self.steering_wheel_proxy.value

大概就是这个意思。

1

如果让我在你们两个方法中选择一个,我会选第一个。因为它更符合Python的风格,利用了“鸭子类型”,这就是“Python的方式”。

第二个方法看起来更难读懂,也不太容易理解。

至于其他建议,抱歉我没有什么好主意。:-)

4

这个怎么样?

class Car(object):
    def __init__(self, _id, color, steering_wheel_ref_id, session):
        self._id = _id
        self.color = color
        self.steering_wheel_ref_id = steering_wheel_ref_id
        self.session = session
        self._steering_wheel = None

    @property
    def steering_wheel(self):
        if self._steering_wheel is None:
            # Get the referecend object from the session 
            self._steering_wheel = self.session.resolve_item_refs(self.steering_wheel_ref_id)
        return self._steering_wheel

撰写回答