文章目录
一、异常
异常是程序发生错误的信号。程序一旦出现错误,便会产生一个异常,若程序中没有处理它,就会抛出该异常,程序的运行也随之终止。在Python中,错误触发的异常如下
异常的分类:
1.逻辑错误:可以出现,但是编程的时候尽量避免逻辑错误的发生
2.语法错误:不允许出现
异常的组成:
- Traceback追溯信息,可以定位到错误发生的位置
- 错误类型:XXXError(TypeError、KeyError、IndexError等)
- 错误的原因:通过这部分我们可以精准找到错误的具体原因,这对我们解决问题来说是最重要的
解决异常的完整语法:
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) # 现在是位置传参,我限制让你限制不允许位置传参,只能够通过关键字传参/