Bo Peng
Bo Peng

Reputation: 603

How best to create object variables from a method

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

Answers (3)

jferard
jferard

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

Axois
Axois

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

Devesh Kumar Singh
Devesh Kumar Singh

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

Related Questions