Mixin研究之一

Stella981
• 阅读 633

如何利用Mixin对已定义的类进行方法和属性扩展

###起因
类已经定义明确,但希望在创建实例时拓展实例的功能,这种情况下怎么办?

###目的
实现一种机制,能够根据一定的规则,在实例创建时动态的拓展实例的功能(方法和属性);

###实现

    1. 动态的拓展类的方法和属性,这样所有实例都具备相应功能;
    • a. 利用python的动态特性:类也是对象,可以在运行时动态的创建它们,就像其他任何 对象一样;

    • b. 元类就是用来创建类的“东西”;

      • Python会在内存中通过_metaclass_ = Foo 创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路),Python没有找到_metaclass_,它会继续在父类中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找_metaclass_,并尝试做同样的操作。如果还是找不到_metaclass_,Python就会用内置的type来创建这个类对象。
    • c.代码实现

        class _MetaMixin(type):
        '''
            利用元编程扩展类属性和方法,示例:
            class DemoMixin1(object):
                __mixinname__ = 'DemoMixin'
                __metaclass__ = _MetaMixin
                def __init__(self):
                    pass
            这样即可将__mixinset__中收集到的__mixinname__相关的方法和属性扩展到自定义类中
            自定义的类中必须包含如下两个类属性:
                __mixinname__ = 'DemoMixin'
                __metaclass__ = _MetaMixin
        '''
        def __new__(cls, clsname, clsbases, clsdict):
            t = type.__new__(cls, clsname, clsbases, clsdict)
            mixinId = clsdict['__mixinname__']
            if mixinId in __mixinset__:
                clsdict['__mixins__'] = __mixinset__[mixinId]
                for k, v in clsdict['__mixins__'].items():
                    clsdict[k] = v
                t = type.__new__(cls, clsname, clsbases, clsdict)            
            return t
      
    • **d.**使用举例:

            class DemoMixin(object):
                __mixinname__ = 'DemoMixin'
                __metaclass__ = _MetaMixin
                def __init__(self):
                    pass
      
      
        + **只须定义\__mixinname__和\__metaclass__**
        + \__mixinname__:__从\__mixinset\__中获取与自己相关的方法和属性__  
        + \__metaclass__:__在元类中根据\__mixinname\__拓展类的方法和属性__
      
  • 2.动态的拓展实例的方法和属性;

    • a. 利用python的动态特性,在__init__对实例的__dict__进行动态扩展

    • b. 代码实现

        class Mixin(object):
            __mixinname__ = ''  #mixin interface name, all subclass need define its own __mixinname__
            def __init__(self):
                self.initmixin()
            def initmixin(self):
                '''
                    1. Add every item in __mixinset__[self.__mixinname__]
                    into instance of the class which inherit from Mixin
                    2. Marked by class attr __mixinname__, all subclass need define its own __mixinname__
                    为继承自Mixin的子类扩展__mixinset__[self.__mixinname__]中收集到的类和方法
                    __mixinset__为一字典,收集扩展方法和属性集合
                    __mixinname__为ID,标识旗下的方法和属性属于哪一个类
                    这种方法必须在子类中定义__mixinname__
                '''
                if self.__class__.__name__ == 'Mixin':  #don't dealing Mixin class itself
                    return
                if not self.__mixinname__:
                    return
                if self.__mixinname__ in __mixinset__:
                    self.__dict__.update(__mixinset__[self.__mixinname__])
                    setattr(self, '__mixins__', __mixinset__[self.__mixinname__])
            def initmixin2(self):
                '''
                    if you use this method
                    Marked by class attr __name__, all subclass don't need define its own __mixinname__
                    直接以__name__为ID,不需要定义__mixinname__,但必须保证__mixinset__与类一一对应
                '''
                name = self.__class__.__name__
                if name == 'Mixin':  #don't dealing Mixin class itself
                    return
                if name in __mixinset__:
                    self.__dict__.update(__mixinset__[name])
                    setattr(self, '__mixins__', __mixinset__[name])
      
    • **c.**使用说明

        class DemoMixin(Mixin):
            __mixinname__ = 'DemoMixin'
            def __init__(self):
                self.initmixin()
      
      • 必须定义_mixinname_, 然后在__init__执行self.initmixin()

###如何收集需要拓展的属性和方法

  • **a.**利用全局变量_mixinset__收集所有需要拓展类的方法和属性,_mixinset 以__mixinname__为key,以相应属性和方法为value的字典形式存在

  • **b.**代码实现

          __mixinset__ = {}  # 收集所有需要扩展的类方法和属性
          def setMixin(mixinname, name, value):
              '''
                  mixinname----ID
                  name -----属性或方法名字
                  value-----对应属性或方法的具体实现
                  如果value为字典,则update更新相应的字典
                  如果value为列表,则extend拓展相应的列表
                  如果value为元组,则sum增加相应的元组
              '''
              if mixinname in __mixinset__:
                  mixins = __mixinset__[mixinname]
              else:
                  __mixinset__[mixinname] = {}
                  mixins = __mixinset__[mixinname]
              if name in mixins:
                  if isinstance(mixins[name], (dict, tuple, list)) and isinstance(value, (dict, tuple, list)):
                      if isinstance(mixins[name], dict) and isinstance(value, dict):
                          mixins[name].update(value)
                      elif isinstance(mixins[name], list) and isinstance(value, list):
                          mixins[name].extend(value)
                      elif isinstance(mixins[name], tuple) and isinstance(value, tuple):
                          mixins[name] = mixins[name] + value
                  else:
                      mixins[name] = value
              else:
                  mixins[name] = value
    
  • c. 使用举例

    • 收集属性和方法

        def add(a, b):
            return a + b
        setMixin('DemoMixin', '1', 'aaaaaaaa')
        setMixin('DemoMixin', '2', (1,2,4))
        setMixin('DemoMixin', '2', (3,4))
        setMixin('DemoMixin', '6', [1, 2, 3])
        setMixin('DemoMixin', '6', [7, 8, 9])
        setMixin('DemoMixin', '3', {1:2})
        setMixin('DemoMixin', '3', {1:3})
        setMixin('DemoMixin', '5', 'asssssssssssssssssssssssssss')
        setMixin('DemoMixin', '5', (1,2,3,45))
        setMixin('DemoMixin', '5', 'assss')
        setMixin('DemoMixin', '5', [1,2])
        setMixin('DemoMixin', 'add', add)
        import os
        setMixin('DemoMixin', 'getcwd111', os.getcwd)
      

###测试Demo演示:

  • **a.**测试代码

      class DemoMixin1(object):
          __mixinname__ = 'DemoMixin'
          __metaclass__ = _MetaMixin
          def __init__(self):
              pass
      class DemoMixin2(Mixin):
          __mixinname__ = 'DemoMixin'
          def __init__(self):
              self.initmixin()
      def main():
          d1 = DemoMixin1()
          print dir(DemoMixin1)
          print DemoMixin1.__dict__
          print dir(d1)
          print d1.__dict__
          print '-' * 20
          d2 = DemoMixin2()
          print dir(DemoMixin2)
          print DemoMixin2.__dict__
          print dir(d2)
          print d2.__dict__
      if __name__ == '__main__':
          main()
    
    • 测试结果过大,不做显示,自己可以运行下面的完整demo程序查看结果
  • **b.**完整代码Demo

      #!/usr/bin/python
      # -*- coding: utf-8 -*-
      '''
          目的:当实例创建时,根据收集到的配置对相应的类进行动态方法和属性扩展
          方式有两种:
              第一种:直接扩展类属性和方法,这种方式使用元编程__new__实现较好;
                      直接拓展类clsdict,因为clsdict中存放的即为所有类属性和方法的集合
              第二种:直接扩展实例属性和方法,这种方式在初始化__init__中实现较好;
                      直接拓展实例__dict__,因为__dict__中存放的即为所有实例属性和方法的集合
      '''
      __mixinset__ = {}  # 收集所有需要扩展的类方法和属性
      class Object_Dict(dict):
          '''
              Makes a dictionary behave like an object.
          '''
          def __init__(self, *args, **kw):
              dict.__init__(self, *args, **kw)
              self.__dict__ = self
      class _MetaMixin(type):
          '''
              利用元编程扩展类属性和方法,示例:
              class DemoMixin1(object):
                  __mixinname__ = 'DemoMixin'
                  __metaclass__ = _MetaMixin
                  def __init__(self):
                      pass
              这样即可将__mixinset__中收集到的__mixinname__相关的方法和属性扩展到自定义类中
              自定义的类中必须包含如下两个类属性:
                  __mixinname__ = 'DemoMixin'
                  __metaclass__ = _MetaMixin
          '''
          def __new__(cls, clsname, clsbases, clsdict):
              t = type.__new__(cls, clsname, clsbases, clsdict)
              mixinId = clsdict['__mixinname__']
              if mixinId in __mixinset__:
                  clsdict['__mixins__'] = __mixinset__[mixinId]
                  for k, v in clsdict['__mixins__'].items():
                      clsdict[k] = v
                  t = type.__new__(cls, clsname, clsbases, clsdict)            
              return t
      class Mixin(object):
          __mixinname__ = ''  #mixin interface name, all subclass need define its own __mixinname__
          def __init__(self):
              self.initmixin()
          def initmixin(self):
              '''
                  1. Add every item in __mixinset__[self.__mixinname__]
                  into instance of the class which inherit from Mixin
                  2. Marked by class attr __mixinname__, all subclass need define its own __mixinname__
                  为继承自Mixin的子类扩展__mixinset__[self.__mixinname__]中收集到的类和方法
                  __mixinset__为一字典,收集扩展方法和属性集合
                  __mixinname__为ID,标识旗下的方法和属性属于哪一个类
                  这种方法必须在子类中定义__mixinname__
              '''
              if self.__class__.__name__ == 'Mixin':  #don't dealing Mixin class itself
                  return
              if not self.__mixinname__:
                  return
              if self.__mixinname__ in __mixinset__:
                  self.__dict__.update(__mixinset__[self.__mixinname__])
                  setattr(self, '__mixins__', __mixinset__[self.__mixinname__])
          def initmixin2(self):
              '''
                  if you use this method
                  Marked by class attr __name__, all subclass don't need define its own __mixinname__
                  直接以__name__为ID,不需要定义__mixinname__,但必须保证__mixinset__与类一一对应
              '''
              name = self.__class__.__name__
              if name == 'Mixin':  #don't dealing Mixin class itself
                  return
              if name in __mixinset__:
                  self.__dict__.update(__mixinset__[name])
                  setattr(self, '__mixins__', __mixinset__[name])
      def setMixin(mixinname, name, value):
          '''
              mixinname----ID
              name -----属性或方法名字
              value-----对应属性或方法的具体实现
              如果value为字典,则update更新相应的字典
              如果value为列表,则extend拓展相应的列表
              如果value为元组,则sum增加相应的元组
          '''
          if mixinname in __mixinset__:
              mixins = __mixinset__[mixinname]
          else:
              __mixinset__[mixinname] = {}
              mixins = __mixinset__[mixinname]
          if name in mixins:
              if isinstance(mixins[name], (dict, tuple, list)) and isinstance(value, (dict, tuple, list)):
                  if isinstance(mixins[name], dict) and isinstance(value, dict):
                      mixins[name].update(value)
                  elif isinstance(mixins[name], list) and isinstance(value, list):
                      mixins[name].extend(value)
                  elif isinstance(mixins[name], tuple) and isinstance(value, tuple):
                      mixins[name] = mixins[name] + value
              else:
                  mixins[name] = value
          else:
              mixins[name] = value
      def add(a, b):
          return a + b
      setMixin('DemoMixin', '1', 'aaaaaaaa')
      setMixin('DemoMixin', '2', (1,2,4))
      setMixin('DemoMixin', '2', (3,4))
      setMixin('DemoMixin', '6', [1, 2, 3])
      setMixin('DemoMixin', '6', [7, 8, 9])
      setMixin('DemoMixin', '3', {1:2})
      setMixin('DemoMixin', '3', {1:3})
      setMixin('DemoMixin', '5', 'asssssssssssssssssssssssssss')
      setMixin('DemoMixin', '5', (1,2,3,45))
      setMixin('DemoMixin', '5', 'assss')
      setMixin('DemoMixin', '5', [1,2])
      setMixin('DemoMixin', 'add', add)
      import os
      setMixin('DemoMixin', 'getcwd111', os.getcwd)
      class DemoMixin1(object):
          __mixinname__ = 'DemoMixin'
          __metaclass__ = _MetaMixin
          def __init__(self):
              pass
      class DemoMixin2(Mixin):
          __mixinname__ = 'DemoMixin'
          def __init__(self):
              self.initmixin()
      def main():
          d1 = DemoMixin1()
          print dir(DemoMixin1)
          print DemoMixin1.__dict__
          print dir(d1)
          print d1.__dict__
          print '-' * 20
          d2 = DemoMixin2()
          print dir(DemoMixin2)
          print DemoMixin2.__dict__
          print dir(d2)
          print d2.__dict__
      if __name__ == '__main__':
          main()
    
点赞
收藏
评论区
推荐文章
Irene181 Irene181
3年前
一篇文章带教会你Python访问限制那些事儿
一、前言在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。二、案例分析以Teacher类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性。classTeacher(object):definit(self,name,score):s
浪人 浪人
3年前
Java基础与提高干货系列——Java反射机制
前言今天介绍下Java的反射机制,以前我们获取一个类的实例都是使用new一个实例出来。那样太low了,今天跟我一起来学习学习一种更加高大上的方式来实现。正文Java反射机制定义Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以
御弟哥哥 御弟哥哥
3年前
Java基础与提高干货系列 -- Java反射机制
前言今天介绍下Java的反射机制,以前我们获取一个类的实例都是使用new一个实例出来。那样太low了,今天跟我一起来学习学习一种更加高大上的方式来实现。正文Java反射机制定义Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
Easter79 Easter79
3年前
Spring使用静态工厂方法创建Bean
1\.使用静态工厂方法创建Bean    使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类。因为Spring需要知道是用哪个工厂来创建Bean实例。另外,还需要使用factorymethod来指定静态工厂方法名,Spring将调用静态工厂方法(可能包含一组参数),
Stella981 Stella981
3年前
Category 特性在 iOS 组件化中的应用与管控
背景iOSCategory功能简介Category是ObjectiveC2.0之后添加的语言特性。Category就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。在ObjectiveC(iOS的开发语言,下文用OC代替)中的具体体现为:实例(类)方法、属性和协
Stella981 Stella981
3年前
Python类中的__new__和__init__的区别
在写Python类时,或者看某些项目源码时,总是见到__init__和__new__方法,一直没有深入研究两者的区别,今天聊聊这个。__new____new__是类(class)方法。class新创建实例时,会调用__new__,它主要控制一个新实例的创建。需要知道的是,__new__是实例创建的第
Stella981 Stella981
3年前
Javascript 构造函数和类
1.构造函数构造函数的名称一般都是首字母大写挂载在this上面的属性为实例属性,实例属性再每个实例间都是独立的原型链属性通过prototype添加,他是所有实例共享的类方法/静态属性只能由构造函数本身访问当实例属性和原型链上的属性重名时,优先访问实例属性,没有实例属性再访问原型属性大多数浏览器的ES5实现之中,每一个对象都有\_\_pr
Wesley13 Wesley13
3年前
Java反射机制及适用场景
什么是Java反射机制?JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为Java的反射机制。反射的适用场景是什么?1.当你做一个软件可以安装插件的功能,你连插件的类型名称都不知道,你怎么实例化这个对象呢
Stella981 Stella981
3年前
C#中Activator.CreateInstance()方法用法分析
本文实例讲述了C中Activator.CreateInstance()方法用法。Activator类包含特定的方法,用以在本地或从远程创建对象类型,或获取对现有远程对象的引用。C在类工厂中动态创建类的实例,所使用的方法为:1.Activator.CreateInstance(Type)2.Activator.
小万哥 小万哥
3个月前
Kotlin 面向对象编程 (OOP) 基础:类、对象与继承详解
面向对象编程(OOP)是一种编程范式,它通过创建包含数据和方法的对象来组织代码。相较于过程式编程,OOP提供了更快更清晰的结构,有助于遵守DRY(Don&39;tRepeatYourself)原则,使代码更易于维护和扩展。在Kotlin中,类和对象是OOP的核心。类作为对象的模板,定义了对象的行为和状态;对象则是类的具体实例。例如,Car类可以定义汽车的品牌、型号等属性,以及如驾驶和刹车等功能。通过构造函数可以快速初始化对象的属性。此外,Kotlin支持继承机制,子类可以从父类继承属性和方法,促进代码重用。