元类、异常处理
文章目录
- 元类、异常处理
- 一、元类
- 1.元类控制类的实例化
- 2.属性/方法的查找顺序
- 3.单例
- 二、异常处理
一、元类
1.元类控制类的实例化
类的__call__方法会在产生的对象被调用时自动触发,args和kwargs就是调用实例时传入的参数,返回值是调用实例以后的结果。
class A():
def __call__(self, *args, **kwargs):
print("from __call__")
print(args,kwargs)
return 333
a=A()
print(a(111,k=222))
<<<from __call__
<<<(111,) {'k': 222}
<<<333
一般情况下类的__call__函数会完成以下的几件事情:创建一个新对象、根据传入的值完成新对象的初始化、返回新建的对象。换句话说__new__和__init__两个方法是在__call__方法里面完成的。
基于此我们就可以通过复写元类的__call__方法完成对类实例化的控制效果。
class Mymeta(type):
def __call__(self, *args, **kwargs):
#因为是类调用的时候触发了__call__方法,所以此处的self是People
# 1、先造出一个People的空对象
#obj=object.__new__(self)
obj=self.__new__(self)
# 2、用People的初始化方法为新建的People对象完成初始化
self.__init__(obj,*args,**kwargs)
# 3、返回初始好的对象
return obj
class People(object,metaclass=Mymeta):
country='China'
def __init__(self,name,age):
self.name=name
self.age=age
print(People('111',222))
<<< <__main__.People object at 0x0000022AFFB9AE80>
上述代码中当调用People(‘111’,222)时,触发元类的__call__方法,首先会调用People的__new__方法产生一个People类的新对象,由于People没有__new__方法,根据继承的机制解释器会去父类object中找,新对象产生后就需要根据People类的初始化方法完成对象的初始化,所以我们调用People的初始化方法并将新产生的obj对象传入完成初始化操作,最后返回这个obj对象也就完成了实例化的操作了。我们完全可以在__call__方法的内部添加一些功能已完成实例的定制,例如下面要讲到的单例。
2.属性/方法的查找顺序
python类的属性/方法的查找顺序为:
- 先在对象层查找,当前对象找不到就去找父类(mro表)
- 如果父类也找不到再去对象上一级查找(注意实例和类、类和元类都是跨一级的关系)
- 但是属性/方法的查找最多只能跨一级(例如实例中调用属性,在对应类及其父类中找不到就结束了,因为实例到元类跨了两个级别,实例中调的属性无法去元类中查找)
根据上面的规则简单来讲就是:
- 在实例中查找:实例——对应的类——对应类的父类
- 在类中查找:类——父类——元类——元类的父类
python中继承和实例的关系如下:
- type是所有类的元类、也是自己的元类
- object是所有类的父类
- int、str、list等是object的子类,是type的实例
- 1、‘1’、[1]等是int、str、list的实例
- class创建的类是type的实例,是object的子类
3.单例
单例模式:基于某种方法实例化多次得到实例是同一个。
当实例化多次得到的对象中存放的属性都一样的情况,应该将多个对象指向同一个内存地址以减少占用的空间。
例如现在我需要传入一个ip地址,我们自动最常用的ip是127.0.0.1,如果需要传入的是127.0.0.1那么就直接调用单例,如果是其他ip就创建新实例。这个需求可以有三种方法实现:
方式一:内置属性法
class Single:
__instacne=None
#默认的ip值
__ip="127.0.0.1"
def __init__(self,ip):
self.ip=ip
#使用默认的ip单例
@classmethod
def from_conf(cls):
if cls.__instacne is None:
cls.__instacne=cls(cls.__ip)
return cls.__instacne
obj1=Single('127.10.0.10')
obj2=Single.from_conf()
obj3=Single.from_conf()
print(obj1,obj2,obj3)
<<< <__main__.Single object at 0x0000021A1F24AEB0> <__main__.Single object at 0x0000021A1F24ADF0> <__main__.Single object at 0x0000021A1F24ADF0>
方法二:装饰器
def singleton(cls):
#闭包函数内存一个传默认值的实例
cls.__instance=cls('127.0.0.1')
def wrapper(*args,**kwargs):
#不传值则返回存的单例
if len(args) == 0 and len(kwargs) == 0:
return cls.__instance
return cls(*args,**kwargs)
return wrapper
@singleton
class Single:
def __init__(self,ip):
self.ip=ip
obj1=Single('127.10.0.10')
obj2=Single()
obj3=Single()
print(obj1,obj2,obj3)
<<< <__main__.Single object at 0x000001F5E256AD60> <__main__.Single object at 0x000001F5E256AE80> <__main__.Single object at 0x000001F5E256AE80>
方法三:元类控制单例的产生
class Mymeta(type):
#初始化类的过程需要传入class_name,class_bases,class_dic
def __init__(self,class_name,class_bases,class_dic):
#继承type的__init__方法初始化Single
super(Mymeta,self).__init__(class_name,class_bases,class_dic )
#由于Single需要一个内置属性存放单例,所以在初始化类的过程中完成单例的创建
self.__instance = self.__new__(self) # 造出一个Mysql的对象
self.__init__(self.__instance, '127.0.0.1')
def __call__(self, *args, **kwargs):
#当实例化不传参时,返回单例,传参则正常完成实例化
if len(args) == 0 and len(kwargs) == 0:
return self.__instance
obj=self.__new__(self)
self.__init__(obj,*args,**kwargs)
return obj
class Single(metaclass=Mymeta):
def __init__(self,ip):
self.ip=ip
obj1=Single('127.10.0.10')
obj2=Single()
obj3=Single()
print(obj1,obj2,obj3)
<<< <__main__.Single object at 0x000001CA60BFCAF0> <__main__.Single object at 0x000001CA60BFCC40> <__main__.Single object at 0x000001CA60BFCC40>
二、异常处理
当程序运行过程中出现错误时,就会报错,这个就是异常。如果我们希望代码在可能发生错误的情况下依然能正常运行,就需要用到异常处理。但是需要注意异常处理并不是越多越好,如果程序中的错误是可以预知的,应该尽可能不使用异常处理,只有当程序中的错误是不可预知的,才需要使用到异常处理。
异常处理的常用格式:
#当try中的程序运行出现错误时会跳转到except处,根据except后的信息处理错误
#except ValueError表示出现ValueError错误时出现跳转到except处运行下面的代码,如果出现其他错误则直接报错
try:
代码块
except ValueError:
代码块
#同时接受多个错误
except (IOError,NameError):
代码块
#接受错误并打印错误信息
except ImportError as e:
#e为错误信息
print(e)
#except Exception表示接受所有的错误,Exception可以不写
except Exception:
代码块
#else表示try中的代码没有异常时运行,finally表示try except代码运行完以后运行,通常用来回收资源
try:
代码块
except:
代码块
else:
代码块
finally:
代码块
常见的错误形式:
断言:
#assert会在不满足后面的条件时报错,一般用于测试阶段
l=[1,2]
assert len(l)==3
<<<AssertionError
raise:
#程序运行至raise处时会抛出后面的错误
raise ValueError('找不到值')
<<<ValueError: 找不到值
自定义异常:
#自定义的错误类必须继承BaseException类
class NagaException(BaseException):
def __init__(self,data):
self.data=data
def __str__(self):
return self.data
try:
raise NagaException('自定义错误')
except NagaException as e:
print(e)
<<<自定义错误