博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python第二十八天,(元类,异常处理,)
阅读量:4342 次
发布时间:2019-06-07

本文共 8416 字,大约阅读时间需要 28 分钟。

元类的介绍:

1.什么时元类?

  在python中,一切皆对象,类也是对象,可以把一个类当成普通对象来使用,比如存储到列表中,或者作为参数传给函数等等。

对象时如何产生的?

  通过类实例化产生的

  类对象是由type实例化产生的

obj = type("TestClass",(object,),{})print(obj)         # 

 

一个类由三个部分组成:

  1.类名,2.类的基类(object) 3. 类的名称空间

而使用type(类名,继承的父类,名称空间字典) 可以获得一个新的类。

所以,总结出来,当定义一个class时,解释器会自动调用type来完成类的实例化

 案例:

 

# 模拟解释器创建类的过程obj = type('TeatClass', (object,), {})print(obj)  # 
得到一个类# 名称空间也可以放实际的函数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) # 创建一个新的类,c1 = C()c1.test2(100) # 可以通过点语法来调用定义的方法。

 

 

 

exec与eval

exec:执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码。

语法:exec(object, globals, locals )

  object : 必选参数,表示需要被指定的Python代码。它必须是字符串或code对象。如果object是一个字符串,该字符串会先被解析为一组Python语句,然后在执行(除非发生语法错误)。如果object是一个code对象,那么它只是被简单的执行。

 

  globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象。

 

  locals:可选参数,表示当前局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与globals相同的值。

返回值

exec 返回值永远时None

 语法:

glob = {}locl = {}code = '''def test(a):    print(a)'''print(exec(code,glob,locl))

 

eval: 用来执行一个字符串表达式,并返回表达式的值,但是不能由任何特殊的语法

 

 

元类:metaclass

用于产生类的类,称之为元类

元类翻译为:metaclass , 所以一般在定义元类时,尽量在类名后面添加MetaClass,方便阅读

 

元类的作用:

  当我们需要高度定制类时,比如限制类名,或着属性的名字等等。就需要使用元类,不过因为原类type中的代码无法被修改,所以我们一般创建新的元类方法是使用继承自type的子类,通过覆盖__init__来完成对类的限制

 

自定义元类的语法:

# 默认创建类时,是找的type来实例化的class MyMetaClass(type):    pass# 使用metaclass关键字来定义原类的继承class Person(metaclass=MyMetaClass):    passclass Student:    def __init__(self):        passprint(type(Person))     # 
print(type(Student)) #
# 创建对象也是一样,会先创建空的类对象,再调用__init__方法# Person = MyMetaClass()

 

 

1.__init__方法*****(重点)

  实例化对象时会自动执行类中的__init__方法,类也是对象,在实例化类对象时会自动执行元类中的__init__方法

  并且传入类的三个必要参数:类的名字,继承的父类们,名称空间

  会自动传入类对象本身作为第一个参数(self)

自定义原类案例:判断类名首字母是否为大写,判断类中的方法名称是否都为小写

class MyMetaClass(type):    def __init__(self, class_name, bases, name_dict):        # 元类中的self表示的都是类对象        # 不要忘记调用父类的初始化        super().__init__(class_name, bases, name_dict)        print(name_dict)        # 判断类名,必须首字母大写,否则直接抛出异常        if not class_name.istitle():            print('类名首字母必须大写')            raise Exception        # 控制类中方法名必须全部小写        # 遍历类中的名称空间,如果发现方法有大写,则报错        for k in name_dict:            if str(type(name_dict[k])) == "
": if not k.islower(): print('方法名称必须全小写') raise Exception# 会自动调用其元类中的__init__方法传入 类对象本身 类名称 父类们 名称空间class Student(object, metaclass=MyMetaClass): # # MyMetaClass("Student",(object,),{}) NAME = 10 def say(self): print('SAY') pass

案例:需求:要求每个类必须包含__doc__属性,__doc__用于访问一个对象的注释信息

# 如果你要控制类的创建,那就自定义元类 覆盖__init__class DocMeatClass(type):    def __init__(self,class_name,bases,name_dict):        super().__init__(class_name,bases,name_dict)        if not self.__doc__:            raise Exceptionclass Person(metaclass=DocMeatClass): pass

 

 

2.__new__方法*****(重点)

元类中的new方法会创建类对象时执行,并且先于init方法,它的作用是创建一个类对象

执行步骤:1.执行__new__方法,拿到一个类对象, 2. 执行__init__方法,传入类对象以及其他属性,进行初始化

 

注意:定义类的时候,本身有一个默认的__new__,如果覆盖了__new__一定也要调用type中的__new__并返回执行结果,不然无法定义类

 

使用new方法也可以完成定制类的工作,和init有什么区别?

  在调用__init__方法前,类对象已经创建完成了,所以如果对性能要求高的话,可以选择在__new__中完成定制,如果发现有问题,就不用创建类对象了

 

语法:

class MyMetaClass(type):    # 传入类的三大组成部分    def __init__(self, class_name, bases, name_dict):        super().__init__(class_name, bases, name_dict)        print('init')    # 该方法会在实例化类对象时自动调用并且优先在__init__之前调用    # 其作用是用于创建新的类对象的    # 注意这里必须调用type类中的__new__否则将无法产生类对象 并且返回其结果    def __new__(cls, *args, **kwargs):        # cls 表示元类自己,即MyMetaClass        return type.__new__(cls, *args, **kwargs)  # 类本身有一个__new__方法,如果覆盖__new__一定要将__new__返回class Person(metaclass=MyMetaClass):    passprint(Person)    # None 如果__new__后面没有 return type.__new__(...),则返回None,代表没有生成类对象             # 反之,则 
 
# 就算__init__中什么都不写 这个类对象其实已经创建完成了 该有的属性都有了 # 这是与普通类不同之处

 

 

3.__call__方法*****(重点)

元类中的__call__方法会在调用类时执行,可以用于控制对象的创建过程

总结:当你要定制类时,就自定义元类并覆盖__init__方法

 案例:使传入的字符串全部改为大写

 

class MyMeta(type):    # 获得某个类的实例    def __call__(self, *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 = ages= Student('jack','man',18)print(s.age)class Person(metaclass=MyMeta):    def __init__(self,name,gender):        self.name = name        self.gender = genderp = Person('rose','woman')print(p.gender)

 

 

 

元类实现单例模式

什么是单例:

  某个类如果只有一个实例对象,那么该类成为单例类

单例的好处:

  当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源,如果是避免重复创建对象

案例:

 

# 实例化传入相同参数,得到的实例地址不同,浪费资源class People:    def __init__(self, name, age):        self.name = name        self.age = agep1 = People('李果',25)  # <__main__.People object at 0x000002A28FC38400>p2 = People('李果',25)  # <__main__.People object at 0x000002A28FC389B0>p3 = People('李果',25)  # <__main__.People object at 0x000002A28FC38AC8>print(p1)print(p2)print(p3)

 

 

 

单列需要与__call__一起使用,在__call__内部加判断

案例:

class SingletonMetaClass(type):    # 创建类时会执行init 在这为每个类设置一个obj属性,默认为None    def __init__(self, a, b, c):        super().__init__(a, b, c)        self.obj = None    # d当类要创建对象时会执行,该方法    def __call__(self, *args, **kwargs):        # 判断这个类,如果已经有实例了就 直接返回,从而实现单例        if self.obj:            return self.obj        # 没有则创建新的实例并保存到类中        obj = type.__call__(self, *args, **kwargs)        self.obj = obj        return objclass Person(metaclass=SingletonMetaClass):    def __init__(self, name, age, gender):        self.name = name        self.age = age        self.gender = gender    def say(self):        print('my name is %s my age is %s my gender is %s' % self.name,self.age,self.gender)stu1 = Student("布兰", 16, "man")stu2 = Student("布兰", 16, "man")stu3 = Student("布兰", 16, "man")print(stu1, stu2, stu3) # 地址完全一样

 案例二:生成单例播放器

 

class Single(type):    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 objclass QQPlayer(metaclass=Single):    def __init__(self, voice_value, repeat=False):        self.voice_value = voice_value        self.repeat = repeat    def play(self, file_path):        if hasattr(self, 'file_path'):            self.stop()        print('正在播放%s' % file_path)        self.file_path = file_path    def stop(self):        print('%s停止播放' % self.file_path)playerl = QQPlayer(100,True)playerl.play('如果我是DJ.mp3')player2 = QQPlayer(100,True)player2.play('你会爱我吗.mp3')player3 = QQPlayer(100,True)player3.play('我的滑板鞋.mp3')

 

 

   异常

1.什么是异常

  异常是程序运行过程中发生的非正常情况,是一个错误发生时的信号,异常如果没有被正常处理的话,将导致程序被终止,这对于用户体验是非常差的,可能导致严重的后果。处理异常的目的就是提高程序的健壮性

2.异常的分类

  

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'      不存在这个keyFileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'  文件找不到

 

3.异常的组成

Traceback (most recent call last):  File "F:/python8期/课堂内容/day29/11.常见异常.py", line 22, in 
with open("xxxxx") as f:FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'

Traceback : 是异常追踪信息,用于展示错误发生的具体位置,以及调用的过程,其中包括了错误发生的  模块  ,  文件路径  ,行号  ,函数名称  ,具体的代码  

最后一行: 前面是错误的类型,后面是错误的详细信息,在查找错误时,主要参考的就是详细信息

 

4.异常的处理

  异常发生后,如果不正确处理将会导致程序终止,所以我们应该尽量的避免这种情况发生

必须掌握的语法:

try:

  可能会出现异常的代码,放到try里面

except 具体的类型 as e:

  如果真的发生异常就会执行except

5.如何正确处理异常

  1.当发生异常时,不是立马加try, 要先找出错误原因并解决它

  2.try 仅在 即使你知道为什么发生错误,但是你却无法避免

    例如,你明确告诉用户需要一真确的文件路径,然而用户依然传入了错误的路径

    如 socket 双方都要使用管道,但是如果以访由于某些原因强行关闭了,即使你只要原因也无法避免出错,那就只能try 保证程序正常结束

    能不加try 就不加 try

 

 

6.自定义异常类:

  当系统提供异常类不能准确描叙错误原因时,就可以自定义异常类,继承自Exception即可

 

class MyException(Exception) :

  pass

 

7.主动抛出异常

什么时候需要主动抛出异常?

  当我们做功能的提供者,给外界提供一个功能接口

  但是使用者不按照相应的方式来使用,或者参数类型不正确等原因,导致功能无法正常执行时,就应该主动抛出异常

主动抛出异常使用 raise 关键字

后面可以跟任何Exception的 子类 或是 对象。

 

raise MyException

raise MyException("错误具体原因!")

 

8.断言assert

断言,其实可以理解为断定的意思

即非常肯定某个条件是成立的

条件是否成立其实可以使用 if 来判断

其存在的目的就是为了简化 if 判断而生的

 

转载于:https://www.cnblogs.com/liguodeboke/p/10920571.html

你可能感兴趣的文章
阿里百川SDK初始化失败 错误码是203
查看>>
透析Java本质-谁创建了对象,this是什么
查看>>
BFS和DFS的java实现
查看>>
关于jquery中prev()和next()的用法
查看>>
一、 kettle开发、上线常见问题以及防错规范步骤
查看>>
eclipse没有server选项
查看>>
CRC码计算及校验原理的最通俗诠释
查看>>
使用Gitbook来编写你的Api文档
查看>>
jquery扩展 $.fn
查看>>
Markdown指南
查看>>
influxDB的安装和简单使用
查看>>
JPA框架学习
查看>>
JPA、JTA、XA相关索引
查看>>
机器分配
查看>>
php opcode缓存
查看>>
springcloud之Feign、ribbon设置超时时间和重试机制的总结
查看>>
观看杨老师(杨旭)Asp.Net Core MVC入门教程记录
查看>>
UIDynamic(物理仿真)
查看>>
Windows下安装Redis
查看>>
winform非常实用的程序退出方法!!!!!(转自博客园)
查看>>