Reputation: 1145
Let's say I have the following class:
class House:
def __init__(self, estimated_cost):
# let's say states are planning, building, built and sold
self.state = "planning"
self.estimated_cost = estimated_cost
def hire_contractors():
# HIRE SOME CONTRACTORS
if self.state not in ("planning", "contractors_hired"):
raise ValueError("We cannot hire contracts if the house is not in planning or building phase!")
self.state = "contractors_hired"
def build_house():
# do stuff to build the house, NEED CONTRACTORS
if self.state != "contractors_hired":
raise ValueError("We cannot build a house without having contractors!")
self.state = "built"
def sell_house():
# sell house for 10% more than cost estimate
if self.state != "built":
raise ValueError("Need to be built before selling!")
print("Selling house for," self.estimated_cost*1.1)
where the following rules are enforced:
planning
or contractors_hired
statecontractors_hired
statebuilt
state.Otherwise errors are raised. Apologies if this is confusing I can add an example with a, b, c later. The current method "works" but seems unpythonic. Are there any better ways of having a state that allows certain methods to be called? This only needs to be enforced through error raising and/or warnings.
EDIT: I realize things are confusing, in reality I'm building an ML model which has something similar to:
class Model:
def set_hyperparams():
# Can be done always
def train():
# Can only be done once set_hyperparams
def predict():
# Can only be done once trained
Upvotes: 0
Views: 121
Reputation: 451
You could use decorators to make your code prettier. The requireState decorator takes a list of required states and checks if one of them is satisfied.
def requiresState(*states):
def decorator(func):
def wrapper(self, *args, **kwargs):
if not self.state in states:
raise ValueError("Requirements not satisfied")
return func(self, *args, **kwargs)
return wrapper
return decorator
class House:
def __init__(self, estimated_cost):
# let's say states are planning, building, built and sold
self.state = "planning"
self.estimated_cost = estimated_cost
@requiresState("planning", "contractors_hired")
def hire_contractors(self):
# HIRE SOME CONTRACTORS
self.state = "contractors_hired"
@requiresState("contractors_hired")
def build_house(self):
# do stuff to build the house, NEED CONTRACTORS
self.state = "built"
@requiresState("built")
def sell_house(self):
# sell house for 10% more than cost estimate
print("Selling house for,", self.estimated_cost*1.1)
Upvotes: 2
Reputation: 45
There doesn't seem to be anything un-pythonic in what you've got there...
The only design choice I'd recommend is to avoid using an enum state
member that will inevitably fail to represent all possible states a house can be in... but instead use conditions on members of the class:
class House():
def __init__(self, funds=0, contractors=0):
self.contractors = contractors
self.funds = funds
def add_funds(self, funds):
self.funds += funds
def hire_contractor(self, cost):
if self.funds < cost:
print(f"{self.funds} not sufficient; funds needed: {cost}")
return
self.funds -= cost
self.contractors += 1
def hire_contractors(self, *costs):
for cost in costs:
self.hire_contractor(cost)
def build(self, required_contractors=1):
if self.contractors < required_contractors:
print(f"please hire more contractors ({self.contractors} available)")
return
self.contractors -= required_contractors
Upvotes: 0