Python面向对象 #5 异常与元类

本文详细介绍了Python中的异常处理机制,包括异常分类、解决异常的完整语法、主动抛出异常和使用assetr断言,同时探讨了Mixins机制和元类的使用,以及元类的__call__方法在实例化过程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、异常

异常是程序发生错误的信号。程序一旦出现错误,便会产生一个异常,若程序中没有处理它,就会抛出该异常,程序的运行也随之终止。在Python中,错误触发的异常如下
在这里插入图片描述

异常的分类:

1.逻辑错误:可以出现,但是编程的时候尽量避免逻辑错误的发生

2.语法错误:不允许出现

异常的组成:
  1. Traceback追溯信息,可以定位到错误发生的位置
  2. 错误类型:XXXError(TypeError、KeyError、IndexError等)
  3. 错误的原因:通过这部分我们可以精准找到错误的具体原因,这对我们解决问题来说是最重要的
解决异常的完整语法:
try:
	被检测代码(这块一般写可能会发生异常的代码)
except 错误类型1 as e:
	print(e)  # 查看错误的原因 
except 错误类型2 as e:
	print(e)  # 查看错误的原因 
except Exception as e: # 万能的异常,以上所有类型都没有被找到,就交给Exception处理
	print(e)  # 查看错误的原因 
else:
	print('被检测代码没有异常的时候就会走这个分支')
finally:
	print('不管被检测代码有没有异常都会走这个分支')
我们有时候也要主动抛出一些异常,来阻止代码运行,举例:

在父类中限制子类必须有某种方法

import abc


class People(metaclass=abc.ABCMeta):
    @abc.abstractmethod  # 设置之后,继承People类的子类都必须要有speak功能
    def speak(self):
        pass


class Dog(People):
    def speak(self):
        pass
"""这种方法不推荐"""

用异常来做:

主动抛出异常的关键字是:raise
语法结构:raise 异常的类型
raise IndexError(‘索引错误’)
raise KeyError(‘字典错误’)
raise Exception(‘字典错误’)万能异常

class People():
    def speak(self):
        raise Exception('请先设置speak功能')


class Dog(People):
    pass


d = Dog() 先实例化出来一个d对象
d.speak() 现在Dog类里面找speak方法,找不到就回去到父类People,这个找到了并且主动抛出异常
assetr断言

可以看做是功能缩小版的 if 语句,它用于判断某个表达式的值,如果值为真,则程序可以继续往下执行;反之,Python 解释器会报 AssertionError 错误。

l = ['jack', 'male',]
assert 'jack' in l  # 条件成立执行下一行代码
print('yes') # yes


l = ['jack', 'male',]
assert 18 in l  # 条件不成立直接报错,并且终止程序运行
print('yes')

"""
Traceback (most recent call last):
  File "E:\Python_month_1\day32\异常.py", line 35, in <module>
    assert 18 in l
AssertionError
"""

有读者可能会问,明明 assert 会令程序崩溃,为什么还要使用它呢?这是因为,与其让程序在晚些时候崩溃,不如在错误条件出现时,就直接让程序崩溃,这有利于我们对程序排错,提高程序的健壮性。

自定义异常
class MyException():
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def index(self):
        pass


raise MyException('xxxxxxxxxxxxxxxxxxxxxxxxxxxx')

二、Mixins机制

class Vehicle:  # 交通工具 主类:就是具备主要的功能
    '''这里面应该有其他交通工具都具备的功能'''


# 辅类:写一些辅助的功能,辅类中得函数一般很少
class FlyMixin(): # 专门写能够支持飞的功能
    def fly(self):
        '''
        飞行功能相应的代码
        '''
        print("I am flying")

# class FlyMixin(): # 专门写能够支持飞的功能
# class Flyable(): # 专门写能够支持飞的功能
class Flyible(): # 专门写能够支持飞的功能
    def fly(self):
        '''
        飞行功能相应的代码
        '''
        print("I am flying")

# 继承的类书写顺序也有讲究
# 辅类一般写在类的左边,主类写在类的右边,主类只有一个,而辅类可以有多个

# 辅类的类名也有讲解,辅类的类名一般都是以,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
class CivilAircraft(FlyMixin, Flyible, Vehicle):  # 民航飞机
    pass


class Helicopter(FlyMixin, Flyible, Vehicle):  # 直升飞机
    pass


class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
    pass


import socketserver

# class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
# class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

三、元类

简介:什么是元类?既然Python中一切皆为对象,有没有想过,我们定义的类它是否也为一个对象?类本质上也是一个对象,既然类是一个对象,那么就应该有一个类来产生它。这也就是本章节讲到的主题:元类,它也可以称之为产生类的类

s = 'hello'
d = {"name1": "jack"}
t = (1, 2, 3)
print(type(s))  # <class 'str'>
print(type(d))  # <class 'dict'>
print(type(t))  # <class 'tuple'>

这里可以看出来type其实是用来查看产生对象的类名
type元类

在Python中一切的类都是由type产生出来的

class Student():
    pass


stu = Student()
print(type(stu))  # <class '__main__.Student'>
print(type(Student))  # <class 'type'>
"""这里可以看出Student类是由type类产生的"""

print(type(type))  # <class 'type'>
"""这里可以看type也是由type类产生的"""
产生类的两种方式
1.最常用的
class 类名():
	pass

2.由于所有的类都是有元类创建的,所以我们可以通过type这个类造出一个新的类,我们可以点击type源代码进去看一下:
在这里插入图片描述

可以看到当type创建类时,需要传递三个参数

#type(object_or_name, bases, dict)
#type('类名', '父类', '类的名称空间')

C1 = type("C1", (), {'school':'SH'})
obj = C1()
print(C1.__dict__) # {'school': 'SH', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'C1' objects>, '__weakref__': <attribute '__weakref__' of 'C1' objects>, '__doc__': None}
print(obj.__dict__) # {}

3.为什么要用元类?元类有什么用?

我们学习元类之后,可以高度对类做定制化

那么此时,我们已经可以控制类在生产过程中做的事情了,如上序:我们在生产类时执行了print操作,在平常可不能这样。

我们可以对类名进行限制,如:首字母必须大写

你能够直接修改元类的代码吗? 不能够直接修改元类的源码,我们是不是可以写一个子类,来继承type类。
然后在子类里面的__init__方法中书写定制化代码,所以,还需要在子类里面执行父类的__init__方法

class MyTypeClass(type):
    def __init__(self, cla_name, cla_bases, cla_dict):
        self.cla_name = cla_name
        self.cal_bases = cla_bases
        self.cla_dict = cla_dict
        if not cla_name.istitle():
            raise Exception('必须大写开头')
        super().__init__(cla_name, cla_bases, cla_dict)


class C1(metaclass=MyTypeClass):  """元类不能够直接被继承,需要指定metaclass=MyTypeClass"""
    def __init__(self):
        print('__init__')


C1()
元类的进阶用法
元类的__call__方法

对象() 的时候会调用产生对象的类中得__call__方法

推导:类名()? 会执行产生类的类中得__call__方法,其实就是元类type中得__call__方法
class MyClass(type):
    def __call__(self, *args, **kwargs):
        print(args) # ()  ('kevin', 20)
        print(kwargs) # {'name': 'kevin', 'age': 20} {}
        if args:
            raise Exception("必须使用关键字传参")
        print('MyClass.__call__')
        super(MyClass, self).__call__(*args, **kwargs)

class C1(metaclass=MyClass):
    def __init__(self,name, age):
        self.name = name
        self.age = age
        print('C1.__init__')
"""观察C1类里的__init__方法和MyClass里的__call__方法的执行顺序?"""
"""得出结论:在执行类的__init__方法之前,其实已经执行了元类的__call__方法了,既然这样,我们就可以在实例化对象的时候,对类做一些定制化"""
# C1('kevin', 20) # 现在是位置传参,我限制让你限制不允许位置传参,只能够通过关键字传参/
C1(name='kevin', age=20) # 现在是位置传参,我限制让你限制不允许位置传参,只能够通过关键字传参/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值