zoukankan      html  css  js  c++  java
  • 面向对象之元类介绍

    1.元类的介绍

    首先来个知识储备:

    # 补充知识点exec
    # 全局作用域global()
    # 局部作用域locals()
    # exec(object,globals(),locals()) 三个参数 需要运行的字符串,全局作用域,局部作用域
    g = {
    "x": 1,
    "y": 2
    }
    
    l = {}  # 放局部作用域字典
    
    exec("""
    global x , m   #声明x,m为全局 作用域 
    x=10  #修改
    m=1
    z=100  # 函数内的局部作用域
    """, g, l)
    
    print(g) #{x:10,y:2,m:1....}
    print(l) #{z:100}
    

    1.类也是对象

    python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例),因而我们可以将类当作一个对象去使用,同样满足第一类对象的概念,可以:

    把类赋值给一个变量

    把类作为函数参数进行传递

    把类作为函数的返回值

    在运行时动态地创建类

    # 一切皆对象,对象可以怎么用?
    # 1、都可以被引用,x=obj
    # 2、都可以当作函数的参数传入
    # 3、都可以当作函数的返回值
    # 4、都可以当作容器类的元素,l=[func,time,obj,1]
    

    比如:

    class Foo:
          pass	
    f1=Foo() #f1是通过Foo类实例化的对象
    
    #type函数可以查看类型,也可以用来查看对象的类,二者是一样的
    print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
    print(type(Foo)) # 输出:<type 'type'>
    

    2.什么是元类

    元类是类的类,是类的模板

    元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为

    元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)

    type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

    3.创建类的两种方式

    方式一:使用class关键字

    class Chinese(object):
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def talk(self):
            print('%s is talking' %self.name)
    

    方式二:就是手动模拟class创建类的过程):将创建类的步骤拆分开,手动去创建

    # 我们用type来创建一个对象
    # 定义类有三个要素1.类得名字,2,类得基类,3,类得名称空间
    
    class_name = "Chinese"   # 类得名字
    class_base = (object,)   #类得继承的类
    class_body = '''     
    country='China'
    
    def __init__(self,namem,age):
        self.name=namem
        self.age=age
    
    def talk(self):
        print('%s is talking' %self.name)
    '''
    
    class_dic = {}  # 这个用法是用exec 把 class_body 转换成局部作用域,放在class_dic中
    
    exec(class_body, globals(), class_dic)
    
    print(class_dic)  # 打印得到一个局部作用域
    
    Chinese1 = type(class_name, class_base, class_dic)  # 实际就是类创建过程
    print(Chinese1)  # 这样就会得到一个<class '__main__.Chinese'>的类
    

    五 自定义元类控制类的行为 !!!

    一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的行为,工作流程是什么)

    我们定义元类如下,来控制类的创建(必须有注释,且类名称首字母必须大写)

    class Mymate(type):
        def __new__(cls, name, base, attr):
            if "__doc__" not in attr or not attr.get("__doc__").strip():  
                # 判断__doc__在不在字典里,还有在字典里值不能为空 否则抛出异常
                raise TypeError('必须为类指定文档注释')
            if not name.istitle():
                raise TypeError('首字母大写')  # 判断首字母大写
            return type.__new__(cls, name, base, attr)
    
    #***********************************************
    class MyMeta(type):
        def __init__(self, class_name, class_base, class_body):
    
            # 这个init方法是创建对象的时候触发  class Chinese() 时候就会触发
    
            if not class_name.istitle():
                raise TypeError('类名的首字母必须大写')
    
            if '__doc__' not in class_body or not class_body['__doc__'].strip():
                raise TypeError('必须有注释,且注释不能为空')
    
            super().__init__(class_name,class_base,class_body)
    #*************************************************
    上面的两种方法那种更合适???
    class People(object, metaclass=Mymate):
        '''dasda'''
        country = "china"
        skip = "yellow"
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print("%s is talking" % self.name)
    

    控制类实例化的行为,那么需要先储备知识__call__方法的使用

    	class MyMeta(type):
    	    def __init__(self, class_name, class_base, class_body):
    	
    	        # 这个init方法是创建对象的时候触发  class Chinese() 时候就会触发
    
            if not class_name.istitle():
                raise TypeError('类名的首字母必须大写')
    
            if '__doc__' not in class_body or not class_body['__doc__'].strip():
                raise TypeError('必须有注释,且注释不能为空')
    
            super().__init__(class_name, class_base, class_body)
    
    def __call__(self, *args, **kwargs):
    
        # 进入call方法做了几件事
        # 1.生成一个实例对象
        # 2.给新造的对象初始化
        # 3.返回这个对象
        print(self)
        obj = object.__new__(self)  # 造一个新的对象
        print(obj)
        self.__init__(obj, *args, **kwargs)
        # 实例化的过程中产生的新对象调用新对象的init方法
        # 这个地方其实就把实例的__dict__中添加我们设置好的属性
        print("__call__", args)
        print("__call__", kwargs)
        print(obj)
        return obj
    
    
    class Chinese(object, metaclass=MyMeta):
        """..."""
        country = "china"
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print("%s is talking !" % self.name)
    
    	
    	obj = Chinese('alex', age=12) #实例化的过程调用__call__方法
    	obj.talk() 
    	print(obj.__dict__)
    

    在元类中控制自定义的类无需init方法

    1.元类帮其完成创建对象,以及初始化操作;
      

    2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
      

    3.key作为用户自定义类产生对象的属性,且所有属性变成大写

    class Mymetaclass(type):
        # def __new__(cls,name,bases,attrs):
        #     update_attrs={}
        #     for k,v in attrs.items():
        #         if not callable(v) and not k.startswith('__'):
        #             update_attrs[k.upper()]=v
        #         else:
        #             update_attrs[k]=v
        #     return type.__new__(cls,name,bases,update_attrs)
    
        def __call__(self, *args, **kwargs):
            if args:
                raise TypeError('must use keyword argument for key function')
            obj = object.__new__(self) #创建对象,self为类Foo
    
            for k,v in kwargs.items():
                obj.__dict__[k.upper()]=v #这一步是直接把参数加到__dict__里面了 不用__init__方法,实际上 也是__call__调用init方法的,现在他自己实行
            return obj
    
    class Chinese(metaclass=Mymetaclass):
        country='China'
        tag='Legend of the Dragon' #龙的传人
        def walk(self):
            print('%s is walking' %self.name)
    
    
    p=Chinese(name='egon',age=18,sex='male')
    print(p.__dict__)
    

    自定义元类控制类得实例化实现单例模式

    单例模式后面的设计模式会讲到,这里不仔细讲解
    主要是为了节约内存空间,避免多个重复实例的创建
    先看下面的例子

    class Mysql():
    __instance = None # 设置类得属性,刚开始为none
    
    def __init__(self):
        self.addr = "127.0.0.0"
        self.port = 1208
    
    @classmethod
    def singleton(cls):
        if not cls.__instance: #判断实例是否已经创建了,没创建则创建
            obj = cls()
            # 这一步实际做的是实例化一个对象 用了类方法、把类传进去 实例化一个类得对象obj,赋值给__instance 这个类变量
            # 赋值给cls.__instance 这个变量
            cls.__instance = obj  # 创建之赋值给__instance
        return cls.__instance  #返回生成的实例
    
    
    # 一下的做法其实是生成两个mysql 实例 可是他们都是一样的 但是占用内存 所以引入单例模式 一样的就不创建了,用以前的就好
    # m1=Mysql()
    # m2=Mysql()
    #
    # print(id(m1))
    # print(id(m2))
    
    #使用类方法的单例模式创建数据引擎,避免内存浪费
    m1 = Mysql.singleton()
    m2 = Mysql.singleton()
    print(id(m1))
    print(id(m2))
  • 相关阅读:
    四,redis6版本的使用部署
    记录篇-浪潮服务器raid卡
    sudo漏洞解决方案--源码转rpm包(spec文件编写)
    关闭 Chrome 浏览器阅读清单功能
    【转译】如何成为一个数据工程师?
    Python 用最小堆获取大量元素 topk 大个元素
    Python 实现二分查找
    Python 排序算法之堆排序,使用 heapq 实现
    Python 排序算法之归并排序
    Python 排序算法之快速排序
  • 原文地址:https://www.cnblogs.com/zjcode/p/8728423.html
Copyright © 2011-2022 走看看