Reputation: 110257
I have several methods that are similar to the following:
@property
def tpr_num_days(self):
"""
The duration, in days, of the TPR interval.
"""
if not self.prices:
raise RuntimeError("Must initialize self.prices first.")
# do something
@property
def revenue_during_tpr_is_greatest(self):
"""
Tells us if the Revenue that was generated during the TPR was both greater
than the revenue that was generated in the same-duration interval before the TPR and after the TPR.
"""
if not self.prices:
raise RuntimeError("Must initialize self.prices first.")
# do something
def build_pricing_data(self):
"""
This will build a list of pricing objects.
Note that we are currently excluding "returns" because the price of the returned
item may be different than the current price of the product, which could open up
a can of worms trying to track down when that item was purchased.
"""
cursor.execute('''SELECT
date,
max(if(offer_code='sdbuy', price, null)) sd_buy_price,
max(if(offer_code='hdbuy', price, null)) hd_buy_price,
max(if(offer_code='sdrent', price, null)) sd_rent_price,
max(if(offer_code='hdrent', price, null)) hd_rent_price,
sum(revenue) revenue
FROM price_date WHERE apple_id=%s and territory=%s and revenue > 0
GROUP BY date
ORDER BY date
''', (self.apple_id, self.territory))
# let's make sure it's re-initialized in case it's called multiple times.
self.prices = []
for row in cursor:
pricing_obj = {"date": item[0], "SDBUY": item[1], "HDBUY": item[2], "SDRENT": item[3], "HDRENT": item[4], "REVENUE": item[5]}
self.prices.append(pricing_obj)
Instead of having this if
statement at the start of dozens of methods, what would be a better way to encapsulate that?
Upvotes: 2
Views: 56
Reputation: 43196
You could use a function decorator:
import functools
def requires_price(func):
@functools.wraps(func)
def wrapper(obj, *args, **kwargs):
if not obj.prices:
raise RuntimeError("Must initialize self.prices first.")
return func(obj, *args, **kwargs)
return wrapper
Which would be used like this:
@property
@requires_price
def tpr_num_days(self):
... # do something
Or you could go a step further and implement the decorator as a descriptor, which would let you omit the @property
:
class price_property:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
if not instance.prices:
raise RuntimeError("Must initialize self.prices first.")
return self.func(instance)
Which is used like this:
@price_property
def tpr_num_days(self):
... # do something
Upvotes: 2
Reputation: 781096
You can add a method to do the check.
def validate_prices(self):
if not self.prices:
raise RuntimeError("Must initialize self.prices first.")
and then put self.validate_prices()
at the beginning of each function.
It only saves one line, but you don't have to repeat the same error message every time.
If you want something more automatic, you'd probably have to define a metaclass that adds this code to every property method.
Upvotes: 2