Python-元类

先有鸡还是先有蛋,this is a question。

在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。Python 中也是如此。

那么是哪个东西创建的这个东西呢?女娲或者上帝?

尝试着站在更高的地方想这个问题(比如6楼别人的宿舍 :) )。

好吧,其实类同样也是一种对象,类对象。下面这段代码,我没有创建对象,也没有调用他,只要一运行,就会输出那句print。

1
2
3
4
5
6
class Person(object):
num = 0
print("-- person test print --")

def __init__(self):
self.name = 'abc'

运行结果

这就是 Python 在创建类对象。创建 Person 类的东西,称为元类。

动态的创建类

类也是对象,那么可以在运行时动态的创建他们吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def choose_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo # 返回的是类,不是类的实例
else:
class Bar(object):
pass
return Bar


if __name__ == '__main__':
mc = choose_class('foo')
print(mc) # 函数返回的是类,不是类的实例
print(mc()) # 可以通过这个类创建实例对象

运行结果

使用 type 创建类

type()不仅可以查看参数是个啥类型的,还可以动态的创建类!

1
2
# 语法
type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Person = type("Person", (), {})
p1 = Person()
print(type(p1))

def printName(self):
print(self.name)

Student = type("Student", (Person,), {"name": 'ahojcn', "age": 20, "printName": printName})
s1 = Student()
s1.printName()
print(s1.age)

print(Person.__class__)
print(Student.__class__)
print(p1.__class__)
print(s1.__class__)

print(type.__class__) # type

运行结果

元类

元类就是用来创建的东西。

函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢?好吧,我猜这是为了和str保持一致性,str是用来创建字符串对象的类,而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查class属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。

__metaclass__ 属性

这个属性决定了这个类长什么样子,不指定的话就是使用系统默认的。

下面这段测试代码,使用自定义的函数来创建 Foo 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def upper_attr(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

# 调用 type 创建一个类
return type(future_class_name, future_class_parents, newAttr)



class Foo(object, metaclass=upper_attr):
# __metaclass__ = upper_attr # py2 要写在这里
bar = 'bip'


if __name__ == '__main__':
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)

运行结果

下面这段代码使用自定义的class来当做元类(代码参考:网络):

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#coding=utf-8

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(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)

#python2的用法
class Foo(object):
__metaclass__ = UpperAttrMetaClass
bar = 'bip'

# python3的用法
# class Foo(object, metaclass = UpperAttrMetaClass):
# bar = 'bip'

print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True

f = Foo()
print(f.BAR)
# 输出:'bip'

__metaclass__ 属性的作用?

  1. 拦截类的创建
  2. 修改类
  3. 返回修改之后的类

文章作者: ahoj
文章链接: https://ahoj.cc/2019/06/Python-元类/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ahoj 的小本本