如何利用Mixin对已定义的类进行方法和属性扩展
###起因
类已经定义明确,但希望在创建实例时拓展实例的功能,这种情况下怎么办?
###目的
实现一种机制,能够根据一定的规则,在实例创建时动态的拓展实例的功能(方法和属性);
###实现
-
- 动态的拓展类的方法和属性,这样所有实例都具备相应功能;
-
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()