python中的元类详解

在python中有句话”一切皆对象“,那么什么是对象?

一、什么是对象?

例如:

python中的字符串,整型、浮点型、列表、元组、字典、集合、布尔类型等,大多时候我们在使用这些类型时都是直接定义变量赋值使用,如下:

s = 'abc'
f = 0.01
d = {"a":1}
li = [1,2]

那为什么我们在使用这些变量时可以直接使用他们相应的方法?如下:

s.index()
f.is_integer()
d.pop()
li.index()

而对于我们熟知的str、float、dict、list这些关键字,其实都是一个一个的类,上面定义的一个个变量,其实就是指向了通过类实例化的存储在内存中的一个个的对象,上面的定义方法只是简写而已。如下:

s = str('abc')  ====>s = 'abc'
f = float(0.01) ====>f = 0.01
d = dict({'a':1}) ====> d = {"a":1}
li = list([1,3]) ====> li = [1,2]

我们也可以在python中看到这些的源码

 由此可以知道我们在定义的所谓的变量,其实就是一个个通过类实例化出来的对象,

对象即就是通过类实例化出来的

s = 'abc'
f = 0.01
d = {"a":1}
li = [1,2]

二、元类

在上面我们讨论了”一切皆对象“ ,对象是由类实例化而得到的,如下

class Foo(object):
    def __init__(self):
        self.__classname = self.__class__

    @property
    def name(self):
        return self.__classname


f1 = Foo()

在这里Foo是一个f1就是通过类Foo实例化出来的一个对象。那么既然都说一切皆对象,那么Foo在 "一切皆对象"这个理论下,那么Foo也应该是一个对象。那么Foo又是通过什么实例化出来的?。

对于f1,可以通过type(f1) 看到f1是通过Foo类调用实例化出来的

print(type(f1))

#
<class '__main__.Foo'>

那么我们也可以看看Foo是通过那个类调用实例化出来的

print(type(f1))
print(type(Foo))


# 

<class '__main__.Foo'>
<class 'type'>

由此我们可以知道Foo是通过type这个类实例化调用出来的。

所谓元类 ,type这个类,在python中我们所有通过class这个关键字定义出来的类,都是通过type类所实例化出来的对象。

三、通过type定义类

对于如下定义类的方式,是通过type元类实例化出来的类对象。在通过class定义类时,为其提供了一个关键字metaclass来指定当前类的元类。如果没有指定则默认为type元类

class Foo(object):
    def __init__(self):
        self.__classname = self.__class__

    @property
    def name(self):
        return self.__classname

f1 = Foo()

既然type元类也是一个类,那么我们也可以通过type关键字来定义一个类,如下:

def __init__(self):
    self.__name = self.__class__


@property
def name(self):
    return self.__name


Foo = type('Foo', (object,), {'__init__': __init__, 'name': name})
foo = Foo()
print(foo.__name)
print(foo.__class__)
print(type(foo))
print(type(Foo))

#

<class '__main__.Foo'>
<class '__main__.Foo'>
<class '__main__.Foo'>
<class 'type'>

格式:

type(classname, (classbase,),{类空间结构体})

  • classname: 类名
  • classbases : 所继承的类
  • 类空间结构体:即变量、函数、条件语句等

四、自定义元类和类和元类实例化过程

1、自定义元类

在上面我们知道我们自己定义的类都是通过type这个元类调用实例化出来的。并且我们可以通过metaclass关键字指定当前类的元类。

那么我们可以通过继承元类type可以在其基础上扩展其他的功能这样我们每次在定义类的时候就可以得到事先在元类中定义好的功能。

需求:

定义一个元类MyMeta,并初始化属性color=red,使得元类MyMeta的所有实例化属性都具有该属性

class MyMeta(type):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # 调用父类的初始化函数,确保自定义的元类具有父类的初始化属性
        self.color = 'red'  # 初始化属性color
        print("2------通过元类<%s>的__init__初始化从__new__传过来的空类对象%s" % (
        self.__class__.__name__, self.__name__))  # 这里的self就是当前通过__new__实例化的空对象

    def __new__(cls, *args, **kwargs):
        new_obj = super().__new__(cls, *args, **kwargs)
        print('1------通过元类<%s>的__new__方法实例化空类对象:%s,返回传递给__init__' % (cls.__name__, new_obj.__name__))
        return new_obj



class Foo(object, metaclass=MyMeta):  # >>>>相当于:# >>>>相当于:Foo1= MyMeta('Foo1', (object,),{})
    pass


print('3------打印类对象的属性color', Foo.color)

2、元类的实例化过程

Foo= MyMeta('Foo', (object,),{})   === class Foo(object, metaclass=MyMeta)

   如下所示即元类的实例化过程 :__new__ -------------> __init__

1------通过元类<MyMeta>的__new__方法实例化空类对象:Foo,返回传递给__init__
2------通过元类<MyMeta>的__init__初始化从__new__传过来的空类对象Foo
3------打印类对象的属性color red

进程已结束,退出代码为 0

3、类的实例化过程(也是完整的类实例化调用过程)

我们知道类中有个魔法方法   __call__ ,该方法的调用前提是   类的实例化对象()

即有个类Foo  ,实例化对象 foo = Foo(). 调用foo()即会用调用 __call__ 方法

如下代码:

class MyMeta(type):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # 调用父类的初始化函数,确保自定义的元类具有父类的初始化属性
        self.color = 'red'  # 初始化属性color
        print("2------通过元类<%s>的__init__初始化从__new__传过来的空类对象%s" % (
            self.__class__.__name__, self.__name__))  # 这里的self就是当前通过__new__实例化的空对象

    def __new__(cls, *args, **kwargs):
        new_obj = super().__new__(cls, *args, **kwargs)
        print('1------通过元类<%s>的__new__方法实例化空类对象:%s,返回传递给__init__' % (cls.__name__, new_obj.__name__))
        return new_obj

    def __call__(self, *args, **kwargs):
        print("3------调用元类<%s>:的__call__方法" % self.__class__.__name__)
        new_obj = self.__new__(self)
        print("4------通过元类<%s>的类对象<%s>的__new__方法实例化对象%s,并返回给元类对象%s的__init__方法" % (
            self.__class__.__name__, self.__name__, new_obj, self.__name__))
        self.__init__(self, *args, **kwargs)
        print("5------通过元类<%s>的类对象<%s>的__init方法初始化对象%s" % (self.__class__.__name__, self.__name__, new_obj))
        return new_obj


class Foo(object, metaclass=MyMeta):  # >>>>相当于:# >>>>相当于:Foo1= MyMeta('Foo1', (object,),{})
    def __init__(self, *args, **kwargs):
        self.name = kwargs.get('name')
        self.age = kwargs.get('age')

    def __new__(cls, *args, **kwargs):
        new_obj = super().__new__(cls, *args, **kwargs)
        return new_obj

print("3------通过元类对象Foo,实例化对象foo.调用方式为foo=Foo(name='python', age=20)")
foo = Foo(name='python', age=20)
print("6------打印对象foo的属性%s,%s,%s" % (foo.name, foo.age,foo.color))

foo = Foo(name='python', age=20), 其中Foo,是元类的实例化对象。那么我们在上面提到__call__ ,该方法的调用前提是   类的实例化对象()。那么Foo(name='python', age=20)就会去调用元类的__call__方法。

以下调用过程就是完整的类调用过程。该过程就是我们平时在定义类,初始化一个类对象,然后调用类对象的完整过程:

1------通过元类<MyMeta>的__new__方法实例化空类对象:Foo,返回传递给__init__
2------通过元类<MyMeta>的__init__初始化从__new__传过来的空类对象Foo
3------通过元类对象Foo,实例化对象foo.调用方式为foo=Foo(name='python', age=20)
4------调用元类<MyMeta>:的__call__方法
5------通过元类<MyMeta>的类对象<Foo>的__new__方法实例化对象<__main__.Foo object at 0x000001DB665E9CA0>,并返回给元类对象Foo的__init__方法
6------通过元类<MyMeta>的类对象<Foo>的__init方法初始化对象<__main__.Foo object at 0x000001DB665E9CA0>
7------打印对象foo的属性python,20,red

进程已结束,退出代码为 0

五、通过元类实现单例类

class MyMeta(type):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.instance = None

    def __new__(cls, *args, **kwargs):
        new_obj = super().__new__(cls, *args, **kwargs)
        return new_obj

    def __call__(self, *args, **kwargs):
        if not self.instance:
            self.instance = self.__new__(self)
        self.__init__(self.instance, *args, **kwargs)
        return self.instance


class Meta(object, metaclass=MyMeta):
    pass


class Foo(Meta):
    def __init__(self, *args, **kwargs):
        self.__name = kwargs['name']

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, value):
        self.__name = value


foo1 = Foo(name='python')
foo2 = Foo(name='java')
print(id(foo1))
print(id(foo2))


#结果
1387986896496
1387986896496

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值