面向对象高级——反射和元类
参考链接:https://www.cnblogs.com/yangyuanhu/p/11207450.html#autoid-0-0-0
一、反射
什么是反射,其实是反省,自省的意思
反射指的是一个对象应该具备,可以检测,修改,增加自身属性的能力,反射是通过字符串操作属性。
涉及的四个函数,这四个函数就是普通的内置函数,没有双下划线,与print等等没有区别。
class A: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def a(self): print('我是a') p = A('jack',18,'meal') print(hasattr(p,'name')) #判断是否存在返回True或者False setattr(p,'id',50) #设置属性id值为50 getattr(p,'a')() #getattr获取到函数名,加括号就是调用 print(getattr(p,'name')) #getattr获取到属性值 delattr(p,'id')#删除
#结果
True
我是a
jack
使用场景:
反射其实就是对属性的增删改查,但是如果直接使用内置的__dict__来操作,语法繁琐,不好理解。
另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法。
框架的设计方式:
为什么反射被称为框架的基石,
因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
所以你提供给框架的对象 必须通过判断验证之后才能正常使用
判断验证就是反射要做的事情,
当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装
二、元类 metaclass
元类是什么,用于创建类的类,万物皆对象,类也是对象
对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是有另一个类实例化产生的,默认情况下所有类的元类都是type
class Person: pass p = Person() print(type(p)) print(type(Person))
Person类是通过type类实例化产生的 #结果 <class '__main__.Person'> <class 'type'>
学习元类的目的:
高度的自定义一个类,例如控制类的名字必须以大驼峰的方式来写
类也是对象,也有自己的类,我们的需求是创建类对象做一些限制。
想到了初始化方法,我们只要找到类对象的类(就是元类),覆盖其中init方法就能实现需求,当然我们不可能去修改源代码,所以应该继承type来编写自己的元类,同时覆盖init来完成需求
重写init方法,一定要记得调用父类的init方法
一个类有三大组成部分,分别是
1、类名clss_name
2、基类们base,用()或者[]
3、类的名称空间dict {}
自定义元类控制类person的创建
""" 只要继承了type 那么这个类就变成了一个元类 """ # 定义了一个元类 class MyType(type): #继承type类才能成为一个元类,不然就是普通的自定义的类 def __init__(self,clss_name,bases,dict): super().__init__(clss_name,bases,dict) #重用父类的功能 print(clss_name,bases,dict) if not clss_name.istitle(): raise Exception("你丫的 类名不会写...") # 为pig类指定了元类MyType class Person(metaclass=MyType): #Person = MyType('Person',(object),...) def __init__(self,name,age)
self.name = name
slef.age = age
def say(self):
print('my name is %s'%self.name)
创建流程:自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程,即Person =MyType('Person',(object),...),调用MyType先会产生一个空对象Person,然后将MyType括号内的参数一同传给MyType下的__init__方法,完成初始化。
自定义元类控制类person的调用
补充:__call__方法
class A: def __call__(self, *args, **kwargs): print("call run") print(args) print(kwargs) a = A() a(1,a=100) #想要a对象变成一个可调用对象,需要在类中定义一个方法__call__方法,该方法在调用对象的时候会自动触发,a的返回值就是__call__方法的返回值 #结果 call run (1,) {'a': 100}
由上可知,调用一个对象的时候,就是触发所在类中的__call__方法,如果把Person也当做一个对象,那么在Person这个对象的类中也必然存在一个__calll__方法。
class MyType(type): def __call__(self, *args, **kwargs): new_args = [] for a in args: new_args.append(a.upper()) return super().__call__(*new_args,**kwargs) class Person(metaclass=MyType): def __init__(self,name,gender): self.name = name self.gender = gender p = Person(name="jack",gender="woman") print(p.name) print(p.gender)
#触发Person类中的__call__方法(使用MyType的),然后将Person传给self,调用Person的返回值就是调用__call__的返回值。
默认地,调用对象p会做三件事:
- 产生一个空对象obj (__new__方法)
- 调用__init__方法初始化对象obj
- 返回初始化好的obj return obj
对应着,Person类中的__call__方法也应该做着三件事
class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类 def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'> #1、调用__new__产生一个空对象obj obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个OldboyTeacher的对象obj #2、调用__init__初始化空对象obj self.__init__(obj,*args,**kwargs) #3、返回初始化好的对象obj return obj class OldboyTeacher(object,metaclass=Mymeta): school='oldboy' def __init__(self,name,age): self.name=name self.age=age def say(self): print('%s says welcome to the oldboy to learn Python' %self.name) t1=OldboyTeacher('egon',18) print(t1.__dict__) #{'name': 'egon', 'age': 18}
当你调用类对象时会自动启动元类中的__call__方法 ,并将这个类本身作为第一个参数传入,以及后面的一堆参数 覆盖元类中的call之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建 并返回其返回值
使用场景:
当你想要控制对象的创建过程时,就覆盖call方法
当你想要控制类的创建过程时,就覆盖init方法
class MyType(type): def __call__(self, *args, **kwargs): new_args = [] for a in args: new_args.append(a.upper()) return super().__call__(*new_args,**kwargs) class Person(metaclass=MyType): def __init__(self,name,gender): self.name = name self.gender = gender p = Person(name="jack",gender="woman") print(p.name) print(p.gender)
#实现将对象的所有属性名称转为大写
需要注意的是:一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象
__new__方法:
当你要创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后会自动调用__init__来对这个类进行初始化操作。
注意:如果你覆盖了该方法则必须保证,new方法必须有返回值并且必须是对应的类对象
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 def __init__(self,a,b,c): super().__init__(a,b,c) print("init run") class A(metaclass=Meta): pass print(A)
总结:new方法和init方法都可以实现控制类的创建过程,init更简单
元类总结:需要知道的魔法方法有:__call__,__new__,__init__
流程:
- 当调用对象的时候,首先会调用__call__方法,执行里面的代码
- 然后调用__new__产生一个空对象
- 调用__init__初始化空对象obj
- 最后返回初始化好的对象obj
需要注意的:可以重写__call__和__new__,如果不重写,就会调用元类type的(__call__如果重写了,一定要调用父类的__call__方法。__new__重写了,就必须要有返回值并且必须是对应的类对象)
三、单例设计模式
什么叫设计模式,就是解决某种固定问题的套路,例如:mvc,mtv等
单例:指的是一个类产生一个对象
为什么要使用单例:单例是为了节省资源,当一个类的所有对象属性全部相同时,则没必要创建多个对象。
# 单例n元类 class Single(type): def __call__(self, *args, **kwargs): if hasattr(self,"obj"): #判断是否存在已经有的对象 return getattr(self,"obj") # 有就返回 obj = super().__call__(*args,**kwargs) # 没有则创建 print("new 了") self.obj = obj # 并存入类中 return obj class Student(metaclass=Single): def __init__(self,name): self.name = name class Person(metaclass=Single): pass # 只会创建一个对象 Person() Person()