元类
元类的用途:自定义元类控制类的创建行为及类的实例化行为
Python 中一切皆为对象。
一切接对象,对象可以怎么用呢?
1、都可以被引用,x=obj
2、都可以当作函数的参数传入
3、都可以当作函数的返回值
4、都可以当作容器类的元素,l=[func,time,obj,1]
让我们先定义一个类,然后逐步分析
class People: def __init__(self, name, age): self.name = name self.age = age def say(self): print('%s say welcome to here' % self.name) p = People('ysg', 21) p.say() print(type(p)) # <class '__main__.People'> print(type(People)) # <class 'type'>
所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类OldboyTeacher得到的
如果一切皆为对象,那么类 Peope 本质也是一个对象,既然所有的对象都是调用类得到的,那么 Peope 必然也是调用了一个类得到的,这个类称为元类
总结:产生类的类称之为元类,默认使用 class 定义的类,他们的元类是 type。
exec的用法
参数1:字符串形式的命令
参数2:全局作用域(字典形式),如果不指定默认就是用 globals()
参数3:局部作用局(字典形式),如果不指定默认就是用 locals()
例子
g = { 'x': 'ysg', 'y': '123' } l = {} exec(""" global x,m x = 'ysging' m = 'pei' z = '21' """, g, l) print(g) # {'x': 'ysging', 'y': '123'..., 'm': 'pei'} print(l) # {'z': '21'}
元类定义的两种方式
方法一:class
class People: country = "China" def __init__(self, name, age): self.name = name self.age = age def func(self): print('%s say good' % self.name) p = People('ysg', '22') print(People) # <class '__main__.People'> print(p, p.name, p.age) # <__main__.People object at 0x0000020B0FC652E8> ysg 22
方法二:type
定义类的三要素:类名、类的基类、类的名称空间
class_name = 'People' class_bases = (object,) class_body = """ country = "China" def __init__(self, name, age): self.name = name self.age = age def func(self): print('%s say good' % self.name) """ class_dic = {} exec(class_body, globals(), class_dic) People1 = type(class_name, class_bases, class_dic) print(People1) # <class '__main__.People'> p1 = People1('ysg', '22') print(p1, p1.name, p1.age) # <__main__.People object at 0x0000020B0FC65470> ysg 22
自定义元类控制类的创建行为
实现检索类名称是否为大写,类中是否写入注释
class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): super(Mymeta, self).__init__(class_name, class_bases, class_dic) if not class_name.istitle(): raise TypeError('类名称首字母要求大写') if '__doc__' not in class_dic or not class_dic['__doc__'].strip(): raise TypeError('新建的类必须标有注释') class People(object, metaclass=Mymeta): # People = Mymeta(class_name,class_bases,class_dic) ''' 使用自定义元类控制类的创建行为 ''' country = "China" def __init__(self, name, age): self.name = name self.age = age def func(self): print('%s say good' % self.name)
自定义元类控制类的实例化行为
预备知识 __call__
class Foo: pass f = Foo() f() # TypeError: 'Foo' object is not callable
class Foo: def __call__(self, *args, **kwargs): print(self) print(args) print(kwargs) f = Foo() f(1, 2, 3, a=1, b=2, c=3) 结果: <__main__.Foo object at 0x000001DBE6B7F240> (1, 2, 3) {'a': 1, 'b': 2, 'c': 3}
由以上两个例子可以看出,在调用方式实际是自动调用了 __call__ 方法
所以可以得出,在使用元类控制类的实例化行为时:
元类内部也应该有一个 __call__ 方法,会在调用 Foo 时触发执行;Foo(1,2,x=1) 就相当于 Foo.__call__(Foo,1,2,x=1)
例子:自定义元类控制类实现单实例
预备知识:单实例
单实例:对象内部特征如果一样的情况下,就不要产生新的内存空间,实现共用一个空间。
非单实例:
class Mysql: def __init__(self): self.host = '127.0.0.1' self.port = 3306 m = Mysql() m1 = Mysql() print(m) # <__main__.Mysql object at 0x000001517C67F278> print(m1) # <__main__.Mysql object at 0x000001517C67F390>
方法一:绑定方法实现单实例
class Mysql: __singleins = None def __init__(self): self.host = '127.0.0.1' self.port = 3306 @classmethod def si(cls): if not cls.__singleins: obj = cls() cls.__singleins = obj return cls.__singleins m = Mysql.si() m1 = Mysql.si() print(m) # <__main__.Mysql object at 0x000001F1ECEC5278> print(m1) # <__main__.Mysql object at 0x000001F1ECEC5278>
方法二:元类实现单实例
class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): super(Mymeta, self).__init__(class_name, class_bases, class_dic) self.__singleins = None def __call__(self, *args, **kwargs): if not self.__singleins: obj = object.__new__(self) self.__init__(obj, *args, **kwargs) self.__singleins = obj return self.__singleins class Mysql(object, metaclass=Mymeta): def __init__(self): self.host = '127.0.0.1' self.port = 3306 m = Mysql() m1 = Mysql() print(m) # <__main__.Mysql object at 0x00000278E56552B0> print(m1) # <__main__.Mysql object at 0x00000278E56552B0>
异常处理
什么是异常:异常是错误发生的信号,一旦程序出错,并且程序没有处理这个错误,就会抛出异常,并且程序的运行随之终止
强调一:异常发生的条件如果是可预知的,此时应该用 if 判断预防异常
强调二:异常发生的条件如果是不可预知的,此时应该使用异常处理机制,try...except
错误分为两种:语法错误、逻辑错误
例子:指定要预防的报错类型
a = 'qwe' try: int(a) except ImportError: print('int 类型报错') 结果:ValueError: invalid literal for int() with base 10: 'qwe'
注意:当指定的类型和实际报错的类型不一致时,报错不会被预防,而是报出真实的报错信息
a = 'qwe' try: int(a) except ValueError: print('int 类型报错') # int 类型报错 print('拦截报错信息后的输出') # 拦截报错信息后的输出
当类型一致时,会拦截报错,并执行下面的语句,不会打断程序的运行
例子:异常的多分支
用途:被检测的代码块存在抛出多种异常的可能性,即为每种异常指定单独的处理逻辑
try: print('=====>1') a = 'qwe' int(a) print('=====>2') l = [1,2,3] l[100] print('=====>3') d = {} d['name'] print('=====>') except ValueError as e: print('----->',e) # -----> invalid literal for int() with base 10: 'qwe' except IndexError as e: print('----->', e) # -----> list index out of range except KeyError as e: print('----->', e) # -----> 'name' print('被检测代码块的代码') # 被检测代码块的代码
例子:万能异常处理——Exception
用途:被检测的代码块存在抛出多种异常的可能性,且我们只为其设置一种处理逻辑
try: print('=====>1') a = 'qwe' int(a) print('=====>2') l = [1,2,3] l[100] print('=====>3') d = {} d['name'] print('=====>') except Exception as e: print('统一的处理方法----->', e) # -----> invalid literal for int() with base 10: 'qwe' print('被检测代码块的代码') # 被检测代码块的代码
例子:else、finally
try: print('=====>1') a = 'qwe' # int(a) print('=====>2') l = [1,2,3] # l[100] print('=====>3') d = {} # d['name'] print('=====>') except Exception as e: print('----->', e) else: print('被检测代码块没有发生异常时执行') # 被检测代码块没有发生异常时执行 finally: print('不管被检测代码块没有发生异常时执行都执行') # 无论被检测代码块是否发生异常时都执行
finally 在回收资源时的运用
不论是否发生异常都执行回收操作
try: f = open('../test.txt', 'r', encoding='utf-8') print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) finally: f.close()
例子:主动触发异常——raise,异常类型(值)
class Foo: def __init__(self,name,age): if not isinstance(name,str): raise TypeError('名称必须要使用 str 类型') self.name = name self.age = age f = Foo(123,12)
例子:自定义异常类型
class MyException(Exception): def __init__(self, msg): super(MyException, self).__init__() self.msg = msg def __str__(self): return self.msg raise MyException('自定义异常类') # raise 包含 print(obj) 结果:__main__.MyException: 自定义异常类
例子:断言——assert
info = {} info['name'] = 'ysg' # info['age'] = 22 # 这一部分的判断 就叫做断言 # if 'name' not in info: # raise KeyError('name 的 key 不存在') # if 'age' not in info: # raise KeyError('age 的 key 不存在') # 使用 assert assert ('name' in info) and ('age' in info) # AssertionError if info['name'] == 'ysg' and info['age'] > 10: print('welcome')