介绍元类之前,先来复习下类。
Python的一切皆为对象。类也不除外,也是对象。既然是对象就能将它赋值给变量,为它增加属性,将它作为函数参数进行传递等。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Hello:
def __init__(self,name):
self.name = name
def say(self):
print("hello",self.name)
print(Hello) # 打印Hello对象
Hello.str = "Hello" # 增加属性str
#定义echo方法
def echo(o):
print(o)
echo(Hello) # 作为函数参数进行传递
h = Hello("test") # 创建类Hello的实例对象h
|
类是对象,还能生成对象(类的实例)。由于类也是对象,所以它们必须是通过什么东西来生成的才对。当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法,那就是元类。元类是类工厂,专门负责生成类,生成的类再来创建实例。元类的主要目的就是为了当创建类时能够自动地改变类。有两种生成元类的方式。方式一,使用内置的元类函数type,方式二是使用metaclass,自定义元类。
type函数创建类对象
type(name, bases, dict)
使用三个参数,name是类名,成为name属性;bases元组逐个列出基类,成为bases属性;dict是包含类体定义的命名空间,成为dict属性。
例如,创建相同的对象Dog
1
2
|
class Dog(object):
a = 1
|
与
Dog = type('Dog', (object,), dict(a=1))
创建相同的类型对象。
例子1,使用type创建带有属性的类
type接受一个字典来为类定义属性,如下:
1
2
3
|
Dog = type('Dog',(),{'name':'二哈','age':1})
print(Dog)
help(Dog)
|
执行以上程序会输出如下结果:
1
2
3
4
5
6
7
8
9
10
11
12
|
<class '__main__.Dog'>
Help on class Dog in module __main__:
class Dog(builtins.object)
| Data descriptors defined here:
| __dict__
| dictionary for instance variables (if defined)
| __weakref__
| list of weak references to the object (if defined)
| -------------------
| Data and other attributes defined here:
| age = 1
| name = '二哈'
|
相当于
1
2
3
|
class Dog:
name = '二哈'
age = 1
|
例子2,使用type实现类的继承
1
2
3
4
5
|
Dog = type('Dog',(),{'name':'二哈','age':1})
print(Dog)
DogChild = type('DogChild', (Dog,),{})
print(DogChild)
print(DogChild.name)
|
执行以上程序会输出如下结果:
1
2
3
|
<class '__main__.Dog'>
<class '__main__.DogChild'>
二哈
|
相当于
1
2
3
4
5
6
|
class Dog:
name = '二哈'
age = 1
class DogChild(Dog):
pass
|
例子3,使用type创建带有方法的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#普通方法
def test(self):
print("test")
#静态方法
@staticmethod
def static_test():
print("static_test")
#类方法
@classmethod
def class_test(cls):
print("class_test")
Dog = type('Dog',(),{'test':test,'static_test':static_test,'class_test':class_test})
print(Dog)
test = Dog()
test.test()
test.static_test()
test.class_test()
|
执行以上程序会输出如下结果:
1
2
3
4
|
<class '__main__.Dog'>
test
static_test
class_test
|
相当于
1
2
3
4
5
6
7
8
9
10
11
|
class Dog:
def test(self):
print("test")
@staticmethod
def static_test():
print("static_test")
@classmethod
def class_test(cls):
print("class_test")
|
例子4,使用type创建方法,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#定义func函数
def func(self):
print("WanWan")
# 创建类名是Dog,继承自object类,方法名是say的类。
Dog = type('Dog', (object,), {'say':func,'name':'二哈','age':1})
# 类实例h
d = Dog()
# 调用类实例方法
d.say()
# 打印类实例属性
print(d.name)
# 打印类和实例的类型
print(type(Dog))
print(type(d))
|
执行以上程序会输出如下结果:
1
2
3
4
|
WanWan
二哈
<class 'type'>
<class '__main__.Dog'>
|
相当于
1
2
3
4
5
6
|
class Dog:
name = '二哈'
age = 1
def say(self)
print("WanWan")
|
自定义元类
除了使用type()动态创建类以外,可以通过在定义类的时候声明metaclass参数来创建元类。先定义继承type类的metaclass,然后指定类的metaclass来创建类。类看成是metaclass创建出来的“实例”。
metaclass是类的模板,所以必须从type类型派生:
实例1:增加一个author类属性
1
2
3
4
5
6
7
8
9
10
11
|
class Dog(type):
def __new__(cls, name, bases, attrs):
attrs['__weight__'] = '10kg'
return type.__new__(cls, name, bases, attrs)
class MyDog(metaclass=Dog):
pass
print(MyDog.__weight__)
a = MyDog()
print(a.__weight__)
|
执行以上程序会输出如下结果:
传入关键字参数metaclass时,创建MyDog时,要通过Dog.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
__new__(cls, name, bases, attrs)方法接收到的参数依次是:
cls,当前准备创建的类的对象
name,类的名字
bases,类继承的父类集合
attrs,类的方法集合
假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是使用metaclass元类。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。
幸运的是,__metaclass__实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调用的特殊方法
# __new__是用来创建对象并返回之的方法
# 而__init__只是用来将传入的参数初始化给对象
# 你很少用到__new__,除非你希望能够控制对象的创建
# 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
# 如果你希望的话,你也可以在__init__中做些事情
# 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
def __new__(cls, future_class_name, future_class_parents, future_class_attr):
#遍历属性字典,把不是__开头的属性名字变为大写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
# 方法1:通过'type'来做类对象的创建
# return type(future_class_name, future_class_parents, newAttr)
# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, future_class_name, future_class_parents, newAttr)
# 方法3:使用super方法
return super().__new__(cls, future_class_name, future_class_parents, newAttr)
class Foo(object, metaclass = UpperAttrMetaClass):
bar = 'bip'
print(hasattr(Foo, 'bar')) # 输出: False
print(hasattr(Foo, 'BAR')) # 输出:True
f = Foo()
print(f.BAR) # 输出:'bip'
|
设计类的继承关系时,通常,主线都是单一继承下来的。但是,如果需要“混入”额外的功能,通过多重继承就可以实现。这种设计通常称之为MixIn。与传统的类继承有所不同。通常mixin并不作为任何类的基类,也不关心与什么类一起使用,而是在运行时动态的同其他零散的类一起组合使用。Mixin类似于java中的interface,interface并不包含实现,而Mixin包含实现。
转载请注明本网址。