Last Updated on

在理解元类之前,您需要掌握Python的类。Python从Smalltalk语言中借用了一个非常独特的类概念。

在大多数语言中,类只是描述如何产生对象的代码段。在Python中也是如此:

class ObjectCreator(object):
    pass

my_object = ObjectCreator()
print(my_object)

# 输出结果:
<__main__.ObjectCreator object at 0x10994a748>

但是在Python中,万物皆为对象,所以,类也是一个对象。

是的,对象。

一旦使用关键字class,Python就会执行它并创建一个对象。指令

class ObjectCreator(object):
    pass

在内存中创建一个名称为“ ObjectCreator”的对象。

这个对象(类)本身具有创建对象(实例)的能力,这就是为什么它是一个类

但是,它仍然是一个对象,因此:

  • 您可以将其分配给变量
  • 你可以复制它
  • 您可以为其添加属性
  • 您可以将其作为函数参数传递

例如:

class ObjectCreator(object):
    pass

# 你能打印一个类,因为它是一个对象
print(ObjectCreator)
# 输出:
<class '__main__.ObjectCreator'>


# def echo(o):
    print(o)
# 你能将其作为函数参数
echo(ObjectCreator)
# 输出:
<class '__main__.ObjectCreator'>


# 你能为其添加属性
print(hasattr(ObjectCreator, 'new_attribute'))
ObjectCreator.new_attribute = 'foo'
print(hasattr(ObjectCreator, 'new_attribute'))
print(ObjectCreator.new_attribute)
# 输出:
False
True
foo


# 你能将其赋值给一个变量
ObjectCreatorMirror = ObjectCreator
print(ObjectCreatorMirror.new_attribute)
print(ObjectCreatorMirror())
# 输出
foo
<__main__.ObjectCreator object at 0x10fcd3828>

动态创建类

由于类是对象,因此您可以像创建任何对象一样即时创建它们。

首先,您可以使用class以下方法在函数中创建一个类:

def choose_class(name):
    if name == 'foo':
        class Foo(object):
            pass
        return Foo # return the class, not an instance
    else:
        class Bar(object):
            pass
        return Bar

MyClass = choose_class('foo')
# 函数返回一个类,而不是一个实例
print(MyClass) 
<class '__main__.Foo'>

# 你可以通过此类,创建一个实例
print(MyClass()) 
<__main__.Foo object at 0x89c6d4c>

但这并不是那么动态,因为您仍然必须自己编写整个类。

由于类是对象,因此它们必须由某种东西生成。

使用class关键字时,Python会自动创建此对象。但是,与Python中的大多数事情一样,它为您提供了一种手动进行操作的方法。

还记得功能type吗?此函数可以让您知道对象的类型:

print(type(1))
<type 'int'>

print(type("1"))
<type 'str'>

print(type(ObjectCreator))
<type 'type'>

print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

嗯,type具有完全不同的功能,它也可以动态创建类。type可以将类的描述作为参数,并返回一个类。

type 这样工作:

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

例如:

class MyShinyClass(object):
    pass

可以通过以下方式手动创建:

# 返回一个类
MyShinyClass = type('MyShinyClass', (), {}) 
print(MyShinyClass)
<class '__main__.MyShinyClass'>

# 使用此类,创建实例
print(MyShinyClass())
<__main__.MyShinyClass object at 0x8997cec>

您会注意到,我们使用“ MyShinyClass”作为类的名称和变量来保存类引用。它们可以不同,但​​是没有理由使事情复杂化。

type接受字典来定义类的属性。所以:

class Foo(object):
    bar = True

可以翻译为:

Foo = type('Foo', (), {'bar':True})

并用作普通类一样使用,当然,同样的还可以继承类。

最终,您需要向类中添加方法。只需定义具有适当签名的函数并将其分配为属性即可。

def echo_bar(self):
    print(self.bar)

# FooClid类,继承自Foo类,并添加方法。
FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})

print(hasattr(Foo, 'echo_bar'))
# 输出:False

print(hasattr(FooChild, 'echo_bar'))
# 输出:True

my_foo = FooChild()
my_foo.echo_bar()
# 输出:True

在动态创建类之后,您可以添加更多方法,就像将方法添加到正常创建的类对象中一样。

def echo_bar_more(self):
    print('yet another method')

FooChild.echo_bar_more = echo_bar_more
print(hasattr(FooChild, 'echo_bar_more'))
# 输出:True

您会看到我们要去的方向:在Python中,类是对象,您可以动态地动态创建类。

这是Python在使用关键字class时所做的,并且通过使用元类来完成。

什么是元类(最终)

元类是创建类的“东西”。

您定义类是为了创建对象,对吗?

但是我们了解到Python类是对象。

好吧,元类是创建这些对象的东西。它们是class的class,您可以通过以下方式描绘它们:

MyClass = MetaClass()
my_object = MyClass()

您已经看到,type您可以执行以下操作:

MyClass = type('MyClass', (), {})

这是因为该函数type实际上是一个元类。type是Python用于在幕后创建所有类的元类。

至于为什么用小写而不是小写Type?好吧,我想这与str创建字符串对象int的类和创建整数对象的类的一致性有关。type只是创建类对象的类。

您可以通过检查__class__属性来看到。

一切,我的意思是,一切都是Python中的对象。其中包括整数,字符串,函数和类。它们都是对象。所有这些都是从一个类创建的:

age = 35
print(age.__class__)
<type 'int'>

name = 'bob'
print(name.__class__)
<type 'str'>

def foo(): pass
print(foo.__class__)
<type 'function'>

class Bar(object): pass
print(Bar.__class__)
<class 'type'>

自定义元类

上面我们了解了元类是什么后,知道Python中所有的类class都是通过元类type创建的,那么我们就可以通过继承type的方式,自定义自己的元类,然后通过自己的元类来创建类,就可以完成一下类的预定义属性,预定义方法等的操作。

自定义元类,需要继承type,通过__new__魔法方法,定义使用此元类创建对象(类对象)时运行的

class MyMetaclass(type):
    def __new__(cls, name, bases, attrs):
        # do something 。。。
        return super().__new__(cls, name, bases, attrs)

其中name.base,attrs就是使用type创建类对象时需要的参数:

  • name: 名称
  • base:继承的父类
  • attrs:属性和方法

使用自定义元类,可以实现很多功能,具体在使用中灵活应用,查看一些优秀的开源代码,会发现其中有众多应用,可以学习其使用方式,灵活的在工作中使用。