[原]Python元类

李余通 18/03/31 21:16:43

python中的类

type这个方法有什么用?

首先纠正一个错误,type并非一个方法,而是一个类(扎心了老铁)。

>>> type(dict)
<class 'type'>
>>> type(type)
<class 'type'>
>>> type(object)
<class 'type'>

很奇怪的一个现象,似乎所有类都是type类,不是说所有类都是从object继承来的啊,为什么连object自己都是type类型呢?
这要归功于Python是一种解释型语言,并非像C++,Java一样属于编译型语言,在编译型语言中,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现。而动态语言,则是在运行期间创建类,那么怎么创建类,其实就是用的type

定义类的两种方法

# 直接定义
class Hello(object):
    'Hello'
    times = 0
    def hello(self,name='world'):
        print('Hello,%s.' % name)
        self.times += 1

# type
def fn(self,name='world'):
    print('Hello,%s.' % name)
    self.times += 1

Hello = type('Hello',(object,),dict(times=0,hello = fn))

这两种方法,实现的效果相同。
现在看看type的定义:

class type(object):
    """
    type(object_or_name, bases, dict)
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    """

只截取了一小部分,type类可以实现两种功能,

  • 显示对象的类型
  • 创建一个新的type

所以,当我们给type()传入相应的值(变量或名字,基类,数字字典),他就动态的创建了一个类。

元类 MetaClass

元类:用来定义类的类

一般来说,我们都是在代码里定义类,用定义的类来创建实例。而使用元类,步骤又是不同,定义元类,用元类创建类,在使用创建出来的类来创建实例。

# metaclass是类的模板,所以必须从'type'类派生:
# 习惯上 元类的类名是以Metaclass结尾
class ListMetaclass(type):
    def __new__(cls,name,bases,attrs):
        attrs['add'] = lambda self,value:self.append(value)
        return type.__new__(cls,name,bases,attrs)

class MyList(list,metaclass=ListMetaclass):
    pass

这段代码很简单,我们实现了一个MyList,它拥有add方法,实际上就是append,当然,如果元类仅仅能做到这么没用的事,那我宁可不做。

用元类将类中的类变量及方法改为大写

class UpperMetaclass(type):
    def __new__(cls,name,bases,attrs):
        print('first in metaclass')
        new_attrs = dict()
        for k,v in attrs.items():
            if not k.startswith('__'):
                new_attrs[k.upper()] = v
            else:
                new_attrs[k] = v

        return type.__new__(cls,name,bases,new_attrs)

class Upper(object,metaclass=UpperMetaclass):
    num = 0
    def __init__(self,name,password):
        print('then in init')
        self.name = name
        self.password = password

分析一下代码:
定义了一个元类,在它的__new__方法中将所有不以__开头的属性转换为大写,然后创建一个类。

print(dir(Upper))
==>
first in metaclass
['NUM', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

可以看出num被变成大写的了。

print(dir(Upper('lyt','lyt')))
==>
first in metaclass
then in init
['NUM', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'password']

可以看出,num变成大写了,但name,password没有,这个原因是显而易见的,由于元类是在生成类时发挥作用的,故而可以控制类变量,但不能控制生成实例时产生的实例变量。

作者:baidu_35085676 发表于 2018/03/31 21:16:43 原文链接 https://blog.csdn.net/baidu_35085676/article/details/79773259
阅读:78