一。反射
在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()
这样就只会创建一个对象了。