元类与 type

元类

类也是对象

在⼤多数编程语⾔中,类就是⼀组⽤来描述如何⽣成⼀个对象的代码段。在Python中这⼀点仍然成⽴!

Python中的类还远不⽌如此。类同样也是⼀种对象。是的,没错,就是对象。只要你使⽤关键字class,Python解释器在执⾏的时候就会创建⼀个对象!

In [1]: class Person:
   ...:     pass
   ...:
   ...:

In [2]: print(Person)
<class '__main__.Person'>

将在内存中创建⼀个对象,名字就是ObjectCreator。这个对象(类对象ObjectCreator)拥有创建对象(实例对象)的能⼒。但是,它的本质仍然是⼀个对象,于是乎你可以对它做如下的操作:

  • 你可以将它赋值给⼀个变量
  • 你可以拷⻉它
  • 你可以为它增加属性
  • 你可以将它作为函数参数进⾏传递
>>> print ObjectCreator # 你可以打印⼀个类,因为它其实也是⼀个对象
<class '__main__.ObjectCreator'>
>>> def echo(o):print o
…
>>> echo(ObjectCreator) # 你可以将类做为参数传给函数
<class '__main__.ObjectCreator'>
>>> print hasattr(ObjectCreator, 'new_attribute')
Fasle
>>> ObjectCreator.new_attribute = 'foo' # 你可以为类增加属性
>>> print hasattr(ObjectCreator, 'new_attribute')
True
>>> print ObjectCreator.new_attribute
foo
>>> ObjectCreatorMirror = ObjectCreator # 你可以将类赋值给⼀个变量
>>> print ObjectCreatorMirror()
<__main__.ObjectCreator object at 0x8997b4c>

动态地创建类

因为类也是对象,你可以在运⾏时动态的创建它们,就像其他任何对象⼀样。⾸先,你可以在函数中创建类,使⽤class关键字即可!

In [1]: def chose_class(name):
   ...:     if name == "foo":
   ...:         class Foo(object):
   ...:             pass
   ...:         return Foo # 返回的是类,而不是类的实例
   ...:     else:
   ...:         class Bar(object):
   ...:             pass
   ...:         return Bar
   ...:


In [2]: MyClass = chose_class("foo")

In [3]: print(MyClass)
<class '__main__.chose_class.<locals>.Foo'>

In [4]:

但这还不够动态,因为你仍然需要..编写整个类的代码。由于类也是对象,所以它们必须是通过什么东.来.成的才对。当你使.class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你自动处理的方法。

In [4]: print(type(100))
<class 'int'>

In [5]: print(type("Hello XPU"))
<class 'str'>

In [6]: print(type(MyClass))
<class 'type'>

类对象的类型居然是type!!!

使用type创建类

type还有一种完全不同的功能,动态的创建类。
type可以接受一个类的描述作为参数,然后返回一个类(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是件很傻的事情,但这在Python中是为了保持向后兼容性)

type创建类的格式

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

In [7]: class Person:
   ...:     nums = 0
   ...:

In [8]: MyPerson = type("MyPerson",(),{"num":0})

In [9]: p1 = Person()

In [11]: p1.nums
Out[11]: 0

In [12]: p2 = MyPerson()

In [13]: p2.num
Out[13]: 0

使用Type创建带属性的类

In [1]: Foo = type("Foo", (),{"bar":True})

In [2]: class Foo2(object):
   ...:     bar = True
   ...:

注意

  • type的第2个参数,元组中是⽗类的名字,⽽不是字符串
  • 添加的属性是类属性,并不是实例属性

使⽤type创建带有⽅法的类

In [1]: Foo = type("Foo", (),{"bar":True})

In [2]: class Foo2(object):
   ...:     bar = True
   ...:

In [3]: def my_fun(self):
   ...:     print(self.bar)
   ...:

In [4]: FooChild = type('FoodChild',(Foo,), {'echo_bar':my_fun})

In [5]: my_foo = FooChild()

In [6]: my_foo.echo_bar()
True

添加静态方法

同上,需要在方法上面加注@staticmethod即可

添加类方法

同上,需要在方法上面加注@classmethod即可

元类的概念

在Python中,类也是对象,你可以动态的创建类。这就是当你使⽤关键字class时Python在幕后做的事情,⽽这就是通过元类来实现的!

元类就是⽤来创建类的“东⻄”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。

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

__metaclass__属性

定义⼀个类的时候为其添加metaclass属性

class Foo(object):
    __metaclass__ = something...

如果你这么做了,Python就会⽤元类来创建类Foo。⼩⼼点,这⾥⾯有些技巧。你⾸先写下class Foo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻找metaclass属性,如果找到了,Python就会⽤它来创建类Foo,如果没有找到,就会⽤内建的type来创建这个类。把下⾯这段话反复读⼏次。当你写如下代码时:

class Foo(Bar):
    pass

Python做了如下的操作:

  1. Foo中有__metaclass__这个属性吗?如果是,Python会通过__metaclass__创建⼀个名字为Foo的类(对象)
  2. 如果Python没有找到__metaclass__,它会继续在Bar(⽗类)中寻找__metaclass__属性,并尝试做和前⾯同样的操作
  3. 如果Python在任何⽗类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作

  4. 如果还是找不到__metaclass__,Python就会⽤内置的type来创建这个类对象

你可以在__metaclass__中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东西都可以!

自定义元类

你决定在你的模块⾥所有的类的属性都应该是⼤写形式。有好⼏种⽅法可以办到,但其中⼀种就是通过在模块级别设定__metaclass__采⽤这种⽅法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成⼤写形式就万事⼤吉了!

Python2.x中的写法:

#-*- coding:utf-8 -*-
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 # 设置Foo类的元类为upper_attr
    bar = 'bip'


print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)

Python3.x的写法

#-*- coding:utf-8 -*-
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): # 设置Foo类的元类为upper_attr

    bar = 'bip'


print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))

f = Foo()
print(f.BAR)

两者的区别只是__metaclass的位置不一样!

就是这样,除此之外,关于元类真的没有别的可说的了。但就元类本身⽽
⾔,它们其实是很简单的:

  • 拦截类的创建
  • 修改类
  • 返回修改之后的类

“Yuan is the magic of depth, and 99% of households should not have to do so. If you want to find out if you need to go to a meta class, you don’t need it. Those that are actually metaclasses often know exactly what they need to do, and there is no need to explain why they need metaclasses at all. ” Tim Peters, the leader of the Python world!

猜你喜欢

转载自blog.csdn.net/m0_38032942/article/details/81459113