元类 与 异常
万物皆对象
类是对象,可以把一个类当成普通对象来使用,比如存储到列表中,或者作为参数传给函数等等...
对象是通过类实例化产生的 类对象 是由type实例化产生的
class AClass: pass print(type(AClass)) 我们可以手动调用type来实例化产生一个类 一个类由三个部分组成 1.类的名称 我是谁 2.类的父类们 我从哪里来 3.类的名称空间 我有什么 type(类名,父类元组,名称空间字典) #返回一个新的类 type(对象) #将会返回这个对象的类型 所以:我们可以总结出 当你定义一个class时,解释器会自动调用type来完成类的实例化
# 模拟解释器创建类对象 def test1(a): print(a) def test2(self,b): print(self,b) class_name = "C" bases = (object,) name_dict = {"name":"jack","test1":test1,"test2":test2} C = type(class_name,bases,name_dict) # print(C) c1 = C() # print(c1) c1.test2(100)
exec 与 eval
exec:用于执行字符串形式的python代码 只要符合python都能执行 ,并且可以指定将执行产生的名字放入某个名称空间
eval 用于执行简单的表达式,不能有任何的特殊语法
class_text = """ class A: def test(self): print(self) """ loca2 = {} exec(class_text,None,loca2) print(loca2) #eval(class_text) #报错
元类
什么是元类:用来产生类的类 被称之为 元类 用 metaclass 来表明
我们在定义元类时,尽量在类名后添加MetaClass 方便阅读
用来干什么: 当我们需要高度定制累时用, eg 限制类名必须大写开头等等....
控制类的创建过程 控制对象的实例化过程
但是元类type 中的代码无法被修改只能创建新的元类(继承自type)通过覆盖__init__来完成对类的限制。
元类的使用:
元类的自定义:
class MyMetaClass(type): pass # 使用自定义元类 class Person(metaclass=MyMetaClass): pass
1):__init__方法:
实例化对象时自执行__init__方法;因为类也是对象, 所以在实例化类对象时会自执行元类中的__init__方法,并且传入类的三个必要参数(类的名字,父类们,名称空间),当然会自动传入类对象本身作为第一个参数
eg:
#限制类名必须首字母大写 控制类中方法名必须全部小写 class MyMetaClass(type): def __init__(self,class_name,bases,name_dict): super().__init__(class_name,bases,name_dict) # 类名必须首字母大写 否则直接抛出异常 if not class_name.istitle(): print("类名必须大写 傻x!") raise Exception # 控制类中方法名必须全部小写 for k in name_dict: if str(type(name_dict[k])) == "<class 'function'>": if not k.islower(): raise Exception pass # 会自动调用其元类中的 __init__ 方法传入 类对象本身 类名称 父类们 名称空间 class Student(object,metaclass=MyMetaClass): # MyMetaClass("Student",(object,),{}) NAME = 10 def say(self): print("SAY") pass
2):__new__方法:
用于创建一个类对象,元类中的new方法会在创建类对象时执行,并且先于init方法;
new方法与init方法的区别:
1)执行 __new__方法----------拿到一个类对象
2)执行 __init __方法-----------传入类对象以及其他的属性,进行初始化
**如果覆盖了new一定要调用type中的new并返回执行结果
init方法在调用init方法前类对象已经创建完成了,而new方法他会在创建类时执行,如果有问题,就不会创建类了,一般用于对性能要求高时
#要求每个类必须包含__doc__属性 __doc__ 用于访问一个对象的注释信息 # 你要控制类的创建 那就自定义元类 覆盖__init__ class DocMeatClass(type): def __init__(self,class_name,bases,name_dict): super().__init__(class_name,bases,name_dict) # if not("__doc__" in name_dict and name_dict["__doc__"]): # raise Exception if not self.__doc__: raise Exception class Person(metaclass=DocMeatClass): pass
3):__call__方法:
用于控制对象的创建过程,元类中的call方法会在控制类时执行
class MyMeta(type): # 获得某个类的实例 def __call__(self, *args, **kwargs): print("call") # return super().__call__(*args,**kwargs) new_args = [] for i in args: if isinstance(i,str): new_args.append(i.upper()) else: new_args.append(i) return super().__call__(*new_args,**kwargs) # 注意注意注意: __new__ __init__ 是创建类对象时还会执行 # __call__ 类对象要产生实例时执行 class Student(metaclass=MyMeta): def __init__(self,name,gender,age): self.name = name self.gender = gender self.age = age s = Student("jack","woman",18) print(s.age) print(s.gender) class Person(metaclass=MyMeta): def __init__(self,name,gender): self.name = name self.gender = gender p = Person("rose","man") print(p.name)
小结:控制类的创建过程 使用__init
控制对象的实例化过程 使用 call 也需要调用type的call 并返回其结果
__new 会先于__init执行 在__new中必须 调用type的__new 并返回结果
元类实现单例模式:
单列:只有一个实例对象的类
优点:可以 节省空间 比如当某个累的所有对象特征和行为完全一样时,可以避免重复创建对象而浪费资源
案例:要定制类是 ```python class SingletonMetaClass(type): #创建类时会执init 在这为每个类设置一个obj属性 默认为None def __init__(self,a,b,c): super().__init__(a,b,c) self.obj = None # 当类要创建对象时会执行 该方法 def __call__(self, *args, **kwargs): # 判断这个类 如果已经有实例了就直接返回 从而实现单例 if self.obj: return self.obj # 没有则创建新的实例并保存到类中 obj = type.__call__(self,*args,**kwargs) self.obj = obj return obj ```
异常
异常:程序运行过程中发生的非正常情况,是错误发生时的信息。
如果没有正确处理异常的话,会导致程序被终止,对用户来说体验是非常差的。所以为了避免这种情况只能提高程序的的健壮性
异常的分类:
语法检测异常:是py解释器在执行代码前检查语法时检查出的错误 。程序员不应该出现
运行时异常:是通过语法检测,在执行代码过程中发生的异常
常见的异常:
-
-
IOError 输入/输出异常;无法打开文件或无法读写
-
ImportError 无法引入模块或包;基本上是路径问题或名称错误
-
SyntaxError Python语法错误异常,代码不能编译
-
IndentationError 缩进异常;代码没有正确缩进
-
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
-
KeyError 试图访问字典里不存在的键
-
KeyboardInterrupt Ctrl+C被按下
-
NameError 使用一个还未被赋予对象的变量
-
TypeError 传入对象类型与要求的不符合
-
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它
-
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
eg ``` TypeError: 'int' object is not subscriptable 对象不能被切片 TypeError: 'list' object is not callable 对象不能被调用 IndexError: list index out of range 索引超出范围 TypeError: 'builtin_function_or_method' object is not iterable 对象不能被迭代 KeyError: 'xxx' 不存在这个key FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx' 文件找不到 ```
异常的组成:
Traceback (most recent call last): 是异常追踪信息 用于展示错误发生的具体位置 以及调用的过程
File "F:/python8期/课堂内容/day29/11.常见异常.py", line 22, in <module>
with open("xxxxx") as f: 其中 包括了 错误发生的模块 文件路径 行号 函数名称 具体的代码
FileNotFoundError: [Errno 2] No such file or directory: 'xx' 前面是错误的类型 后面 错误的详细信息 在查找错误时 主要参考的就是详细信息
异常的处理:
必须掌握的语法
try:
可能会出现异常的代码 放到try里面
except 具体异常类型 as e:
如果真的发生异常就执行except
如何正确处理异常:
首先发生异常时不是立马加try,而是找出异常解决异常;try仅仅在你知道为甚么发生异常却无法避免的情况下用 比如说:你明确告诉用户要正确的文件路径,但用户给了你错误的路径。那就只能用try来保证程序正常结束
总之:能不用try就不用try
自定义异常:
当系统提供的异常类不能准确描述错误原因时就可以自定义异常类了
#定义 class MyException(Exception): pass #使用 raise MyException("这是一个自定义异常类型!") """ Traceback (most recent call last): File "/Users/jerry/PycharmProjects/work/test.py", line 167, in <module> raise MyException("这是一个自定义异常类型!") __main__.MyException: 这是一个自定义异常类型! """
主动抛出异常:
什么时候做?当我们的程序在执行中由于接收的参数不正确或者其他情况导致后续代码无法执行时可以主动抛出异常
主动抛出异常使用 raise关键字,后面可以看跟任何Exception的子类 或是
# 直接抛出异常 raise TypeError # 直接抛出异常 并附加错误信息 raise TypeError("类型错误")
断言 :断定的意思,当一些代码的执行必须满足某个条件是,就需要县断定条件再继续执行
存在目的:为简化if 判断而生的