当前位置: 首页 > news >正文

python全栈学习记录(二十四)元类、异常处理

元类、异常处理

文章目录

  • 元类、异常处理
  • 一、元类
    • 1.元类控制类的实例化
    • 2.属性/方法的查找顺序
    • 3.单例
  • 二、异常处理

一、元类

1.元类控制类的实例化

类的__call__方法会在产生的对象被调用时自动触发,args和kwargs就是调用实例时传入的参数,返回值是调用实例以后的结果。

class A():def __call__(self, *args, **kwargs):print("from __call__")print(args,kwargs)return 333a=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 objclass People(object,metaclass=Mymeta):country='China'def __init__(self,name,age):self.name=nameself.age=ageprint(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单例@classmethoddef from_conf(cls):if cls.__instacne is None:cls.__instacne=cls(cls.__ip)return cls.__instacneobj1=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.__instancereturn cls(*args,**kwargs)return wrapper@singleton
class Single:def __init__(self,ip):self.ip=ipobj1=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_dicdef __init__(self,class_name,class_bases,class_dic):#继承type的__init__方法初始化Singlesuper(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.__instanceobj=self.__new__(self)self.__init__(obj,*args,**kwargs)return objclass Single(metaclass=Mymeta):def __init__(self,ip):self.ip=ipobj1=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=datadef __str__(self):return self.datatry:raise NagaException('自定义错误')
except NagaException as e:print(e)
<<<自定义错误
http://www.lryc.cn/news/461007.html

相关文章:

  • Golang Slice扩容机制及注意事项
  • 华为OD机试 - 猜数字 - 暴力枚举(Python/JS/C/C++ 2024 E卷 100分)
  • Flink触发器Trigger
  • 【操作系统的使用】Linux 系统环境变量与服务管理:设置与控制的艺术
  • 速盾:高防cdn配置中性能优化是什么?
  • Qt_软件添加版本信息
  • mallocfree和newdelete的区别
  • 无锁队列实现(Michael Scott),伪代码与c++实现
  • 猜数字小游戏
  • 在Windows上搭建ChatTTS:从本地部署到远程AI音频生成全攻略
  • 如何用好 CloudFlare 的速率限制防御攻击
  • Unity3D 立方体纹理与自制天空盒详解
  • 【工具】VSCODE下载,配置初次设置
  • vue使用jquery的ajax,页面跳转
  • 基于微信小程序的社区二手交易系统的详细设计和实现(源码+lw+部署文档+讲解等)
  • D34【python 接口自动化学习】- python基础之输入输出与文件操作
  • 【Linux系列】set -euo pipefail 命令详解
  • 【Python爬虫实战】正则:中文匹配与贪婪非贪婪模式详解
  • 保护数据安全:JS前端加密与PHP后端解密实战教程,让敏感信息更安全
  • 72 分布式锁
  • 使用Windbg分析dump文件排查C++软件异常的一般步骤与要点分享
  • 30 天 Python 3 学习计划
  • 【MATLAB实例】批量提取.csv数据并根据变量名筛选
  • 【软件】Ubuntu下QT的安装和使用
  • 在Spring Boot中具有多个实现的接口正确注入的六种方式
  • 登陆微软账户太慢了,如何解决
  • Vue3动态组件component不生效问题解决方法
  • 算力基础篇:从零开始了解算力
  • Redis 万字入门教程
  • LeetCode :LCR 173. 点名