Classes & OOP--Defining Your Own Exception Classes

简介: Classes & OOP--Defining Your Own Exception Classes

Classes & OOP–Defining Your Own Exception Classes
When I started using Python, I was hesitant to write custom exception classes in my code. But defining your own error types can be of great value. You’ll make potential error cases stand out clearly, and as a result, your functions and modules will become more maintainable. You can also use custom error types to provide additional debugging information.

All of this will improve your Python code and make it easier to understand, easier to debug, and more maintainable. Defining your own exception classes is not that hard when you break it down to a few simple examples. Let’s say you. Wanted to validate an input string representing a person’s name in your application. A toy example for a name validator function might look like this:

In [1]: def validate(name):
   ...:     if len(name)<10:
   ...:         raise ValueError

If the validation fails, it throws a ValueError exception. That seems fitting and kind of Pythonic already. So far, so good.

However, there’s a downside to using a “high-level” generic exception class like ValueError. Imagine one of your teammates calls this function as part of a library and doesn’t know much about its internals. When a name fails to validate, it’ll look like this in the debug stack trace:

In [2]: validate('joe')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-2-84bf9236a789> in <module>
----> 1 validate('joe')

<ipython-input-1-e46a2bbae29e> in validate(name)
      1 def validate(name):
      2     if len(name)<10:
----> 3         raise ValueError
      4 

ValueError:

This stack trace isn’t really all that helpful. Sure, we know that something went wrong and that the problem had to do with an “incorrect value” of sorts. but to be able to fix the problem your temmate almost certainly has to look up the implementation of validate(). However, reading code costs time. And it can add up quickly.

Luckily we can do better. Let’s introduce a custom exception type to represent a failed name validation. We’ll base your new exception class on Python’s built-in ValueError. but make it speak for itself by giving it a more explicit name:

In [3]: class NameTooShortError(ValueError):
   ...:     pass
   ...: 

In [4]: def validate(name):
   ...:     if len(name) < 10:
   ...:         raise NameTooShortError(name)

Now we have a “self-documenting” NameTooShortError exception type that extends the built-in ValueError class. Generally, you’ll want to either derive your custom exceptions from the root Exception class or the other built-in Python exceptions like ValueError or TypeError–whichever feels appropriate.

Also, see how we’re now passing the nam variable to the constructor of our custom exception class when we instantiate it inside validate?The new implementation results in a much nicer stack trace for your colleague:

In [5]: validate('jane')
---------------------------------------------------------------------------
NameTooShortError                         Traceback (most recent call last)
<ipython-input-5-e12fe3239433> in <module>
----> 1 validate('jane')

<ipython-input-4-fa64b43535ec> in validate(name)
      1 def validate(name):
      2     if len(name) < 10:
----> 3         raise NameTooShortError(name)
      4 

NameTooShortError: jane

Once again, try to put yourself in your teammate’s shoes. Custom exception classes make it much easier to understand what’s going on when things go wrong (and eventually they always do).

The same is true even if you’re working on a code base all by yourself. A few weeks or months down the road you’ll have a much easier time maintaining your code if it’s well-structured.

By spending just 30 seconds on defining as simple exception class, this code snipped became much more communicative already. But let’s keep going. There’s more to cover.

Whenever you’re publicly releasing a Python package, or even if you’re creating a reusable module for your company, it’s good practice to create a custom exception base class for the module and athen derive all of your other exceptions from it.

Here’s how to create a custom exception hierarchy for all exceptions in a module or package. The first step is to declare a base class that all of our concrete errors will inherit from:

In [6]: class BaseValidationError(ValueError):
   ...:     pass

Now, all of our “real” error classes can be derived from the base error class. This gives a nice and clean exception hierarchy with little extra effort:

In [6]: class BaseValidationError(ValueError):
   ...:     pass
   ...: 

In [7]: class NameTOOShortError(BaseValidationError):
   ...:     pass
   ...: 

In [8]: class NameTOOLongError(BaseValidationError):
   ...:     pass
   ...: 

In [9]: class NameTOOCuteError(BaseValidationError):
   ...:     pass

For example, this allows users of your package to write try…except

try:
  validate(name)
except BaseValidationError as err:
  handle_validation_error(err)

People can still catch more specific exceptions that way, but if they don’t want to, at least they won’t have to resort to snapping up all exceptions with a catchall except statement. This is generally considered an anti-pattern–it can silently swallow and hide unrealated errors and make your programs much harder to debug.

Of course you can take this idea further and logically group your exceptions into fine grained sub-hierarchies. But be careful–it’s easy to introduce unnecessary complexity by going overboard with this.

In conclusion, defining custom exception classes makes it easier for your users to adopt an it’s easier to ask for forgiveness than permission (EAFP)

相关文章
|
Java 数据库连接 mybatis
Consider defining a bean of type ‘com.example.democrud.democurd.usermapper.DaoMapper‘ in your config
Consider defining a bean of type ‘com.example.democrud.democurd.usermapper.DaoMapper‘ in your config
206 0
|
6月前
|
Java 数据库连接 Apache
Correct the classpath of your application so that it contains compatible versions of the classes com
Correct the classpath of your application so that it contains compatible versions of the classes com
Unable to interpret the implicit parameter configuration with dataType: , dataTypeClass: class java.
Unable to interpret the implicit parameter configuration with dataType: , dataTypeClass: class java.
563 0
|
1月前
|
前端开发 Python
Python Tricks-- Abstract Base Classes Keep Inheritance in Check
Python Tricks-- Abstract Base Classes Keep Inheritance in Check
|
4月前
|
Java Maven
The JAVA_HOME environment variable is not defined correctly,this environment variable is needed to
The JAVA_HOME environment variable is not defined correctly,this environment variable is needed to
|
Java Spring
BeanCreationException: Error creating bean with name ‘configurationPropertiesBeans‘ defined in class
BeanCreationException: Error creating bean with name ‘configurationPropertiesBeans‘ defined in class
226 0
|
Java Apache Spring
解决required a single bean, but 2 were found问题
背景:springboot整合shiro中自定义Realm时出现 错误描述 Parameter 0 of method getDefaultWebSecurityManager in cn.ken.springboot_shiro.config.ShiroConfig required a single bean, but 2 were foun
|
消息中间件 Java RocketMQ
【Java】Error creating bean with name ‘functionBindingRegistrar‘ defined in class path resource的一种解决方式
【Java】Error creating bean with name ‘functionBindingRegistrar‘ defined in class path resource的一种解决方式
144 0
|
Java 关系型数据库 MySQL
15. 成功解决:java: Can't generate mapping method with primitive return type.
今天启动 SpringBoot 项目时,报了如下错误:`java: Can't generate mapping method with primitive return type.`
1022 0
|
前端开发 Java
java:How to use variable parameter? (easy to understand)
It's the first time to write a blog in English for me. (About variable parameter)
93 0
java:How to use variable parameter? (easy to understand)