元类以及反射

元类定义

'''
什么是元类: 就是产生类的类 简称为元类  metaclass
用途:对类的产生设定一些规定,
作用: 创建新的元类继承type,通过覆盖"__init__"完成对类的限制. 如例子对类名和方法的限制
什么时候用 :  需要对类进行一些限制的时候
'''

type的两种用法  

type 的两种意思:  一种是类型的  比如 print(isinstance('a', str))  二是类的元类
type(对象) 就是对象的类型
type(类名,父类们,名称空间)  产生的 就是一个新的类  名称空间实际上就是一个字典  用来存贮数据的
3:类的组成部分  1)类名  2)父类们  3)名称空间

 

type接收三个参数分别是:
classname: 要创建的class 的名称
object:要创建类的父类所组成的元组
sttr_dict: 要创建类的属性
type返回一个class,我们接收并赋值到一个变量上,现在这个变量就指向我们所创建的类,我们可以通过这个变量来使用类

  

自定义元类

class UpperAttrMetaClass(type):
    def __new__(cls,class_name,class_parents,class_attr, *args, **kwargs):
        print("__new__")
        class_attr['name'] = "jiao"
        return type.__new__(cls,class_name,class_parents,class_attr)
    def __init__(self,*args,**kwargs):
        print("__init__")
        super().__init__(*args, **kwargs)
        self.__cache = {}
    def __call__(self, *args, **kwargs):
        print("__call__")
        if args in self.__cache:
            return self.__cache[args]
        else:
            obj = super().__call__(*args)
            self.__cache[args] = obj
            return obj

class A(metaclass=UpperAttrMetaClass):
    def __init__(self,name):
        self.name = name
        print("a.__init__")


元类中__init__ __new__ __call__ 函数

1)__init__在元类中的应用

 在实例化对象时会自动调用__init__方法,会将对象本身作为第一个参数传递过去
 因为类也是对象  在实例化类对象时也会自动调用__init__方法,会见类对象作为第一个参数传递进去,
 也会传递三个参数  分别是 类名称  父类们  名称空间
__init__在元类中的作用: 创建新的元类继承type,通过覆盖"__init__"完成对类的限制.

  

class A:
    def __init__(self,name):
        self.name= name
a= A()#  实例化对象

class MyMetaclass(type):
    def __init__(self,class_name,bases,name_dict):  # 
        '''
        自定义元类
        :param class_name: 类名称
        :param bases: 父类们
        :param name_dict: 名称空间
        '''
        pass
class B(metaclass=MyMetaclass):  # 此时运行到class 就是实例化类对象B了
    pass
# 案例   限制类名必须首字母大写   控制类中方法名必须全部小写
# 分析:那么可以元类中限制类的创建条件
# 因为类名和属性和方法都是存储到类中名称空间的 类名下对应下是属性和方法 那么可以通过操作名称空间进行限制

class MyMetaclass(type):
    def __init__(self,class_name,bases,name_dict):
        # 元类的self代表的就是类对象
        super().__init__(class_name,bases,name_dict)
        # 对父类进行初始化

        # 对类名首字母进行限制 必须大写字母
        if not class_name.istitle():
            print('类名必须大写')
        # else:  这一行可不需要
            raise Exception # 否则就报异常

        # 取出类中的属性和方法
        for k in name_dict:
            if str(type(name_dict[k])) == "<class 'function'>": #  "<class 'function'>" 就是类的方法
                if not k.islower():
                    print('方法首字母必须小写')
                    raise Exception




class student(object,metaclass=MyMetaclass):  # 类名小写报错    提示 类名必须大写 根据上面元类的限制类名的方法
    NAME = 10
    def say_hai(self):
        print("============")

# class Student(metaclass=MyMetaclass):  #  方法名大写报错  提示 方法名必须小写 
#     name = 10
#     def Say(self):
#         print('111111111111111111111')


元类中的__new__方法 

__new__创建类对像的时候执行   先于__init__运行
作用:创建类对象
注意:如果覆盖了`__new__` 一定也要调用type中的`__new__`并返回执行结果
__new__与__init__的区别 :
1)先于__init__执行
2)__new__是创建类对象的(指的是元类的对象) __init__是实例化类对象的

  

class MyMetaclass(type):
    def __init__(self,class_name,bases,name_dict):
        pass

    def __new__(cls, *args, **kwargs):
        # pass
        # cls表示类自己  即MyMetaclass
        return type.__new__(cls,*args,**kwargs)


class People(metaclass=MyMetaclass):
    pass
print(People)
# __new__作用是创建类的  必须要返回值 没有无法创建对象的 当把return type.__new__(cls,*args,**kwargs)注释掉
# print(People) 的结果是None
# 案例  要求每个类中必须含有__doc__属性  就是必须要有注释的意思

class MyMetaclass(type):
    def __init__(self,class_name,bases,name_dict):
        super().__init__(class_name,bases,name_dict)
        # if not self.__doc__:
        #     raise Exception  # 两种方法都行
        # 或者
        if not('__doc__' in name_dict and name_dict['__doc__']):  #  __doc__在名称空间还里
            raise Exception

# class A(metaclass=MyMetaclass):
#     pass  # 无注释 报错

class B(metaclass=MyMetaclass):  #有注释不报错
    '''
    
    '''

元类中的__call__

执行时机:在元类中,调用类时执行
作用:控制对象的创建过程
案例  用__call__来实现单例模式  单例模式就是仅有一个实例的意思

  

class SingletonMetaClass(type):
    #创建类时会执init 在这为每个类设置一个obj属性 默认为None
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        self.obj = None

    # 当类要创建对象时会执行 该方法
    def __call__(self, *args, **kwargs):
         # 判断这个类 如果已经有实例了就 直接返回 从而实现单例
        if self.obj:
            return self.obj

        # 没有则创建新的实例并保存到类中
        obj = type.__call__(self,*args,**kwargs)
        self.obj = obj
        return obj  #  这是比较固定模式 记住以上三部分

class People(metaclass=SingletonMetaClass):
    def __init__(self,name,age,gender):
        self.name=name
        self.age =age
        self.gender =gender
    def  say(self):
        print('my name is %s my aunt is 龙妈'% self.name)

class Stu(metaclass=SingletonMetaClass):
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def say(self):
        print('my name is %s my aunt is 龙妈' % self.name)



p1 = People('jeck',18,'man')
print(p1)  #  地址都是一样的

p2 = People('tom',22,'man')
print(p2)  # 地址都是一样的
# 案例  单例模式制作一个QQ播放器案例

class MyMetaclass(type):
    def __init__(self,class_name,bases,name_dict):
        super().__init__(class_name,bases,name_dict)
        self.obj = None

    def __call__(self, *args, **kwargs):
        if self.obj:
            return self.obj

        obj = type.__call__(self,*args,**kwargs)
        self.obj = obj
        return obj

class QQplayer(metaclass=MyMetaclass):
    def __init__(self,voice_value,repeat=None):  #  repeat  重复的意思
        self.voice_value =voice_value
        self.repeat = repeat

    def play(self,file_path):  # 播放功能
        if hasattr(self,'file_path'):  #
            self.stop()
        print('正在播放%s' % file_path)
        self.file_path = file_path

    def stop(self):  # 停止播放
        print('停止播放%s' % self.file_path)



q1 = QQplayer(100,True)
q1.play('多远多要在一起')

q2 = QQplayer(80,True)
q2.play('泡沫')
# 正在播放多远多要在一起
# 停止播放多远多要在一起
# 正在播放泡沫


# 1. hasattr(object, 'name')
#
#   判断object对象中是否存在name属性,当然对于python的对象而言,属性包含变量和方法;有则返回True,没有则返回False;
# 需要注意的是name参数是string类型,所以不管是要判断变量还是方法,其名称都以字符串形式传参;getattr和setattr和delattr也同样;

 

元类实现单例模式

什么是单例:

某个类如果只有一个实例对象,那么该类成为单例类

单例的好处:

当某个类的所有对象特征和行为完全一样时,避免重复创建对象,浪费资源

案例:

class SingletonMetaClass(type):
    #创建类时会执init 在这为每个类设置一个obj属性 默认为None
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        self.obj = None
    
    # 当类要创建对象时会执行 该方法
    def __call__(self, *args, **kwargs):
         # 判断这个类 如果已经有实例了就直接返回 从而实现单例
        if self.obj:
            return self.obj

        # 没有则创建新的实例并保存到类中
        obj = type.__call__(self,*args,**kwargs)
        self.obj = obj
        return obj

  

反射

反射就是通过字符串来操作对象属性

英文中叫反省 (自省)

面向对象中的反省 指的是,一个对象必须具备,发现自身属性,以及修改自身属性的能力;

一个对象在设计初期,可能考虑不够周全后期需要删除或修改已经存在的属性, 和增加属性

涉及到的方法:

hasattr 判断是否存在某个属性

getattr	获取某个属性的值

setattr	新增或修改某个属性 

delattr 删除某个属性

案例:  

class MY_CMD:

    def dir(self):
        os.system("dir")

    def ipconfig(self):
        os.system("ipconfig")

cmd = MY_CMD()

while True:
    name = input("请输入要执行的功能:")
    if hasattr(cmd,name):
        method = getattr(cmd,name)
        print(method)
        method()
    else:
        print("sorry this method is not exists....!")

  

 

原文地址:https://www.cnblogs.com/wakee/p/10921960.html