-1

I'm trying to learn object orientated programming and when setting an attribute why does it return None?

Any tips on writing better code also appreciated... This is my class:

BEANS = ["black", "pinto"]
RICES = ["brown", "white"]
MEATS = ["chicken", "pork", "steak", "tofu"]


class Burrito:

    def __init__(self,
                meat,
                to_go,
                rice,
                beans,
                extra_meat=False,
                guacamole=False,
                cheese=False,
                pico=False,
                corn=False
                ):

        self.meat = self.setter("meat",meat)
        self.rice = self.setter("rice",rice)
        self.beans = self.setter("beans",beans)
        self.to_go = False
        self.extra_meat = False
        self.guacamole = False
        self.cheese = False
        self.pico = False
        self.corn = False

    def setter(self,category,attribute):

        print("category: ",category)
        print("attribute: ",attribute)

        if category == "meat":
            
            if attribute in MEATS:
                self.meat = attribute
            else:
                self.meat = False

        if category == "rice":
            
            if attribute in RICES:
                self.rice = attribute
            else:
                self.rice = False

        if category == "beans":
            
            if attribute in BEANS:
                self.beans = attribute

            else:
                self.beans = False
                
        else:
            self.category = "Error"

When I run this I expected the print to be False but returning None

noodle_burrito = Burrito("spagetti", True, True, False)
print("noodle_burrito.meat: ",noodle_burrito.meat)

And when I run this I expected the print to be tofu but returning None

vegg_burrito = Burrito("tofu", True, True, False)
print("vegg_burrito.meat: ",vegg_burrito.meat)

Thanks for any learning tips! Trying to reference this other SO post but I have never used super before.

bbartling
  • 3,288
  • 9
  • 43
  • 88

2 Answers2

0

Your setter sets the attribute, but then returns None, which you assign to the attribute again, overwriting whatever the setter itself assigned. Just drop the assignment in __init__.

def __init__(self,
            meat,
            to_go,
            rice,
            beans,
            extra_meat=False,
            guacamole=False,
            cheese=False,
            pico=False,
            corn=False
            ):

    self.setter("meat",meat)
    self.setter("rice",rice)
    self.setter("beans",beans)
    self.to_go = False
    self.extra_meat = False
    self.guacamole = False
    self.cheese = False
    self.pico = False
    self.corn = False

However, there's no reason for the setter method at all. Just assign values directly to the attributes.

class Burrito:

    def __init__(self,
                meat,
                to_go,
                rice,
                beans,
                extra_meat=False,
                guacamole=False,
                cheese=False,
                pico=False,
                corn=False
                ):

        self.meat = meat
        self.rice = rice
        self.beans = beans
        self.to_go = to_go
        self.extra_meat = extra_meat
        self.guacamole = guacamole
        self.cheese = cheese
        self.pico = pico
        self.corn = corn
chepner
  • 497,756
  • 71
  • 530
  • 681
  • How would I filter for bad attributes, what I was trying to define in `MEATS`, `RICES` ,`BEANS`? – bbartling Jul 23 '22 at 14:45
  • You don't. `__init__`'s job is to simply do what the caller asks; it's their job to pass the correct arguments, and on them if they fail to do so. (But see [AKX's answer](https://stackoverflow.com/a/73091635/1126841) for using a *different* function that *only* validates the arguments; I won't repeat it here.) – chepner Jul 23 '22 at 14:47
0

Any function that doesn't explicitly return anything implicitly returns None.

Your setter function internally sets things, but has no other return statement, so

self.meat = self.setter("meat",meat)

ends up setting self.meat to None right after self.setter has already set it to something.

You're also not doing anything with more than half of the parameters your constructor accepts. I would write your class as

BEANS = {"black", "pinto"}
RICES = {"brown", "white"}
MEATS = {"chicken", "pork", "steak", "tofu"}


def validate_attr(value, options):
    if value in options:
        return value
    return False


class Burrito:
    def __init__(
        self,
        *,
        meat,
        rice,
        beans,
        to_go,
        extra_meat=False,
        guacamole=False,
        cheese=False,
        pico=False,
        corn=False,
    ):
        self.meat = validate_attr(meat, MEATS)
        self.rice = validate_attr(rice, RICES)
        self.beans = validate_attr(beans, BEANS)
        self.to_go = bool(to_go)
        self.extra_meat = bool(extra_meat)
        self.guacamole = bool(guacamole)
        self.cheese = bool(cheese)
        self.pico = bool(pico)
        self.corn = bool(corn)
AKX
  • 152,115
  • 15
  • 115
  • 172
  • thank you for this... Still learning here : ) – bbartling Jul 23 '22 at 14:47
  • Ill hit the green check when I can, what is `*,`? Never seen that before. Wildcard? – bbartling Jul 23 '22 at 14:48
  • [In a function signature, it means the remaining arguments must be passed kwarg-only](https://peps.python.org/pep-3102/#specification), i.e. you need to do `Burrito(meat=..., rice=...)`, etc; for a function that has many arguments, it makes it harder to accidentally pass a rice for a meat, or cheese for guacamole (which would be a no-no for a lactose-intolerant person!) – AKX Jul 23 '22 at 14:49
  • In your answer, `RICES`,`MEATS`,`BEANS` I notice you use dictionary instead of list. Is that a better choice than a list? – bbartling Jul 23 '22 at 15:38
  • It's not a dictionary, it's a set. Sets are better when you don't care about the order of the items within, and want faster inclusion lookups. – AKX Jul 23 '22 at 15:52
  • Ah cool and with sets the items or keys all need to be unique right? That makes sense for this use case. – bbartling Jul 24 '22 at 12:04
  • Yep - sets are unique-itemed and unordered; like dicts without values. – AKX Jul 24 '22 at 13:10