Home > Back-end >  How do I avoid except returning an empty object class
How do I avoid except returning an empty object class

Time:04-26

I want to include an except inside a class, but when I run the code, the except is returning an empty class object. How can I make the except return only the error message and nothing else?

class InputError(Exception):
    """Raised when input format not allowed"""
    pass

class ReadMap1:
    def __init__(self, input):
        try:
            if (type(input) == str) | (type(input) == int):
                if type(input) == str:
                    self.input = input
                else:
                    self.input = str(input)
            else:
                raise InputError
        except InputError:
            print("----Input not allowed----")

Output:

ReadMap1([1])
----Input not allowed----
<__main__.ReadMap1 object at 0x0000024B30B49FD0>

CodePudding user response:

Many people confuse Python's __init__ with constructors from other languages, because like a constructor, __init__ is often used to initialize attributes. However, __init__ is not a constructor, but an initializer. The distinction is subtle though not terribly complicated in practice: The constructor constructs and returns an object, hence you can do my_obj = ClassConstructor() whereas the initializer only works on the object that the constructor already created. This is why Python's MyClass.__init__ returns nothing.

Your code is behaving as expected. You have told it:

if input is a string or integer, then do some stuff
if input is another type, then crash with InputError
if the above ever crashes with InputError, print an error message instead of crashing and move on

Then you pass the initializer [1] which is a Python list. This being neither a string nor a number, the interpreter raises an InputError. Normally exceptions cause a crash, but since you have the try catch, the crash is then negated, and instead a message is printed.

You can "fix" this trivially by changing your except clause to be like:

except InputError:
    print("----Input not allowed----")
    self.input = "default value"

It sounds like you want the initialization to be aborted in this case. Then you should remove the try-except, and let __init__ crash. The caller will then have to create the object like so:

try:
   rm = ReadMap1([1])
except InputError:
   print("----Input not allowed----")

Or you could omit the try-except here as well and allow the entire program to crash. It all depends: If input was invalid, could you program a way for your program to keep ticking, or is everything lost regardless?


There some additional issues in your code:

  • input is the name of a builtin function -- it's better to avoid shadowing it. Find a more unique name for your argument.
  • If you are going to check types in your if, you don't need try. You would either try to use the value and handle except TypeError, or you would use if type(..., not both.
  • It makes little sense to raise an exception inside a try that catches it. try is useful when the exception may arise form somewhere deep inside the code you are calling, and it would be impractical to directly check for the problem. In this case it is evident that you can check the type yourself, instead of relying on exceptions, so just do that.
  • If you are merely converting the value to a string, you don't need to check the type at all. Just do self.input = str(input). If it's already a string, Python will pass it unaltered. Everything else will be converted to a string (by calling the object's [or its parents'] .__str__ method).

CodePudding user response:

You can define __new__() in order to intercept the creation of the object. You can return None here given a certain condition. Doing that will prevent __init__() from being called.

class ReadMap:
    def __new__(cls, input):
        if (type(input) == str) | (type(input) == int):
            return super().__new__(cls)
        
        print("----Input not allowed----")
        return None
    
    def __init__(self, input):
        self.input = input

o = ReadMap(1)
o.input
# 1

p = ReadMap([1])
# prints ----Input not allowed----
print(p)
# None

I suspect this isn't a great idea, but I don't know how you are using this. It's pretty unexpected for the caller to call ReadMap() and get None, but it is possible. In general, I would opt for raising and letting the caller deal with the bad input — they after all sent the bad input.

  • Related