Confused about try/except with custom Exception
Asked Answered
J

4

9

My code:

class AError(Exception):
    print 'error occur'
for i in range(3):
    try:
        print '---oo'
        raise AError
    except AError:
        print 'get AError'
    else:
        print 'going on'
    finally:
        print 'finally'

When I run the above code, the output is this:

error occur
---oo
get AError
finally
---oo
get AError
finally
---oo
get AError
finally

I think the string "error occur" should occur three times, like "---oo", but it only occurs once; why?

Junji answered 4/11, 2014 at 11:9 Comment(0)
B
11

To clarify Paul's answer, here's a simple example:

class Test(object):

    print "Class being defined"

    def __init__(self):
        print "Instance being created"


for _ in range(3):
    t = Test()

The output from this will be:

Class being defined
Instance being created
Instance being created
Instance being created

Code within the class definition but outside a method definition runs only once, when the class is defined.

If you want code to run whenever an instance is created, it should be in the __init__ instance method (or, occasionally, the __new__ class method). However, note that if you define __init__ for a subclass, you should probably ensure that it also calls the superclass's __init__:

class AError(Exception):

    def __init__(self, *args, **kwargs):
        Exception.__init__(self, *args, **kwargs) # call the superclass
        print 'error occur' # print your message

This ensures that the subclass supports the arguments for the superclass; in the case of Exception, you can e.g. pass an error message:

>>> raise AError("Something went wrong.")
error occur # your message gets printed when the instance is created

Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    raise AError("Something went wrong.")
AError: Something went wrong. # the error message passes through to the traceback

For an explanation of the *args, **kwargs syntax, if you aren't familiar with it, see e.g. What does ** (double star) and * (star) do for parameters?. You can also use super to call the superclass methods, see e.g. Understanding Python super() with __init__() methods.

Botsford answered 4/11, 2014 at 11:31 Comment(1)
This answer is very clear, I get it. 3Q very much ,so kand of you!Junji
B
5

'error occur' only gets printed once, for the entire class.

You probably expected it to get run for each instance of the class that was created.

For that to happen, put it in the __init__ function,

class AError(Exception):
    def __init__(self):
        print 'error occur'

__init__ is called when an instance of AError is created.

Betancourt answered 4/11, 2014 at 11:13 Comment(2)
You should mention the difference between class definition and instance creation, i.e. why this makes a difference. Additionally, __init__ should take arguments and call the superclass initialiser, otherwise the custom error won't support e.g. an error message.Botsford
I forgot the difference between class method and instance method,3Q for giving directions.Junji
C
2

I strongly recommend not to place any print statements in your Exception, esp. not their constructors! Exceptions are semantic entities and you can print them out if you need to. If you must automate the printing at least use logging or a similar package.

What you might not be aware of is that you can collect the exception instance for use in the except clause like so:

class MyError(Exception):
    pass

for i in range(3):
    try:
        print '---oo'
        raise MyError("error msg no. {}".format(i))
        # Exception usually always accept a string as their first parameter
    except MyError, ex:
        # Exception instance available inside the except clause as `ex`
        print ex
    else:
        print 'going on'
    finally:
        print 'finally'
Charmain answered 4/11, 2014 at 12:36 Comment(0)
C
1

The 'error occur' string appears only one because Python executes it when parsing your AError class definition.

If you want to get it executed every time you create an instance of your class, you must define the class's initialiser:

class AError(Exception):
    def __init__(self):
        print 'error occur'

for i in range(3):
    try:
        print '---oo'
        raise AError
    except AError:
        print 'get AError'
    else:
        print 'going on'
    finally:
        print 'finally'

Have fun (and read the language manual maybe...)

Cattleman answered 4/11, 2014 at 11:32 Comment(1)
See my answer (and my comment on Paul's) - AError.__init__ should call Exception.__init__ and handle appropriate arguments.Botsford

© 2022 - 2024 — McMap. All rights reserved.