python——内置方法补充、异常处理

目录

一、new方法

二、__len__方法

三、__eq__方法

四、__hash__方法

 五、__eq__和__hash__应用

官方解释

六、异常处理

1、什么是异常

2、异常的种类

3、异常处理

4、异常处理的意义

 5、捕获异常

5.1 捕获所有异常

5.2 捕获指定异常

5.3 捕获多个异常

5.4 异常中的else

5.5 异常中的finally

5.6 raise主动触发异常

5.7 采用traceback模块查看异常

6、断言assert

7、自定义异常

7.1、自定义异常的两种方式

7.2、如何手动抛出异常:raise

7.3.捕捉用户手动抛出的异常

8、什么时候使用异常


一、new方法

__init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象

实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法

__new__()必须要有返回值,返回实例化出来的实例

def __new__(cls, *args, **kwargs):

例子

# -*- coding: utf-8 -*-

class Foo(object):
    def __init__(self,name):
        self.name=name

    def __new__(cls, *args, **kwargs):
        obj=super(Foo, cls).__new__(cls) #利用object的__new__方法创建新的空对象
        obj.__init__(*args,**kwargs)
        return obj

f=Foo('nike') 
print(f.name)
print(f.__dict__)

二、__len__方法

如果一个类表现得像一个list,要获取有多少个元素,就得用 __len__() 函数。

通过len(object)调用

class Student():
    def __init__(self,*args):
        self.names = args
​
    def __len__(self):
        return len(self.names)
​
s = Student("nick","jack","pony")
print(len(s)) #用len直接调用__len__方法
from collections import namedtuple

Card = namedtuple("Card",["rank","suit"]) #这里相当于创建了一个名为Card的类,后面的列表里是类的类属性,没有函数属性
                                          #
class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    suits = ['红心','方板','梅花','黑桃']

    def __init__(self):
        self._cards = [(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] #这里是一个列表生成式,
        #将第一个for循环里的值赋值给rank,将第二个for循环里的值赋给suit,这个类只要一实例化就自动生成了一副除了没有大小王的牌
        #每张牌都是用元组来表示的
    def __len__(self):
        return len(self._cards) # choice()方法依赖__len__(self)

    def __getitem__(self, item):
        return self._cards[item]

    def __setitem__(self, key, value):
        self._cards[key] = value

deck = FranchDeck()  #这里相当于创建了一副牌
print(deck[0]) #打印第一张牌
print(deck[2:5]) #打印第2-4张牌
from random import choice,shuffle
print(choice(deck))  #随机抽取一张牌
shuffle(deck) #洗牌
print("洗牌后的第一张",deck[0]) #打印第一张牌

三、__eq__方法

__eq__(slef,other) ,判断self对象是否等于other对象,使用==或者is调用此方法。

class Foo:
​
    def __init__(self,name):
        self.name = name
​
    def __eq__(self, other):
        if self.name == other.name:  #判断如果对象的name属性相等就返回True
            return True
        else:
            return False
​
obj1 = Foo("nick")
obj2 = Foo("nicholas")
print(obj1 is obj2)

四、__hash__方法

获取取一个对象的哈希值,一般来说,对于对象的hash值是基于对象的内存地址生成的,但是重写__hash__方法可以自己定制hash取值的对象

class Foo:
​
    def __init__(self,name,age):
        self.name = name
        self.age = age
​
    def __hash__(self):
        return hash(self.name+str(self.age)) #这里基于对象的两个属性返回hash值
​
​
obj1 = Foo("nick",18)  #注意hash的对象不能是整数
obj2 = Foo("nick",18)
print(hash(obj1))
print(hash(obj2))

注意魔法方法__hash__的使用场景有二:

(1)被内置函数hash()调用

(2)hash类型的集合对自身成员的hash操作:set(),  frozenset([iterable]),   dict(**kwarg)

 五、__eq__和__hash__应用

1. 以下代码前半段只用到了__eq__,因为没涉及可hash集合,也就没有涉及到hash

2、如果自定义类重写了__eq__()方法没有重写__hash__()方法,那么这个类无法作为哈希集合的元素使用(这个hashable collections指的是set、frozenset和dict)

比如哈希集合放的全是对象,只定义的__eq__,没定义__hash__,会报错


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    # def __hash__(self):
    #     print(self.name, '使用了__hash__方法')
    #     return hash(self.name)
 
    def __eq__(self, other):
        print(self.name, '使用了__eq__方法')
        return self.__dict__ == other.__dict__
 
 
person1 = Person('zs', 20)
person2 = Person('ls', 20)
person3 = Person('ww', 30)
person4 = Person('zs', 20)
 
print(person1 == person4)
print(person2 == person3)

set1 = {person1, person2, person3, person4}
print(set1)

输出:
zs 使用了__eq__方法
True
ls 使用了__eq__方法
False
Traceback (most recent call last):
  File "D:/self_study/python_study/class_study/python——内置方法.py", line 88, in <module>
    set1 = {person1, person2, person3, person4}
TypeError: unhashable type: 'Person'

这其实是因为重写__eq__()方法后会默认把__hash__赋为None(文档后面有说),像list一样。如下面测试:

class A:
    def __eq__(self, other):
        pass 
a = A()
 
print(a.__hash__)  
hash(a)
输出: 
None 
TypeError: unhashable type: 'A'

3. 如果自定义类重写了__hash__()方法没有重写__eq__()方法

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def __hash__(self):
        print(self.name, '使用了__hash__方法')
        return hash(self.name)
 
    # def __eq__(self, other):
    #     print(self.name, '使用了__eq__方法')
    #     return self.__dict__ == other.__dict__
 
 
person1 = Person('zs', 20)
person2 = Person('ls', 20)
person3 = Person('ww', 30)
person4 = Person('zs', 20)
 
 
set1 = {person1, person2, person3, person4}
print(set1)

不报错,但不符合需求———我们认为person1与person4是同一个人,他们却都被添加到集合,违背了集合的理念:

zs 使用了__hash__方法
ls 使用了__hash__方法
ww 使用了__hash__方法
zs 使用了__hash__方法
{<__main__.Person object at 0x0000000000719CF8>,
 <__main__.Person object at 0x00000000007192E8>, 
 <__main__.Person object at 0x0000000000719CC0>,
 <__main__.Person object at 0x0000000000719320>}

我们期望中没有person4.为什么这里会有person4呢?而且person1与person4的hash(name)是相同的,为什么最后还是加到了集合呢? 主要是因为当发现hash出的值相同时,就需要__eq__进行下一步判断。我们重写了__hash__却没重写__eq__,默认调用了object的__eq__,而默认的__eq__比较的是对象地址,person1与person4地址不同,所以将最后的person4加到了集合.

4、自定义类重写了__hash__()方法和__eq__()方法

可哈希的集合(hashed collections),需要集合的元素实现了__eq____hash__,而这两个方法可以作一个形象的比喻:
哈希集合就是很多个桶,但每个桶里面只能放一个球。

__hash__函数的作用就是找到桶的位置,到底是几号桶。
__eq__函数的作用就是当桶里面已经有一个球了,但又来了一个球,它声称它也应该装进这个桶里面(__hash__函数给它说了桶的位置),双方僵持不下,那就得用__eq__函数来判断这两个球是不是相等的(equal),如果是判断是相等的,那么后来那个球就不应该放进桶里,哈希集合维持现状。

class Foo:
    def __init__(self, item):
        self.item = item

    def __eq__(self, other):
        print('使用了equal函数的对象的id',id(self))
        if isinstance(other, self.__class__):
            return self.__dict__ == other.__dict__
        else:
            return False
    def __hash__(self):
        print('f'+str(self.item)+'使用了hash函数')
        return hash(self.item)       
f1 = Foo(1)
f2 = Foo(2)
f3 = Foo(3)
fset = set([f1, f2, f3])
print(fset)
print()
f = Foo(3)
fset.add(f)
print('f3的id:',id(f3))
print('f的id:',id(f))
fset.remove(f)
print(fset)

运行结果:

f1使用了hash函数
f2使用了hash函数
f3使用了hash函数
{<__main__.Foo object at 0x00000203EB7EB630>, <__main__.Foo object at 0x00000203EB987358>, <__main__.Foo object at 0x00000203EB987390>}

f3使用了hash函数
使用了equal函数的对象的id 2215860794256
f3的id: 2215860794256
f的id: 2215860797392
使用了equal函数的对象的id 2215860794256
{<__main__.Foo object at 0x00000203EB7EB630>, <__main__.Foo object at 0x00000203EB987358>}

可见,在将f1,f2,f3加入到set中时,每次都会调用一次__hash__函数。
由于我定义的___hash__函数是return hash(self.item),所以f和f3找到的桶的位置是同一个位置,因为它俩的item是相同的。当执行fset.add(f)时,f就会调用它自身的__hash__函数,以找到f所属于的桶的位置。但此时桶里已经有别的球了,所以这时候就得用上__eq__来判断两个对象是否相等,从输出可以看出,是已有对象调用__eq__来和后来的对象进行比较(看对象的id)。
这里如果是删除操作fset.remove(Foo(3)),道理也是一样,先用hash找到桶的位置,如果桶里有球,就判断这两个球是否相等,如果相等就把桶里那个球给扔掉。

官方解释

当可哈希集合(set,frozenset,dict)调用hash函数时,应该返回一个int值。唯一的要求就是,如果判断两个对象相等,那么他们的hash值也应该相等。当比较两个对象相等时是使用对象的成员来比较时,建议要把成员弄进元祖里,再得到这个元祖的hash值来比较。

当class没有定义__eq__()方法时,那么它也不应该定义__hash__()方法。如果它定义了__eq__()方法,却没有定义__hash__()方法,那么这个类的实例就不能在可哈希集合使用。如果一个类定义了一个可变对象(这里应该是指class的成员之一为可变对象),且implement了__eq__()方法,那么这个类就不应该implement hash()方法,因为可哈希对象的实现(implement )要求键值key的hash值是不变的(如果一个对象的hash值改变了,那么它会被放在错误的hash桶里)

用户定义的类中都有默认的__eq__和__hash__方法;有了它,所有的对象实例都是不等的(除非是自己和自己比较),在做x == y比较时是和这个等价的hash(x) == hash(y)。

六、异常处理

1、什么是异常

异常就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常,程序的运行也随之终止)

错误分成两种:语法错误和逻辑错误

语法错误是一些代码的标点符号错误等。逻辑错误有很多种。

#TypeError:int类型不可迭代
for i in 3:
    pass
     
#ValueError
num=input(">>: ") #输入hello
int(num)
​
#NameError
aaa
​
#IndexError
l=['egon','aa']
l[3]
​
#KeyError
dic={'name':'egon'}
dic['age']
​
#AttributeError
class Foo:pass
Foo.x
​
#ZeroDivisionError:无法完成计算
res1=1/0
res2=1+'str'

2、异常的种类

常用异常

AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x

IOError 输入/输出异常;基本上是无法打开文件

ImportError 无法引入模块或包;基本上是路径问题或名称错误

IndentationError 语法错误(的子类) ;代码没有正确对齐

IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]

KeyError 试图访问字典里不存在的键

KeyboardInterrupt Ctrl+C被按下

NameError 使用一个还未被赋予对象的变量

SyntaxError Python代码非法,代码不能编译,语法错误

TypeError 传入对象类型与要求的不符合

UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,

导致你以为正在访问它

ValueError 传入一个调用者不期望的值,即使值的类型是正确的

 所有异常

BaseException  # 所有异常的基类
 +-- SystemExit  # 解释器请求退出
 +-- KeyboardInterrupt  # 用户中断执行(通常是输入^C)
 +-- GeneratorExit  # 生成器(generator)发生异常来通知退出
 +-- Exception  # 常规异常的基类
      +-- StopIteration  # 迭代器没有更多的值
      +-- StopAsyncIteration  # 必须通过异步迭代器对象的__anext__()方法引发以停止迭代
      +-- ArithmeticError  # 各种算术错误引发的内置异常的基类
      |    +-- FloatingPointError  # 浮点计算错误
      |    +-- OverflowError  # 数值运算结果太大无法表示
      |    +-- ZeroDivisionError  # 除(或取模)零 (所有数据类型)
      +-- AssertionError  # 当assert语句失败时引发
      +-- AttributeError  # 属性引用或赋值失败
      +-- BufferError  # 无法执行与缓冲区相关的操作时引发
      +-- EOFError  # 当input()函数在没有读取任何数据的情况下达到文件结束条件(EOF)时引发
      +-- ImportError  # 导入模块/对象失败
      |    +-- ModuleNotFoundError  # 无法找到模块或在在sys.modules中找到None
      +-- LookupError  # 映射或序列上使用的键或索引无效时引发的异常的基类
      |    +-- IndexError  # 序列中没有此索引(index)
      |    +-- KeyError  # 映射中没有这个键
      +-- MemoryError  # 内存溢出错误(对于Python 解释器不是致命的)
      +-- NameError  # 未声明/初始化对象 (没有属性)
      |    +-- UnboundLocalError  # 访问未初始化的本地变量
      +-- OSError  # 操作系统错误,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合并到OSError中,构造函数可能返回子类
      |    +-- BlockingIOError  # 操作将阻塞对象(e.g. socket)设置为非阻塞操作
      |    +-- ChildProcessError  # 在子进程上的操作失败
      |    +-- ConnectionError  # 与连接相关的异常的基类
      |    |    +-- BrokenPipeError  # 另一端关闭时尝试写入管道或试图在已关闭写入的套接字上写入
      |    |    +-- ConnectionAbortedError  # 连接尝试被对等方中止
      |    |    +-- ConnectionRefusedError  # 连接尝试被对等方拒绝
      |    |    +-- ConnectionResetError    # 连接由对等方重置
      |    +-- FileExistsError  # 创建已存在的文件或目录
      |    +-- FileNotFoundError  # 请求不存在的文件或目录
      |    +-- InterruptedError  # 系统调用被输入信号中断
      |    +-- IsADirectoryError  # 在目录上请求文件操作(例如 os.remove())
      |    +-- NotADirectoryError  # 在不是目录的事物上请求目录操作(例如 os.listdir())
      |    +-- PermissionError  # 尝试在没有足够访问权限的情况下运行操作
      |    +-- ProcessLookupError  # 给定进程不存在
      |    +-- TimeoutError  # 系统函数在系统级别超时
      +-- ReferenceError  # weakref.proxy()函数创建的弱引用试图访问已经垃圾回收了的对象
      +-- RuntimeError  # 在检测到不属于任何其他类别的错误时触发
      |    +-- NotImplementedError  # 在用户定义的基类中,抽象方法要求派生类重写该方法或者正在开发的类指示仍然需要添加实际实现
      |    +-- RecursionError  # 解释器检测到超出最大递归深度
      +-- SyntaxError  # Python 语法错误
      |    +-- IndentationError  # 缩进错误
      |         +-- TabError  # Tab和空格混用
      +-- SystemError  # 解释器发现内部错误
      +-- TypeError  # 操作或函数应用于不适当类型的对象
      +-- ValueError  # 操作或函数接收到具有正确类型但值不合适的参数
      |    +-- UnicodeError  # 发生与Unicode相关的编码或解码错误
      |         +-- UnicodeDecodeError  # Unicode解码错误
      |         +-- UnicodeEncodeError  # Unicode编码错误
      |         +-- UnicodeTranslateError  # Unicode转码错误
      +-- Warning  # 警告的基类
           +-- DeprecationWarning  # 有关已弃用功能的警告的基类
           +-- PendingDeprecationWarning  # 有关不推荐使用功能的警告的基类
           +-- RuntimeWarning  # 有关可疑的运行时行为的警告的基类
           +-- SyntaxWarning  # 关于可疑语法警告的基类
           +-- UserWarning  # 用户代码生成警告的基类
           +-- FutureWarning  # 有关已弃用功能的警告的基类
           +-- ImportWarning  # 关于模块导入时可能出错的警告的基类
           +-- UnicodeWarning  # 与Unicode相关的警告的基类
           +-- BytesWarning  # 与bytes和bytearray相关的警告的基类
           +-- ResourceWarning  # 与资源使用相关的警告的基类。被默认警告过滤器忽略。

 详细说明请参考:https://docs.python.org/3/library/exceptions.html#base-classes

3、异常处理

python解释器检测到错误,触发异常(也允许程序员自己触发异常)

程序员编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关)

如果捕捉成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理

4、异常处理的意义

python解析器去执行程序,检测到了一个错误时,触发异常,异常触发后且没被处理的情况下,程序就在当前异常处终止,后面的代码不会运行,所以你必须提供一种异常处理机制来增强你程序的健壮性与容错性。

 5、捕获异常

当发生异常时,我们就需要对异常进行捕获,然后进行相应的处理。python的异常捕获常用try...except...结构,把可能发生错误的语句放在try模块里,用except来处理异常,每一个try,都必须至少对应一个except。此外,与python异常相关的关键字主要有:

关键字 关键字说明
try/except 捕获异常并处理
pass 忽略异常
as 定义异常实例(except MyError as e)
else 如果try中的语句没有引发异常,则执行else中的语句
finally 无论是否出现异常,都执行的代码
raise     抛出/引发异常

异常捕获有很多方式,下面分别进行讨论

5.1 捕获所有异常

包括键盘中断和程序退出请求(用sys.exit()就无法退出程序了,因为异常被捕获了),因此慎用。

try:
     被检测的代码块
except 异常类型:
     try中一旦检测到异常,就执行这个位置的逻辑

5.2 捕获指定异常

try:
    f = open("file-not-exists", "r")
except IOError as e:
    print("open exception: %s: %s" %(e.errno, e.strerror))

万能异常:

在python的异常中,有一个万能异常:Exception,他可以捕获任意异常。它是一把双刃剑,有利有弊,我们要视情况使用

  如果你想要的效果是,无论出现什么异常,我们统一丢弃,或者使用同一段代码逻辑去处理他们,那么只有一个Exception就足够了。

  如果你想要的效果是,对于不同的异常我们需要定制不同的处理逻辑,那就需要用到多分支了。我们可以使用多分支+万能异常来处理异常。使用多分支优先处理一些能预料到的错误类型,一些预料不到的错误类型应该被最终的万能异常捕获。需要注意的是,万能异常一定要放在最后,否则就没有意义

5.3 捕获多个异常

捕获多个异常有两种方式,第一种是一个except同时处理多个异常,不区分优先级:

try:
     <语句>
except (<异常名1>, <异常名2>, ...):
      print('异常说明')

第二种是区分优先级的:

l1 = [('电脑',16998),('鼠标',59),('手机',8998)]
while 1:
    for key,value in enumerate(l1,1):
        print(key,value[0])
    try:
        num = input('>>>')
        price = l1[int(num)-1][1]
    except ValueError:
        print('请输入一个数字')
    except IndexError:
        print('请输入一个有效数字')
#这样通过异常处理可以使得代码更人性化,用户体验感更好。

该种异常处理语法的规则是:

  • 执行try下的语句,如果引发异常,则执行过程会跳到第一个except语句。
  • 如果第一个except中定义的异常与引发的异常匹配,则执行该except中的语句。
  • 如果引发的异常不匹配第一个except,则会搜索第二个except,允许编写的except数量没有限制。
  • 如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。

5.4 异常中的else

如果判断完没有某些异常之后还想做其他事,就可以使用下面这样的else语句。

try:
    for i in range(10):
        int(i)
except IndexError as e:
    print(e)
else:
    print('***********')   #***********   执行了此处
    #当try语句中的代码没有异常,被完整地执行完,就执行else中的代码

5.5 异常中的finally

try...finally...语句无论是否发生异常都将会执行最后的代码。

def dealwith_file():
    try:
        f = open('file',encoding='utf-8')
        for line in f:
            int(line)
        return True
    except Exception as e:
        print(e)
        return False
    finally:
        '''不管try语句中的代码是否报错,都会执行finally分支中的代码'''
        '''去完成一些连接操作的收尾工作'''
        print('finally 被执行了')
        f.close()
ret = dealwith_file()
print(ret)

5.6 raise主动触发异常

可以使用raise语句自己触发异常,raise语法格式如下:

raise [Exception [, args [, traceback]]]

语句中Exception是异常的类型(例如ValueError),参数是一个异常参数值。该参数是可选的,如果不提供,异常的参数是"None"。最后一个参数是跟踪异常对象,也是可选的(在实践中很少使用)。

看一个例子:

def not_zero(num):
    try:
        if num == 0:
            raise ValueError('参数错误')
        return num
    except Exception as e:
        print(e)

not_zero(0)

5.7 采用traceback模块查看异常

发生异常时,Python能“记住”引发的异常以及程序的当前状态。Python还维护着traceback(跟踪)对象,其中含有异常发生时与函数调用堆栈有关的信息。记住,异常可能在一系列嵌套较深的函数调用中引发。程序调用每个函数时,Python会在“函数调用堆栈”的起始处插入函数名。一旦异常被引发,Python会搜索一个相应的异常处理程序。如果当前函数中没有异常处理程序,当前函数会终止执行,Python会搜索当前函数的调用函数,并以此类推,直到发现匹配的异常处理程序,或者Python抵达主程序为止。这一查找合适的异常处理程序的过程就称为“堆栈辗转开解”(StackUnwinding)。解释器一方面维护着与放置堆栈中的函数有关的信息,另一方面也维护着与已从堆栈中“辗转开解”的函数有关的信息。

格式如下:

try:
    block
except:
    traceback.print_exc()

举个栗子:

try:
    1/0
except Exception as e:
    print(e)

如果我们这样写的话,程序只会报“division by zero”错误,但是我们并不知道是在哪个文件哪个函数哪一行出的错。

下面使用traceback模块,官方参考文档:https://docs.python.org/2/library/traceback.html

import traceback
 
try:
    1/0
except Exception as e:
    traceback.print_exc()

这样就会帮我们追溯到出错点:

Traceback (most recent call last):
  File "E:/PycharmProjects/ProxyPool-master/proxypool/test.py", line 4, in <module>
    1/0
ZeroDivisionError: division by zero

另外,traceback.print_exc()跟traceback.format_exc()有什么区别呢?

区别就是,format_exc()返回字符串,print_exc()则直接给打印出来。即traceback.print_exc()与print(traceback.format_exc())效果是一样的。print_exc()还可以接受file参数直接写入到一个文件。比如可以像下面这样把相关信息写入到tb.txt文件去。

traceback.print_exc(file=open('tb.txt','w+'))

6、断言assert

assert断言是声明其布尔值必须为真的判定,如果发生异常就说明表达示为假。可以理解assert断言语句为raise-if-not,用来测试表示式,其返回值为假,就会触发异常。

assert的异常参数,其实就是在断言表达式后添加字符串信息,用来解释断言并更好的知道是哪里出了问题。格式如下:
assert expression [, arguments]
assert 表达式 [, 参数]

assert len(lists) >=5,'列表元素个数小于5'

assert 2==1,'2不等于1'

备注:格式:assert  条件  , 条件为false时的错误信息,结果为raise一个AssertionError出来

7、自定义异常

7.1、自定义异常的两种方式

# 方式一
class MYERROR(Exception):
​
    def __init__(self):
        self.err = "自定义异常"
​
    def __str__(self):
        return self.err
​
e = MYERROR()
raise MYERROR
​
#方式二
class MYERROR2(BaseException):
​
    def __init__(self,msg):
        self.msg = msg
    #继承BaseException类不再需要__str__方法,BaseException中已经实现了
try:
    raise MYERROR2("自定义异常")
except Exception as e:
    print("-----")
    print(e)

7.2、如何手动抛出异常:raise

系统的自带的异常只要触发会自动抛出,比如NameError,但用户自定义的异常需要用户自己决定什么时候抛出。
raise 唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。大多数的异常的名字都以"Error"结尾,所以实际命名时尽量跟标准的异常命名一样。

#1.用户自定义异常类型
class TooLongExceptin(Exception):
    "this is user's Exception for check the length of name "
    def __init__(self,leng):
        self.leng = leng
    def __str__(self):
        print("姓名长度是"+str(self.leng)+",超过长度了")
 
#2.手动抛出用户自定义类型异常
def name_Test():
        name = input("enter your naem:")
        if len(name)>4:
            raise TooLongExceptin(len(name))  #抛出异常很简单,使用raise即可,但是没有处理,即捕捉
        else :
            print(name)
 
#调用函数,执行
name_Test()
-----------------执行时满足条件后抛出一个用户定义的异常如下:--------------------------------------
enter your naem:是打发斯蒂芬
Traceback (most recent call last):
姓名长度是6,超过长度了
  File "D:/pythoyworkspace/file_demo/Class_Demo/extion_demo.py", line 21, in <module>
    name_Test()
__main__.TooLongExceptin: <exception str() failed>

7.3.捕捉用户手动抛出的异常

#1.捕捉用户手动抛出的异常,跟捕捉系统异常方式一样

def name_Test():
    try:
        name = input("enter your naem:")
        if len(name) > 4:
            raise TooLongExceptin(len(name))
        else:
            print(name)

    except TooLongExceptin as e_result:  # 这里异常类型是用户自定义的
        print("捕捉到异常了")
        print("打印异常信息:", e_result)

# 调用函数,执行
name_Test()

=========执行结果如下=========
enter your naem:kklklk;l;l
捕捉到异常了
打印异常信息: 姓名长度是10,超过长度了
姓名长度是10,超过长度了
Traceback (most recent call last):
  File "D:/self_study/python_study/class_study/异常.py", line 15, in name_Test
    raise TooLongExceptin(len(name))
__main__.TooLongExceptin: <exception str() failed>

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:/self_study/python_study/class_study/异常.py", line 24, in <module>
    name_Test()
  File "D:/self_study/python_study/class_study/异常.py", line 21, in name_Test
    print("打印异常信息:", e_result)
TypeError: __str__ returned non-string (type NoneType)

Process finished with exit code 1

8、什么时候使用异常

try...except应该尽量少用,因为给你的程序加了一种异常处理的逻辑,会导致代码可读性变差。

而且异常处理,只有在有些异常无法预知的情况下,才加上try...except,其他的逻辑错误应该尽量修正。

猜你喜欢

转载自blog.csdn.net/zangba9624/article/details/110477262
今日推荐