Python中元类的理解

Python中元类的理解:

定义:

1,元类(metaclass)是创建类的类,可以理解为类是元类的实例,只有type及其派生类才能充当元类。正如我们要创建一个类,才能创建一个实例对象。要创建一个元类,才能创建一个类。
一句话就是:元类—>类—>实例

2,元类是可以让你定义某些类是如何被创建出来的。从根本上说,就是赋予你如何创建类的控制权。元类也是一个类,是一个type类。

3, 元类一般用于创建类。在执行类定义时,python解释器必须要知道这个类的属性(_metaclass_)的元类。如果此属性没有定义,它会向上查找父类中的_metaclass_属性。如果都没有该类就通过普通的type创建类。

4,对于普通的类,它们的元类是types.ClassType。

从以下九个方面详细加以说明:

Num01–>类也是对象

在大多数的编程语言中,类就是一组用来描述如何生成对象的代码片段。在python中这一点仍然成立。代码如下:

#随便创建一个类
class ObjectCreator(object):
    pass

my_object = ObjectCreator()
print(my_object)
# 结果如下:
# <__main__.ObjectCreator object at 0x000002331C60C358>

但是python中类还远不止如此。类同样也是一种对象。只要你使用关键字class,python解释器在执行的时候,就会创建一个对象。

上面的那段代码,将在内存中创建一个对象,名字就是ObjectCreator。这个对象(类对象)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,既然是一个对象,就可以对它做如下操作:

1,可以将它(类对象)赋值给一个变量。
2,可以拷贝它(类对象)。
3,可以为它(类对象)增加属性。
4,可以把它(类对象)作为一个参数,传递给函数。

#随便创建一个类
class ObjectCreator(object):
    pass

my_object = ObjectCreator()
print(my_object)
# 结果如下:打印一个实例对象, <__main__.ObjectCreator object at 0x000002331C60C358>


# 1,可以打印一个类,因为它其实也是一个对象
print(ObjectCreator)
# 结果为:<class '__main__.ObjectCreator'>


def demo(o):
    print(o)
# 打印结果为:其实还是打印类,<class '__main__.ObjectCreator'>
# 2,你可以将类做为参数传给函数
demo(ObjectCreator)

# 3,你可以为类增加属性
ObjectCreator.attr2 = 'xiaoke'
# 判断类中是否有刚添加的属性
print(hasattr(ObjectCreator, 'attr2'))
# 结果为:True
print(ObjectCreator.attr2)
# 结果为:xiaoke

# 4,你可以将类赋值给一个变量
temp = ObjectCreator
print(temp())
# 结果为:其实就是打印一个实例对象,<__main__.ObjectCreator object at 0x0000018CE3647080>

Num02–>动态地创建类

因为类也是对象,你可以在运行时动态的创建类,就像其他任何对象一样。

下面的案例就是在函数中创建类,只要使用关键字class即可。

def choose_class(name):
    if name == 'xiaoke':
        class Foo(object):
            pass
        return Foo     # 返回的是Foo类,不是类的实例
    else:
        class Bar(object):
            pass
        return Bar     # 返回的是Bar类,不是类的实例

MyClass = choose_class('xiaoke')
# 函数返回的是类,不是类的实例
print(MyClass)
# 结果是:<class '__main__.choose_class.<locals>.Foo'>

# 你可以通过这个类创建类实例,也就是对象
print(MyClass())
# 结果是:<__main__.choose_class.<locals>.Foo object at 0x000001C3E7237128>

但这还不够动态,因为你仍然需要编写整个类的代码。由于类也是对象,所以它们必须通过什么来生成的才对。当你使用class关键字的时候,Python解释器自动创建这个对象。

内建函数type可以让你知道一个对象的类型是什么。就如下:

# 数值的类型
print(type(1))
# 结果是:<class 'int'>
# 字符串的类型
print(type("1"))
# 结果是:<class 'str'>
# 实例对象的类型
print(type(ObjectCreator()))
# 结果是:<class '__main__.ObjectCreator'>
# 类的类型
print(type(ObjectCreator))
# 结果是:<class 'type'>

仔细观察上面的运行结果,发现使用type对ObjectCreator查看类型时,答案为type。

Num03–>使用type创建简单的类

type还有一种完全不同的功能,就是动态的创建类。
type可以接收一个类的描述作为参数,然后返回一个类。
type的构成如下:

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

看下面的代码:

# 定义了一个ObjectCreate类
class ObjectCreate(object):
    pass

# 创建了一个ObjectCreate类的实例对象
print(ObjectCreate())
# 结果是:<__main__.ObjectCreate object at 0x000002714EDAD358>

# 使用type动态的创建类
NewObjectCreate = type("NewObjectCreate", (), {})

# 创建了一个NewObjectCreate类的实例对象
print(NewObjectCreate())
# 结果是:<__main__.NewObjectCreate object at 0x000001B51DC8D358>

print(help(ObjectCreate))
# 结果是:
'''
Help on class ObjectCreate in module __main__:

class ObjectCreate(builtins.object)
 |  # 定义了一个ObjectCreate类
 |  
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

None

'''

print(help(NewObjectCreate))
# 结果是:
'''
Help on class NewObjectCreate in module __main__:

class NewObjectCreate(builtins.object)
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

None

'''

Num04–>使用type创建带有属性的类

type接收一个字典来为类定义属性,如下:

# 定义Foo类
Foo = type("Foo", (), {"name": "xiaoke"})


# 上面定义Foo类,也可以理解为如下:
class Foo(object):
    name = 'xiaoke'

# 可以将类Foo当成一个普通的类使用
print(Foo)
# 结果是:打印类:<class '__main__.Foo'>
print(Foo.name)
# 结果是:xiaoke
f = Foo()
print(f)
# 结果是:打印一个实例对象:<__main__.Foo object at 0x00000203DBD5A0F0>
print(f.name)
# 结果是:xiaoke

print("======子类ChildFoo=========")
# 可以向这个类继承,所以,如下的代码:
class ChildFoo(Foo):
    pass

# 使用type创建ChildFoo类,继承父类Foo
ChildFoo = type("ChildFoo", (Foo,), {})
print(ChildFoo)
# 结果:<class '__main__.ChildFoo'>
print(ChildFoo.name)  # name属性是由子类ChildFoo继承父类Foo而来的
# 结果是:xiaoke

注意点:
1, type的第2个参数,元组中是父类的名字,而不是字符串。
2, 添加的属性是类属性,并不是实例属性。

Num05–>使用type创建带有方法的类

最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值(放在type的字典中)就可以。

特别注意:使用type创建带有方法的类时,这些方法(静态方法,类方法,普通方法等)都是放在类的外面。或者可以理解为:把这些方法都添加新的类中,作为新类的方法而已。如果新类有父类,父类的方法子类是直接继承下来;如果子类继承父类的方法中,有名字相同的方法,子类就是重写父类的方法,对该方法加以修改。

# 先定义一个带有类属性的类
class A(object):
    num = 100


# 在类的外部定义个普通的方法
def print_fun(self):
    print("----我是普通方法----")
    print("B类的属性值是:%d" % self.num)


# 在类的外部定义一个静态的方法,这仅在使用type创建类的时候,才把静态方法放在类外,其他都是在类中
@staticmethod
def print_static():
    print("----我是静态方法----")
    print("B类的属性值是:%d" % A.num)


# 在类的外部定义一个类方法,这仅在使用type创建类的时候,才把类方法放在类外,其他都是在类中
@classmethod
def print_class(cls):
    print("----我是类方法----")
    print("B类的属性值是:%d" % cls.num)


B = type("B", (A,), {"fun_B": print_fun, "static_B": print_static, "class_B": print_class})
b = B()
b.fun_B()
b.static_B()
b.class_B()
# 结果:
# ----我是普通方法----
# B类的属性值是:100
# ----我是静态方法----
# B类的属性值是:100
# ----我是类方法----
# B类的属性值是:100 

Num06–>到底什么是元类

元类就是用来创建类的东西。你创建类基本就是为了创建实例对象。
Python中的类也是对象。元类就是用来创建这些类(对象)的。
总之:元类就是类的类。

# 使用元类创建出一个对象,这个对象称为“类”
MyClass = MetaClass() 

# 使用“类”来创建出实例对象
MyObject = MyClass() 

使用type也可以这样创建类:

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

这是因为type实际上就是一个元类。type就是Python在背后用来创建所有类的元类。type之所以全部采用小写的原因,就是与str(字符串对象的类),int(整数对象的类)等类保持一致性的。type就是创建类对象的类,可以通过查看class这一属性来查看这一点。

Python中所有的东西都是对象。包括整数、字符串、函数和类等。它们全都是对象,而且它们都是从一个类创建而来,就是type类。

age = 35
print(age.__class__)
# 结果:<class 'int'>
name = 'bob'
print(name.__class__)
# 结果:<class 'str'>


def foo():
    pass
print(foo.__class__)
# 结果:<class 'function'>


class Bar(object):
    pass

b = Bar()
print(b.__class__)
# 结果:<class '__main__.Bar'>  

对于任何一个classclass属性又是怎样呢?

age = 35
print(age.__class__.__class__)
# 结果:<class 'type'>
name = 'bob'
print(name.__class__.__class__)
# 结果:<class 'type'>


def foo():
    pass
print(foo.__class__.__class__)
# 结果:<class 'type'>


class Bar(object):
    pass

b = Bar()
print(b.__class__.__class__)
# 结果:<class 'type'>

因此,元类就是创建类这种对象的东西。type就是Python中内置元类,当然,你也可以自定义元类。

Num07–>_metaclass_属性

你可以在定义一个类的时候,为其添加_metaclass_属性。

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

如果你这么做了,Python就会使用元类来创建类Foo。
首先,当你写下class Foo(object),但是类Foo还没有在内存中创建
其次,Python会在类的定义中寻找metaclass属性,如果找到了,Python就会用它来创建类Foo;如果没有找到,就用内建的type来创建这个类。

class ChildFoo(Foo):
    pass

上面的代码,Python做了如下的操作:
1,ChildFoo中有_metaclass_这个属性吗?如果有,Python会通过_metaclass_创建一个名字为ChildFoo的类(对象)。
2,如果在ChildFoo中没有找到_metaclass_,它会继续在Foo(父类)中查找_metaclass_属性,并尝试做和前面同样的操作。
3,如果在任何父类中都找不到_metaclass_属性,它就会在模块层次中去寻找_metaclass_,并尝试做和前面同样的操作。
4,如果最终还是没有找到_metaclass_属性,Python就会用内置的type来创建这个类对象。

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

Num08–>自定义元类

元类的主要目的就是当创建类的时候能够自动改变类。通常,你会为API做这样的事,你希望可以创建符合当前上下文的类。

_metaclass_属性可以被任意的调用,可以是一个函数,可以是一个方法,可以是一个类。

使用一个函数,来创建元类
在python2中

def upper_attr(future_class_name, future_class_parents, future_class_attr):

    # 遍历属性字典,把不是__开头的属性名字变为大写
    newAttr = {}

    for key, value in future_class_attr.items():
        if not key.startswith("__"):
            newAttr[key.upper()] = value

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


# 设置Foo类的元类为upper_attr
class Foo(object):
    __metaclass__ = upper_attr
    name = 'xiaoke'

print(hasattr(Foo, 'name'))
# 结果:False
print(hasattr(Foo, 'NAME'))
# 结果:True

f = Foo()
print(f.NAME)
# 结果:xiaoke
在Python3中:

def upper_attr(future_class_name, future_class_parents, future_class_attr):

    # 遍历属性字典,把不是__开头的属性名字变为大写
    newAttr = {}

    for key, value in future_class_attr.items():
        if not key.startswith("__"):
            newAttr[key.upper()] = value

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


# 设置Foo类的元类为upper_attr
class Foo(object, metaclass=upper_attr):
    name = 'xiaoke'

print(hasattr(Foo, 'name'))
# 结果:False
print(hasattr(Foo, 'NAME'))
# 结果:True

f = Foo()
print(f.NAME)
# 结果:xiaoke
使用一个真正的类,来创建元类
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:在python3中使用super方法
        #return super().__new__(cls, future_class_name, future_class_parents, newAttr)

        # 方法4:在python2中使用super方法
        return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr)


# python2的用法
 class Foo(object):
     __metaclass__ = UpperAttrMetaClass
     name = 'xiaoke'


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


print(hasattr(Foo, 'name'))
# 输出: False
print(hasattr(Foo, 'NAME'))
# 输出:True

f = Foo()
print(f.NAME)
# 输出:xiaoke

Num09–>元类案例:简单ORM的实现

# 1,来定义Field类,它负责保存数据库表的字段名和字段类型:
class Field(object):
    # 初始化数据
    def __init__(self, name, column_type):
        # 字符串类型的字段
        self.name = name
        # 整数类型的字段
        self.column_type = column_type

    # 打印的方法
    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

# 2,定义字符串类型的子类
class StringField(Field):

    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')


# 2.1,定义整数类型的子类
class IntegerField(Field):

    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')


# 3,定义一个元类,用来修改创建的类
class ModelMetaclass(type):

    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        # mappings = dict()
        mappings = {}
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings # 保存属性和列的映射关系
        attrs['__table__'] = name # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


# 4,原始类
class Model(dict, metaclass=ModelMetaclass):

    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))


# 5,应用的类
class User(Model):
    # 定义类的属性到列的映射:
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

# 创建一个实例:
u = User(id=666666, name='xiaoke', email='976249817@qq.com', password='521xiaoke')
# 保存到数据库:
u.save()

总结:

元类的作用:
1,拦截类的创建
2,修改类
3,返回修改之后的类

参考资料如下:
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319106919344c4ef8b1e04c48778bb45796e0335839000

http://blog.jobbole.com/21351/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值