Reputation: 1124
I have data in form of a dict (coming from a MongoDB database via PyMongo), like:
car = {_id: "1", "color": "silver", "steering_wheel":"2"}
where the value of "steering_wheel" is the id of another document in my database which represents an instance of a class SteeringWheel. Loaded from the DB to Python would result in:
steering_wheel = {_id: "2", "cover": "plastic"}
To work with the data I use Python classes. Now, my question is about lazy loading. I can think of two ways:
1) Keep the referenced id and create another runtime only attribut for accessing the referenced objects directly:
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) The other option would be to perform a type check:
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
Which way would you prefer? Or are there better ways / best practices for resolving access to references by id?
Upvotes: 2
Views: 2673
Reputation: 10415
How about this?
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
Upvotes: 4
Reputation: 61607
While in general it's EAFP, this doesn't apply where it creates redundancy, IMO.
So:
@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
Speaking of redundancy... if we're going to be doing this a lot, maybe we should encapsulate this logic in its own Proxy class:
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
Something like that.
Upvotes: 1
Reputation: 33726
The first option which uses duck typing and exception handling is preferred. This follows the "Easier to ask for forgiveness than permission" (aka EAFP) methodology that is favored in Python.
Part of the reasoning for this is that instead of baking in the name of the type/class into your code, you're just trying to perform an operation and handling an expected error. This allows you to code for BEHAVIOR over IDENTITY.
Further reading:
Upvotes: 0
Reputation: 957
If I had to choose between one of your two approaches, I'd go with the first. It's more pythonic in the sense you make use of duck-typing, which is "the Python way".
The second one is harder to read and understand.
As for other suggestions, sorry but I got nothing. :-)
Upvotes: 1