Mixin研究之一

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

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

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

###实现

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

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

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

      1 class _MetaMixin(type): 2 ''' 3 利用元编程扩展类属性和方法,示例: 4 class DemoMixin1(object): 5 __mixinname__ = 'DemoMixin' 6 __metaclass__ = _MetaMixin 7 def __init__(self): 8 pass 9 这样即可将__mixinset__中收集到的__mixinname__相关的方法和属性扩展到自定义类中 10 自定义的类中必须包含如下两个类属性: 11 __mixinname__ = 'DemoMixin' 12 __metaclass__ = _MetaMixin 13 ''' 14 def __new__(cls, clsname, clsbases, clsdict): 15 t = type.__new__(cls, clsname, clsbases, clsdict) 16 mixinId = clsdict['__mixinname__'] 17 if mixinId in __mixinset__: 18 clsdict['__mixins__'] = __mixinset__[mixinId] 19 for k, v in clsdict['__mixins__'].items(): 20 clsdict[k] = v 21 t = type.__new__(cls, clsname, clsbases, clsdict) 22 return t
    • **d.**使用举例:

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

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

    • b. 代码实现

      1 class Mixin(object): 2 __mixinname__ = '' #mixin interface name, all subclass need define its own __mixinname__ 3 def __init__(self): 4 self.initmixin() 5 def initmixin(self): 6 ''' 7 1. Add every item in __mixinset__[self.__mixinname__] 8 into instance of the class which inherit from Mixin 9 2. Marked by class attr __mixinname__, all subclass need define its own __mixinname__ 10 为继承自Mixin的子类扩展__mixinset__[self.__mixinname__]中收集到的类和方法 11 __mixinset__为一字典,收集扩展方法和属性集合 12 __mixinname__为ID,标识旗下的方法和属性属于哪一个类 13 这种方法必须在子类中定义__mixinname__ 14 ''' 15 if self.__class__.__name__ == 'Mixin': #don't dealing Mixin class itself 16 return 17 if not self.__mixinname__: 18 return 19 if self.__mixinname__ in __mixinset__: 20 self.__dict__.update(__mixinset__[self.__mixinname__]) 21 setattr(self, '__mixins__', __mixinset__[self.__mixinname__]) 22 def initmixin2(self): 23 ''' 24 if you use this method 25 Marked by class attr __name__, all subclass don't need define its own __mixinname__ 26 直接以__name__为ID,不需要定义__mixinname__,但必须保证__mixinset__与类一一对应 27 ''' 28 name = self.__class__.__name__ 29 if name == 'Mixin': #don't dealing Mixin class itself 30 return 31 if name in __mixinset__: 32 self.__dict__.update(__mixinset__[name]) 33 setattr(self, '__mixins__', __mixinset__[name])
    • **c.**使用说明

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

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

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

  • **b.**代码实现

    1 __mixinset__ = {} # 收集所有需要扩展的类方法和属性 2 def setMixin(mixinname, name, value): 3 ''' 4 mixinname----ID 5 name -----属性或方法名字 6 value-----对应属性或方法的具体实现 7 如果value为字典,则update更新相应的字典 8 如果value为列表,则extend拓展相应的列表 9 如果value为元组,则sum增加相应的元组 10 ''' 11 if mixinname in __mixinset__: 12 mixins = __mixinset__[mixinname] 13 else: 14 __mixinset__[mixinname] = {} 15 mixins = __mixinset__[mixinname] 16 if name in mixins: 17 if isinstance(mixins[name], (dict, tuple, list)) and isinstance(value, (dict, tuple, list)): 18 if isinstance(mixins[name], dict) and isinstance(value, dict): 19 mixins[name].update(value) 20 elif isinstance(mixins[name], list) and isinstance(value, list): 21 mixins[name].extend(value) 22 elif isinstance(mixins[name], tuple) and isinstance(value, tuple): 23 mixins[name] = mixins[name] + value 24 else: 25 mixins[name] = value 26 else: 27 mixins[name] = value
  • c. 使用举例

    • 收集属性和方法

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

###测试Demo演示:

  • **a.**测试代码

    1 class DemoMixin1(object): 2 __mixinname__ = 'DemoMixin' 3 __metaclass__ = _MetaMixin 4 def __init__(self): 5 pass 6 class DemoMixin2(Mixin): 7 __mixinname__ = 'DemoMixin' 8 def __init__(self): 9 self.initmixin() 10 def main(): 11 d1 = DemoMixin1() 12 print dir(DemoMixin1) 13 print DemoMixin1.__dict__ 14 print dir(d1) 15 print d1.__dict__ 16 print '-' * 20 17 d2 = DemoMixin2() 18 print dir(DemoMixin2) 19 print DemoMixin2.__dict__ 20 print dir(d2) 21 print d2.__dict__ 22 if __name__ == '__main__': 23 main()
    • 测试结果过大,不做显示,自己可以运行下面的完整demo程序查看结果
  • **b.**完整代码Demo

    1 #!/usr/bin/python 2 # -*- coding: utf-8 -*- 3 ''' 4 目的:当实例创建时,根据收集到的配置对相应的类进行动态方法和属性扩展 5 方式有两种: 6 第一种:直接扩展类属性和方法,这种方式使用元编程__new__实现较好; 7 直接拓展类clsdict,因为clsdict中存放的即为所有类属性和方法的集合 8 第二种:直接扩展实例属性和方法,这种方式在初始化__init__中实现较好; 9 直接拓展实例__dict__,因为__dict__中存放的即为所有实例属性和方法的集合 10 ''' 11 __mixinset__ = {} # 收集所有需要扩展的类方法和属性 12 class Object_Dict(dict): 13 ''' 14 Makes a dictionary behave like an object. 15 ''' 16 def __init__(self, *args, **kw): 17 dict.__init__(self, *args, **kw) 18 self.__dict__ = self 19 class _MetaMixin(type): 20 ''' 21 利用元编程扩展类属性和方法,示例: 22 class DemoMixin1(object): 23 __mixinname__ = 'DemoMixin' 24 __metaclass__ = _MetaMixin 25 def __init__(self): 26 pass 27 这样即可将__mixinset__中收集到的__mixinname__相关的方法和属性扩展到自定义类中 28 自定义的类中必须包含如下两个类属性: 29 __mixinname__ = 'DemoMixin' 30 __metaclass__ = _MetaMixin 31 ''' 32 def __new__(cls, clsname, clsbases, clsdict): 33 t = type.__new__(cls, clsname, clsbases, clsdict) 34 mixinId = clsdict['__mixinname__'] 35 if mixinId in __mixinset__: 36 clsdict['__mixins__'] = __mixinset__[mixinId] 37 for k, v in clsdict['__mixins__'].items(): 38 clsdict[k] = v 39 t = type.__new__(cls, clsname, clsbases, clsdict) 40 return t 41 class Mixin(object): 42 __mixinname__ = '' #mixin interface name, all subclass need define its own __mixinname__ 43 def __init__(self): 44 self.initmixin() 45 def initmixin(self): 46 ''' 47 1. Add every item in __mixinset__[self.__mixinname__] 48 into instance of the class which inherit from Mixin 49 2. Marked by class attr __mixinname__, all subclass need define its own __mixinname__ 50 为继承自Mixin的子类扩展__mixinset__[self.__mixinname__]中收集到的类和方法 51 __mixinset__为一字典,收集扩展方法和属性集合 52 __mixinname__为ID,标识旗下的方法和属性属于哪一个类 53 这种方法必须在子类中定义__mixinname__ 54 ''' 55 if self.__class__.__name__ == 'Mixin': #don't dealing Mixin class itself 56 return 57 if not self.__mixinname__: 58 return 59 if self.__mixinname__ in __mixinset__: 60 self.__dict__.update(__mixinset__[self.__mixinname__]) 61 setattr(self, '__mixins__', __mixinset__[self.__mixinname__]) 62 def initmixin2(self): 63 ''' 64 if you use this method 65 Marked by class attr __name__, all subclass don't need define its own __mixinname__ 66 直接以__name__为ID,不需要定义__mixinname__,但必须保证__mixinset__与类一一对应 67 ''' 68 name = self.__class__.__name__ 69 if name == 'Mixin': #don't dealing Mixin class itself 70 return 71 if name in __mixinset__: 72 self.__dict__.update(__mixinset__[name]) 73 setattr(self, '__mixins__', __mixinset__[name]) 74 def setMixin(mixinname, name, value): 75 ''' 76 mixinname----ID 77 name -----属性或方法名字 78 value-----对应属性或方法的具体实现 79 如果value为字典,则update更新相应的字典 80 如果value为列表,则extend拓展相应的列表 81 如果value为元组,则sum增加相应的元组 82 ''' 83 if mixinname in __mixinset__: 84 mixins = __mixinset__[mixinname] 85 else: 86 __mixinset__[mixinname] = {} 87 mixins = __mixinset__[mixinname] 88 if name in mixins: 89 if isinstance(mixins[name], (dict, tuple, list)) and isinstance(value, (dict, tuple, list)): 90 if isinstance(mixins[name], dict) and isinstance(value, dict): 91 mixins[name].update(value) 92 elif isinstance(mixins[name], list) and isinstance(value, list): 93 mixins[name].extend(value) 94 elif isinstance(mixins[name], tuple) and isinstance(value, tuple): 95 mixins[name] = mixins[name] + value 96 else: 97 mixins[name] = value 98 else: 99 mixins[name] = value 100 def add(a, b): 101 return a + b 102 setMixin('DemoMixin', '1', 'aaaaaaaa') 103 setMixin('DemoMixin', '2', (1,2,4)) 104 setMixin('DemoMixin', '2', (3,4)) 105 setMixin('DemoMixin', '6', [1, 2, 3]) 106 setMixin('DemoMixin', '6', [7, 8, 9]) 107 setMixin('DemoMixin', '3', {1:2}) 108 setMixin('DemoMixin', '3', {1:3}) 109 setMixin('DemoMixin', '5', 'asssssssssssssssssssssssssss') 110 setMixin('DemoMixin', '5', (1,2,3,45)) 111 setMixin('DemoMixin', '5', 'assss') 112 setMixin('DemoMixin', '5', [1,2]) 113 setMixin('DemoMixin', 'add', add) 114 import os 115 setMixin('DemoMixin', 'getcwd111', os.getcwd) 116 class DemoMixin1(object): 117 __mixinname__ = 'DemoMixin' 118 __metaclass__ = _MetaMixin 119 def __init__(self): 120 pass 121 class DemoMixin2(Mixin): 122 __mixinname__ = 'DemoMixin' 123 def __init__(self): 124 self.initmixin() 125 def main(): 126 d1 = DemoMixin1() 127 print dir(DemoMixin1) 128 print DemoMixin1.__dict__ 129 print dir(d1) 130 print d1.__dict__ 131 print '-' * 20 132 d2 = DemoMixin2() 133 print dir(DemoMixin2) 134 print DemoMixin2.__dict__ 135 print dir(d2) 136 print d2.__dict__ 137 if __name__ == '__main__': 138 main()
点赞
收藏

评论区

加载中...

相关推荐

一篇文章带教会你Python访问限制那些事儿

一、前言在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。二、案例分析以Teacher类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性。classTeacher(object):definit(self,name,score):s

Java基础与提高干货系列——Java反射机制

前言今天介绍下Java的反射机制,以前我们获取一个类的实例都是使用new一个实例出来。那样太low了,今天跟我一起来学习学习一种更加高大上的方式来实现。正文Java反射机制定义Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以

Java基础与提高干货系列 -- Java反射机制

前言今天介绍下Java的反射机制,以前我们获取一个类的实例都是使用new一个实例出来。那样太low了,今天跟我一起来学习学习一种更加高大上的方式来实现。正文Java反射机制定义Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性

Spring使用静态工厂方法创建Bean

1\.使用静态工厂方法创建Bean    使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类。因为Spring需要知道是用哪个工厂来创建Bean实例。另外,还需要使用factorymethod来指定静态工厂方法名,Spring将调用静态工厂方法(可能包含一组参数),

Category 特性在 iOS 组件化中的应用与管控

背景iOSCategory功能简介Category是ObjectiveC2.0之后添加的语言特性。Category就是对装饰模式的一种具体实现。它的主要作用是在不改变原有类的前提下,动态地给这个类添加一些方法。在ObjectiveC(iOS的开发语言,下文用OC代替)中的具体体现为:实例(类)方法、属性和协

Python类中的__new__和__init__的区别

在写Python类时,或者看某些项目源码时,总是见到__init__和__new__方法,一直没有深入研究两者的区别,今天聊聊这个。__new____new__是类(class)方法。class新创建实例时,会调用__new__,它主要控制一个新实例的创建。需要知道的是,__new__是实例创建的第