面向对象高级——反射和元类

参考链接: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()

 

posted @ 2019-07-30 17:19  只会玩辅助  阅读(211)  评论(0编辑  收藏  举报