0

This script simulates orders inside a pizzeria. I'm trying to create two exceptions. One that warns you when the amount of cheese is too much. One that notifies you when a pizza isn't on the menu.

Why does the line print(tmce, ':', tmce.cheese) prints too much cheese : 110, if in the instruction raise TooMuchCheeseError(pizza, cheese,"too much cheese") I pass 3 arguments?

shouldn't it print ('margherita', 110, 'too much cheese') : 110?
Using the tuple args like this print (tmce.args, ':', tmce.cheese) I get this output : ('too much cheese',) : 110. Shouldn't the tuple contain three elements?

Here is the code :

class PizzaError(Exception):
    def __init__(self, pizza, message):  
        Exception.__init__(self, message)
        self.pizza = pizza

class TooMuchCheeseError(PizzaError): 
    def __init__(self, pizza, cheese, message):
        PizzaError.__init__(self, pizza, message)
        self.cheese = cheese

def makePizza(pizza, cheese):
    if pizza not in ['margherita', 'capricciosa', 'calzone']:
        raise PizzaError(pizza, "no such pizza on the menu")
    if cheese > 100:
        raise TooMuchCheeseError(pizza, cheese, "too much cheese")
    print("Pizza ready!")

for (pz, ch) in [('calzone', 0), ('margherita', 110), ('mafia', 20)]:
    try:
        makePizza(pz, ch)
    except TooMuchCheeseError as tmce:
        print(tmce, ':', tmce.cheese)
    except PizzaError as pe:
        print(pe, ':', pe.pizza)

Output :

Pizza ready!
too much cheese : 110
no such pizza on the menu : mafia
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
  • You are not passing a tuple... You are creating a new object with some arguments. I don't see why you are suprised. When accessing the object (exception) itself, it prints the message – Tomerikoo Mar 29 '20 at 19:08
  • my question is : because if I print tmce, it prints the string "too much cheese" and not another of the three arguments that I pass to the object? (exception) Please be clear, it doesn't matter if you write many lines, I just want to understand. – Antonio Pio Mennuni Mar 29 '20 at 23:36
  • By default, Exceptions print their message. Because you didn't override the `__str__` method for any of your classes, this is what is being printed. What I meant in my previous comment is that this has nothing to do with tuples, and not even exceptions... Just basic OOP. Read about the [Difference between `__str__` and `__repr__`](https://stackoverflow.com/questions/1436703/difference-between-str-and-repr) – Tomerikoo Mar 30 '20 at 06:11
  • You can also read about [Exception Handling](https://docs.python.org/3/tutorial/errors.html#handling-exceptions). In the bottom of the section you will find all information relevant to you. Small correction to my last comment: *For convenience, the exception instance defines `__str__()` so the arguments can be printed directly without having to reference .args.*. It's not that it will print the message, rather all arguments. So to get what you were trying you can change to: `Exception.__init__(self, pizza, message)` – Tomerikoo Mar 30 '20 at 06:23
  • When I raise TooMuchCheeseError, I pass 3 topics. In the constructor of the TooMuchCheeseError class, I define the self.cheese attribute and call the constructor of PizzaError, I define the self.pizza attribute. From the documentation I read that the args contains the arguments passed to the constructor. If I print 'tmce.args' I get the tuple with only one argument that would be 'too much cheese', when instead I raised 3 arguments with the TooMuchCheeseError raise. By printing tmce.args shouldn't I get all 3 topics I raised with the raise? – Antonio Pio Mennuni Mar 30 '20 at 20:42
  • when I switch to Exception .__ init __ (self, message) will my TooMuchCheeseError class object also have the self.message attribute? – Antonio Pio Mennuni Mar 30 '20 at 20:42
  • The `args` are in the exception are what you pass to the `Exception` constructor, not your own classes... If you want to define a specific way **your** exceptions are printed, you can define their `__str__` method – Tomerikoo Mar 30 '20 at 22:24
  • so based on the argument I pass to the constructor of the Exception class, do I decide what will be printed when I raise my custom exception (in my case TooMuchCheeseError and PizzaError)? – Antonio Pio Mennuni Mar 30 '20 at 22:52
  • Basically yes. I would like to make an informative answer to better explain myself, I just need you to clarify what is exactly the output you are going for. I understand your current output, tell me what exactly you're trying to achieve and I will make this into a concise answer – Tomerikoo Mar 31 '20 at 07:57
  • The example he showed you is taken from the PCAP certification of the python institute. I wanted to understand why it came out that output. Now I understand and thank you so much. But why is the message that is printed, raising the exception, determined by what I pass to the constructor of the Exception class? – Antonio Pio Mennuni Mar 31 '20 at 11:43

1 Answers1

0

The main thing to understand is what happens when you try to print an object (any, not just an exception). What happens in the background is that this object's __str__ method is called and its return value is printed.

In the Handling Exceptions section of the docs, near the end it is stated:

For convenience, the exception instance defines __str__() so the arguments can be printed directly without having to reference .args.

Those args mentioned are the ones passed to the exception. That is, to the Exception's __init__. In your case that is just message in:

Exception.__init__(self, message)

If you want more arguments to be printed, you need to pass them to the Exception args. For example if you change the above line to:

Exception.__init__(self, pizza, message)

You will get as output:

Pizza ready!
('margherita', 'too much cheese') : 110
('mafia', 'no such pizza on the menu') : mafia

Alternatively, you can simply override the __str__ method for your exceptions. For example adding this to TooMuchCheeseError:

def __str__(self):
    return "Oh no! the pizza {} has too much cheese: {}".format(self.pizza, self.cheese)

will now give:

Pizza ready!
Oh no! the pizza margherita has too much cheese: 110 : 110
no such pizza on the menu : mafia

Lastly, the fact that you pass a message to the errors is a bit redundant. Because your exceptions serve a specific purpose, the message can be inherent in them. This allows your handling to be more dynamic. For example:

class PizzaError(Exception):
    def __init__(self, pizza):
        super().__init__()
        self.pizza = pizza

    def __str__(self):
        return "Oh no! There is not a pizza '{}' on the menu".format(self.pizza)

class TooMuchCheeseError(PizzaError):
    def __init__(self, pizza, cheese):
        PizzaError.__init__(self, pizza)
        self.cheese = cheese

    def __str__(self):
        return "Oh no! the pizza {} has too much cheese: {}".format(self.pizza, self.cheese)

def makePizza(pizza, cheese):
    if pizza not in ['margherita', 'capricciosa', 'calzone']:
        raise PizzaError(pizza)
    if cheese > 100:
        raise TooMuchCheeseError(pizza, cheese)
    print("Pizza ready!")

for (pz, ch) in [('calzone', 0), ('margherita', 110), ('mafia', 20)]:
    try:
        makePizza(pz, ch)
    except (TooMuchCheeseError, PizzaError) as pe:
        print(pe)

will print:

Pizza ready!
Oh no! the pizza margherita has too much cheese: 110
Oh no! There is not a pizza 'mafia' on the menu 
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
  • Thanks a lot, the code is really well done and even more optimized. Can I ask you what did you studied? I'm Italian, so don't mention the names of the universities because I don't know. – Antonio Pio Mennuni Apr 01 '20 at 17:12
  • Happy to help. I studied engineering and CS but had only basic courses in Python and mostly pursued it by myself. With great help from a very big project I did at work that made me learn ALOT. I also learn alot here on SO, I browse questions to answer but learn alot from other people's answers :) BTW feel free to accept or vote the answer if it helped you – Tomerikoo Apr 01 '20 at 19:49
  • I still do high school, but I love computer science. I would like to study computer security in short. I vote for you right away because you helped me a lot. Hi and thanks again – Antonio Pio Mennuni Apr 01 '20 at 22:09