Reputation: 603
What is the best way to set object variables that are set by a method? Is the methodology below where self.toppings
is first defined in __init__()
and then the value is set in the select_toppings()
method a good practice on how to set the self.toppings
variable? Please let me know if you have better suggestions. Thanks.
class Pizza:
def __init__():
self.toppings = None
def select_toppings(input):
self.toppings = input
Upvotes: 1
Views: 123
Reputation: 8190
The main question is: what happens when an honest programmer writes:
pizza = Pizza()
pizza.do_something()
and do_something()
needs self.toppings != None
?
Remember: the role of the constructor is to properly initialize the object. All mandatory fields should be initialized at the end of the construction.
If the initialization of the object gets too complex (I would say: as soon as the constructor does more than just assigning values to fields), use a creational pattern. Example with a factory:
class Pizza:
@staticmethod
def create(topping_list):
toppings = Toppings(topping_list)
return Pizza(toppings)
def __init__(self, toppings):
self._toppings = toppings
...
Or a builder:
class PizzaBuilder:
def __init__(self):
self._toppings = DEFAULT_TOPPINGS
self._size = DEFAULT_SIZE
def build(self):
return Pizza(self.toppings, self._size)
def toppings(self, toppings):
self._toppings = toppings
return self
def size(self, size):
self._size = size
return self
class Pizza:
def __init__(self, toppings, size):
self._toppings = toppings
self._size = size
You can create all combinations you need:
pizza1 = PizzaBuilder().build() # default pizza
pizza2 = PizzaBuilder().size(10).build() # big pizza with default toppings
pizza3 = PizzaBuilder().toppings(t1).build() # ...
An advantage of creational patterns is that your objects are fully initialized and are, in most cases, immutable. To put it in simple terms, setters and getters are separated and the design of your program gets cleaner.
Upvotes: 3
Reputation: 2061
Here is another way you can set using property
class Pizza:
def __init__(self):
self._toppings = None
def get_toppings(self):
print("get_toppings called!")
return self._toppings
def set_toppings(self, x):
print("set_toppings called!")
self._toppings = x
toppings = property(get_toppings, set_toppings)
margherita = Pizza()
margherita.set_toppings("mozzarella cheese")
print(margherita.get_toppings())
# outputs
mozzarella cheese
One pitfall of having no set
and get
method is this, I hope you can see how it causes a problem of changing the variables.
class Pizza:
def __init__(self, toppings, dough):
self.toppings = toppings
self.pizza = toppings + ', ' + dough
pz = Pizza('cheese', 'flour')
pz.toppings = "chicken"
print(pz.toppings)
print(pz.pizza)
# outputs
chicken
cheese, flour
Upvotes: 1
Reputation: 20500
You can pass toppings
directly to the constructor. (Don't use input as a variable name since it's a python builtin function)
class Pizza:
def __init__(self, toppings):
self.toppings = toppings
pz = Pizza('cheese')
print(pz.toppings)
The output will be cheese
Upvotes: 2