今日内容:
1.元类
2.异常
Python:一门动态语言
最大特点:运行期间动态生成类,修改对象属性
详解:
1.元类:
1.1什么是元类?
一切皆对象
类也是对象,可以把一个类当作普通对象对象来使用,(对象怎么用,类就怎么用)
使用:
例如,存储到列表中,或者作为参数传给函数等等
对象是如何产生的?
通过实例化产生的
类对象:
是由type实例化产生的
案例:
class Aclass:
pass
print(type(Aclass))
#值<class 'type'>
我们可以手动调用type来实例化产生一个类
一个类由三部分组成:
1.类的名称 我是谁
2.类的父类们 我从哪里来
3.类的名称空间 我有什么
type的两种使用方法:
.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)
#值:产生一个新的类<class '__main__.c'>
c1=c()
print(c1)
#值:<__main__.c object at 0x00000256F4337A90>
c1.test2(100)
#值:<__main__.c object at 0x000002405D6A7A90> 100
1.2补充:解释器执行代码的时候就是exec
exec(执行)与eval(评价)
glob全局名称空间 locl局部名称空间
exec用于执行字符串形式的Python代码 只要符合Python都能执行,并且可以指定将执行产生的名字放入某个名称空间
eval用于执行简单的表达式,(加减,基础运算)不能有任何的特殊语法
class_test="""
class A:
def test(self):
print(self)
"""#字符串形式的Python代码
loca2={}
exec(class_test,None,loca2)
#print(loca2)
#值{'A': <class '__main__.A'>}
eval(class_test)#语法错误
1.3元类(Type)(元类继承自object):用于产生类的类,称之为元类
元类翻译为:
metaclass 只要看见他就应该想起来这就是元类
我们在定义元类的时候,尽量在类名后添加MetaClass 方便阅读
1.4(应用)用来干啥?(三种方法:__init__,__new__,__call__)
当我们需要高度定制类的时候,如限制类名必须大写开头等等。。。。
就需要使用元类,但是元类type中的代码 无法被修改,只能创建新的元类(继承自type)通过覆盖__init__来完成对类的限制
使用元类
如何自定义元类:
class MyMetaCLass(type):
pass
#使用自定义元类
class Person(metaclass=MyMetaCLass):
pass
1.4.1__init__方法(重点)
实例化对象时会自动执行类中__init__方法,类也是对象,在实例化对象时会自动执行元类中__init__方法
并且传入类的三个必要参数,类的名字,父类们,名称空间
当然会自动传入类对象本身作为第一个参数
案例:说明
限制类名必须首字母大写 控制类中的方法名 必须全部小写
class MyMetaClass(type):
def __init__(self,class_name,bases,name_dict):
super().__init__(class_name,bases,name_dict)
#类名必须首字母大写,否则直接剖出异常
if not class_name.istitle():
print("类名必须大写")
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):
pass
1.4.2__new__方法(重点)
作用是创建一个类对象
class A(metaclass=MyMetaClass):
pass
1.执行MyMetaClass的__new__
方法 拿到一个类对象
2.执行MyMetaClass的__init__
方法 传入类对象以及其他的属性 ,进行初始化
注意:如果覆盖了__new__
一定也要调用type中的__new__
并返回执行结果
使用new方法也可以完成定制类的工作 和init有什么区别?
在调用init方法前类对象已经创建完成了
所以如果对性能要求高的话 可以选在在new中完成定制 如果发现有问题,就不用创建类对象了
需求: 要求每个类必须包含__doc__
属性(打印注释)
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
练习:
# 需求: 要求每个类必须包含__doc__属性 __doc__ (文档的缩写)用于访问一个对象的注释信息
# 需求: 要求每个类必须包含__doc__属性 __doc__ 用于访问一个对象的注释信息
# class A:
# """
# this is a Class
# author is jerry
# """
# pass
# print(A.__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
1.4.3__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方法
什么是单例:
某个类如果只有一个实例对象,那么该类成为单例类
单例的好处:
当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源
案例:
#创建类时会执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
2.1什么是异常
异常是程序运行过程中发生的非正常情况,是一个错误发生时的信号
异常如果没有被正确处理的话,将导致程序被终止,这对于用户体验是非常差的,可能导致严重的后果
处理异常的目的就是提高程序的健壮性
2.2异常的分类
python解释器在执行代码前会先检查语法,语法检查通过才会开始执行代码
1.语法检测异常 作为一个合格的程序员 是不应该出现这种低级错误
2.运行时异常
已经通过语法检测,开始执行代码,执行过程中发生异常 称之为运行时异常
2.3异常:
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' 文件找不到
2.4异常的组成:
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: 'xxxxx'
Traceback 是异常追踪信息 用于展示错误发生的具体位置 以及调用的过程
其中 包括了 错误发生的模块 文件路径 行号 函数名称 具体的代码
最后一行 前面是错误的类型
后面 错误的详细信息 在查找错误时 主要参考的就是详细信息
2.5异常处理
异常发生后 如果不正确处理将导致程序终止,我们必须应该尽量的避免这种情况发生
重点:
必须掌握的语法
语法:
try:
可能会出现异常的代码 放到try里面
except 具体异常类型 as e:
如果真的发生异常就执行except
如何正确处理异常
-
当发生异常 不是立马加try 要先找出错误原因并解决它
-
try 仅在 即使你知道为什么发生错误 ,但是你却无法避免
例如 你明确告诉用户 需要一个正确文件路径 然而用户依然传入了错误的路径如 socket 双方都要使用管道 ,但是如果一方有由于某些原因强行关闭了 ,即使你知道原因也无法避免出错 那就只能try 保证程序正常结束
总结一句话:能不加try 就不加try
2.6自定义异常类
当系统提供异常类不能准确描述错误原因时 就可以自定义异常类
继承自Exception即可
class MyException(Exception):
pass
主动抛出异常:
什么时候需要主动抛出异常
当我们做功能的提供者,给外界提供一个功能接口
但是使用者不按照相应的方式来使用,或者参数类型不正确等原因,导致功能无法正常执行时,就应该主动抛出异常
主动抛出异常使用raise 关键字
后面可以跟任何Exception的子类 或是 对象
raise MyException
raise MyException("错误具体原因!")
断言assert
断言 其实可以理解为断定的意思
即非常肯定某个条件是成立的
条件是否成立其实可以使用if来判断
其存在的目的就是 为了简化if 判断而生的
优先掌握的内容:
1.元类中的三个方法的执行时机以及使用场景
2.单例模式
3.异常处理语法
4.要注重提升解决bug能力
有什么用
如何处理
什么是元类
有什么用?
1.控制类的创建过程
2.控制对象的实例化过程
如何控制
控制类的创建过程 使用__init
控制对象的实例化过程 使用 call 也需要调用type的call 并返回其结果
__new 会先于__init执行 在__new中必须 调用type的__new 并返回结果