介绍元类之前,先来复习下类。 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__)

执行以上程序会输出如下结果:

1
2
10kg
10kg

传入关键字参数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包含实现。


转载请注明本网址。