day24_7.30 反射与元类
一。反射
在python中,反射通常是指一个对象应该具备,可以检测修复,增加自身属性的能力。
而反射可以通过字符串对其进行操作。
其中涉及了4个函数,是四个普通 的函数:
hasattr(oop,str,none)判断该对象是否有某个属性,如果有返回其值,如果没有,返回第三参数,默认为none
getattr(oop,str) 获取该对象的某个属性的值
setattr(oop,str,key)将某个属性添加到该对象中
delattr(oop,str)将从对象中删除属性
class Test1:
def __init__(self,name,age):
self.name=name
self.age=age p=Test1('lzx',19)
print(hasattr(p,'lzx'))
print(getattr(p,'name','not this'))
print(setattr(p,'gender','male'))
print(p.__dict__)
print(delattr(p,'age'))
print(p.__dict__)
#False
#lzx
#None
#{'name': 'lzx', 'age': 19, 'gender': 'male'}
#None
#{'name': 'lzx', 'gender': 'male'}
如果对其对象的字典进行操作也能达到效果,但是语法复杂,不如函数简洁。
练习:制作一个终端指令:
class Windows:
def cd(self):
print('执行cd') def ls(self):
print('执行ls') def md(self):
print('执行md') win=Windows()
def run(oop):
while True:
cmd=input('输入你需要执行的指令:')
if cmd=='exit':
print('再见')
break
if not hasattr(oop,cmd):
print('没有该指令')
else:
getattr(oop,cmd)() run(win)
在程序编写时,不可能知道这个程序以后需要调用什么样的模块,使用什么样的类,在编写后也不易修改,所以,需要使用动态导入模块,对一个字符串中的模块信息进行处理:
import settings
import importlib#动态模块包
def run(oop):
while True:
cmd=input('输入你需要执行的指令:')
if cmd=='exit':
print('再见')
break
if not hasattr(oop,cmd):
print('没有该指令')
else:
getattr(oop,cmd)()
path, class_info = settings.classpath.rsplit('.', 1)#将路径切割成模块路径和类名
mk = importlib.import_module(path)#动态导入模块
cls = getattr(mk, class_info)#通过字符串在模块中寻找该类的名字,获取类
obj = cls()#实例化类
run(obj)#运行
以上就是动态导入模块,被导入模块的信息放在了settings中,这样即使在编写这段代码时不知道路径,也可以运行。
注意,对于一个模块来说,也是一个类,可以使用反射getattr方法寻找其中的类(名称空间里的值),倒数第三行可以这样使用。
二。元类metaclass
在python中,所以的东西都是对象,类可以实例化一个对象,而类也是由元类实例化而来的对象,所以类也是一个对象,默认情况下所有的类的元类都是type,包括object:
class Person:
pass p=Person()
print(type(p))
print(type(Person))
print(type(object))
#<class '__main__.Person'>
#<class 'type'>
#<class 'type'>
所以可以根据type实例化一个类出来,在源码中,元类 的初始化是这样的:
def __init__(cls, what, bases=None, dict=None):
其中,what表示类的名字,bases表示类的父类,dict表示类的名称空间。
cls_object=type('animal',(),{'name':'dog'})
class cls_object:
name='dog'
这两者都是定义了类。
元类的作用就是高度的自定义一个类,
例:在定义类时规定使用大写字母开头:
1,在源码中修改type,风险太高,不提倡。
2,新建一个自己的类,继承type,任何继承了type的类都是一个元类,可以用来自定义一个类。
其中可以使用metaclass=元类将一个类的元类指向它。
class Mytype(type):
def __init__(self,what,bases,dict):
super().__init__(what,bases,dict)
if not what.istitle():
raise Exception('no') class pdd(metaclass=Mytype):
pass
覆盖了type的初始化方法后就可以当作元类使用,再编写其限制。
在元类中,也有__call__方法,这个方法是在该类创建的对象被调用时,执行,而元类的对象是类,类被调用就是将类实例化的过程。
class Mytype1(type):
def __init__(self,what,bases,dict):
super().__init__(what,bases,dict) def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
#return super().__call__(*args, **kwargs)
class Per(metaclass=Mytype1): def __init__(self,name,age): self.name=name self.age=age e=Per('lzx',age=10) # print(e.name)
<class '__main__.Per'>
('lzx',)
{'age': 10}
如上,将元类终端__call__覆盖,输出其参数self,*args,**kwargs,可以发现self是生成的类对象,args是类实例化中 的任意参数,而kwargs是指定参数。
由于覆盖__call__方法后没有对其进行返回值,所以并没有实例化成功,需要调用父类方法。
例:在实例化对象时一定要使用指定参数,不能使用位置参数:
class Mytype1(type):
def __init__(self,what,bases,dict):
super().__init__(what,bases,dict) def __call__(self, *args, **kwargs):
if args:
raise Exception('not this')
return super().__call__(*args,**kwargs) class Per(metaclass=Mytype1):
def __init__(self,name,age):
self.name=name
self.age=age e=Per(name='lzx',age=10)
在__call__方法中可以以使用__new__方法new一个新的空对象,让对象使用类的初始化方法。这就是call的内部原理:
def __call__(self, *args, **kwargs):
obj=object.__new__(self)
self.__init__(obj,*args, **kwargs)
return obj
注意,一定要返回obj值。
补充new。
在元类中,也可以使用new方法创建类对象,
当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作 。
注意:,如果你覆盖了该方法则必须保证,new方法必须有返回值且必须是,对应的类对象。(如果传入了其他类,就不会运行init函数)
class Meta(type):
def __new__(cls, *args, **kwargs):
print(cls) # 元类自己
print(args) # 创建类需要的几个参数 类名,基类,名称空间
print(kwargs) #空的
print("new run")
# return super().__new__(cls,*args,**kwargs)
obj = type.__new__(cls,*args,**kwargs)
return obj #返回对应 的类后会执行init方法
def __init__(self,a,b,c):
super().__init__(a,b,c)
print("init run")
class A(metaclass=Meta):
pass
print(A)
三。单例设计模式
在类中,可以创建很多对象,而当一个类产生的值是一模一样时,就不需要创建那么多的类,所以就有单例设计模式。
单例就是一个类只产式一个对象。
这样的设计可以节省资源,当一个类的所有对象属性全部相同时,就没必要创建多个对象。
class Single:
def __init__(self,name,age):
self.name=name
self.age=age def say_hi(self):
print('hi') s1=Single('lzx',24)
s1.say_hi() s2=Single('lzx',24)
s2.say_hi()
可以看到,由single创建的s1,s2都是一样的属性,但是却创建了两个不同的对象,浪费空间,可以使用单例设计模式,将两次实例化的对象变成一个。
class Single1:
def __init__(self,name,age):
self.name=name
self.age=age def say_hi(self):
print('hi') @classmethod
def get_single(cls):
if hasattr(cls,'obj'):
return getattr(cls,'obj')
obj=cls('lzx',18)
cls.obj=obj
return obj
s3=Single1.get_single()
s3.say_hi() s4=Single1.get_single()
虽然实现了单例,但是不能使用原来的方法调用该类,所以,可以使用该类的元类中 的call完成功能:
class Single2(type):
def __call__(self, *args, **kwargs):
if hasattr(self,'obj'):
return getattr(self,'obj')
obj=super().__call__( *args, **kwargs)
self.obj=obj
print('new')
return obj class test_sin(metaclass=Single2):
def __init__(self,name,age):
self.name=name
self.age=age def say_hi(self):
print('hi') s5=test_sin('lzx',12)
s5.say_hi() s6=test_sin('lzx',12)
s6.say_hi()
这样就只会创建一个对象了。
day24_7.30 反射与元类的更多相关文章
- python3全栈开发-内置函数补充,反射,元类,__str__,__del__,exec,type,__call__方法
一.内置函数补充 1.isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(object): pass obj = Foo() print(isinstan ...
- python面试题~反射,元类,单例
1 什么是反射?以及应用场景? test.py def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') def f4(): ...
- python基础--反射、元类、单例设计模式
反射:reflect,反射指的是一个对象应该具备可以检测.修改.增加自身属性的能力,反射就是通过字符串操作属性 hasattr(对象,带查询的属性名称) 判断某个对象中是否存在某个属性 getattr ...
- python元类深入解析
元类 什么是元类 元类是类的类,是类的模板(就如对象的模板是类一样) 元类的实例为类,类的实例为对象 元类是用来产生类的 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,是运行时动 ...
- 04 -- 元类和ORM
本篇主要介绍元类,为什么说一切皆对象:如何动态的创建类等:以及ORM,即什么是ORM等知识 一.元类 1.1 在Python中一切皆对象 在学习元类中我们首先需要了解一个概念-- python中一切皆 ...
- Python 元类实现ORM
ORM概念 ORM(Object Ralational Mapping,对象关系映射)用来把对象模型表示的对象映射到基于 SQL 的关系模型数据库结构中去.这样,我们在具体的操作实体对象的时候,就不 ...
- python基础之反射内置方法元类
补充内置函数 isinstance(obj,Foo) # 判断obj是不是foo的实例 issubclass() # 判断一个类是不是另一个类的子类 反射 什么是反射? 通过字符串来操作 ...
- QObject提供了QMetaObject元类信息(相当于RTTI和反射),信号与连接,父子关系,调试信息,属性,事件,继承关系,窗口类型,线程属性,时间器,对象名称,国际化
元类信息(相当于RTTI和反射),信号与连接,父子关系,调试信息,属性,事件,继承关系,窗口类型,线程属性,时间器,对象名称,国际化其中元类又提供了:classInfo,className,构造函数, ...
- 内置函数、反射、__str__、__del__、元类
一.内置函数的补充 isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo: pass obj=Foo() print(isinstance(obj,Foo) ...
随机推荐
- LG2444/BZOJ2938 「POI2000」病毒 AC自动机
问题描述 LG2444 BZOJ2938 I \(\mathrm{AC}\)自动机 \(\mathrm{AC}\)自动机是一种多模式串匹配算法,本萌新今天刚学了它qwq 约定在构造\(\mathrm{ ...
- Java List<T> 去重
1.List<T>,是个泛型,实际业务里,它经常是一个bean,例如Person类,里面有age.name等属性. 2.如果List<Person> ps 有重复的数据,我们 ...
- linux操作系统 - 综合习题
登录超级用户,完成以下操作: [linux@slave053 ~]$ su - 1.用户和组群管理(本大题共5小题,共10分) (1)创建两个用户tangseng,monkey,并指定密码为12345 ...
- Python连载19-装饰器
一.检视一个函数相同的另一种方法 利用属性:函数._name def hello(): print("我是一个测试程序") f = hello print(f.__name__) ...
- ICP 匹配定位算法学习记录
icp 算法原理是: 选取目标点云P和源点云Q,按照一定的约束条件,找到最邻近点(pi,qi),然后计算出最优R和t(旋转和平移), 使得误差函数最小,误差函数E(R,t): 基本算法流程: 1.在目 ...
- 有状态 Vs 无状态
NET Core 分布式框架 公司物联网项目集成Orleans以支持高并发的分布式业务,对于Orleans也是第一次接触,本文就分享下个人对Orleans的理解. 这里先抛出自己的观点:Orleans ...
- 洛谷 P2656 (缩点 + DAG图上DP)
### 洛谷 P2656 题目链接 ### 题目大意: 小胖和ZYR要去ESQMS森林采蘑菇. ESQMS森林间有N个小树丛,M条小径,每条小径都是单向的,连接两个小树丛,上面都有一定数量的蘑菇.小胖 ...
- C++:Name Lookup & Best Match
名字查找 每当一个变量或者一个对象出现,编译器都会进行名字查找(name lookup),以确认这个变量或对象的具体属性.一般情况下,程序会从变量出现的地方开始向上查找,由内向外查找各级作用域直到全局 ...
- 使用Git Bash在码云上上传和下载代码
前提是在码云上已经新建一个空的项目 1.新建一个目录,存放下载下来的项目,我在D盘新建了一个"gitspace"文件夹,用来存放下载下来的项目 2.进入刚刚新建的文件夹,即进入&q ...
- OpenGL入门1.2:渲染管线简介,画三角形
每一个小步骤的源码都放在了Github 的内容为插入注释,可以先跳过 图形渲染管线简介 在OpenGL的世界里,任何事物是处于3D空间中的,而屏幕和窗口显示的却是2D,所以OpenGL干的事情基本就是 ...