【python 笔记】元类中的__call__方法

【python 笔记】元类中的__call__方法

 

在学到用metaclass实现单例时,发现用的是元类的__call__方法,而不是__new__方法。对元类__call__比较疑惑,看起来似乎和普通类的__call__不一样。学习之后,一些基础的总结如下。

 

先是一些基本的概念等

  • 元类是类的类,元类之于类就相当于类之于实例。
  • 元类的new方法会创建一个类并返回,就像类的new方法会创建一个实例并返回一样。
  • 元类中其他方法的定义类似于类中方法的定义。
  • __call__方法是元类中有一个特殊的方法。

 

在元类方法中,参数cls对应的对象:

class Meta(type): 
    def __new__(cls, name, bases, dct):  # cls为元类Meta
        return type.__new__(cls, name, bases, dct)
    def __init__(cls, *args, **kwargs):  # cls为元类创建的类
        pass
    def __call__(cls, *args, **kwargs):  # cls为元类创建的类
        pass

 

对元类__new__、__call__的分析

 

Code1:元类 改写__new__方法

class SingletonType(type):
    def __new__(cls, class_name, class_parents, class_attr):  # cls为元类
        print('元类__new__')
        return type(class_name, class_parents, class_attr)

    def __init__(cls, *args, **kwargs):  #cls为元类创建的类
        print('元类__init__')
        super().__init__(*args, **kwargs)


class Foo(metaclass=SingletonType):
    def __init__(self, name):
        print("Foo __init__")
        self.name = name

    def __new__(cls, *args, **kwargs):
        print('Foo __new__')
        return object.__new__(cls)

obj = Foo('name')
print(obj)

obj = Foo('name')
print(obj)



# 输出:
# 元类__new__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f7312016080>
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f73120160b8>

Code1中:

  1. 定义类时,元类定义并执行了__new__,但元类的__init__不会再被调用
  2. 创建对象时,Foo类中的__new__方法和__init__方法依次执行。
  3. 第二次创建对象时,元类的__new__不会再被调用。因为元类的__new__方法会创建一个类并返回,而这个类在定义时已经存在了。

 

Code2: 元类 改写__call__特殊方法,不改写__new__方法

class SingletonType(type):
    def __call__(cls, *args, **kwargs):  # cls为元类创建的类
        print('元类__call__')
        # obj = cls.__new__(cls, *args, **kwargs)  # 手动调用Foo.__new__(obj)
        # cls.__init__(obj, *args, **kwargs)  # 手动调用Foo.__init__()
        obj = object.__new__(cls)
        return obj

    def __init__(cls, *args, **kwargs):  #cls为元类创建的类
        print('元类__init__')
        super().__init__(*args, **kwargs)


class Foo(metaclass=SingletonType):
    def __init__(self, name):
        print("Foo __init__")
        self.name = name

    def __new__(cls, *args, **kwargs):
        print('Foo __new__')
        return object.__new__(cls)

obj = Foo('name')
print(obj)

obj2 = Foo('name')
print(obj2)


# 输出:
# 元类__init__
# 元类__call__
# <__main__.Foo object at 0x7f2f9367b048>
# 元类__call__
# <__main__.Foo object at 0x7f2f9367b080>
  1. 两次创建对象都会调用元类的__call__方法。因为 元类__call__方法返回的是实例和类的__new__方法返回的一样
  2. 类的__new__方法和__init__方法并没有被执行。因为 元类__call__方法会截断类的__new____init__方法,阻止其执行。(可以手动调用执行)

Code3:元类 进一步改写__call__特殊方法,将其返回改为type.__call__(cls, *args, **kwargs)

class SingletonType(type):
    def __call__(cls, *args, **kwargs):  # cls为元类创建的类
        print('元类__call__')
        # obj = cls.__new__(cls, *args, **kwargs)  # 手动调用Foo.__new__(obj)
        # cls.__init__(obj, *args, **kwargs)  # 手动调用Foo.__init__()
        # obj = object.__new__(cls)
        obj = type.__call__(cls, *args, **kwargs)
        return obj

    def __init__(cls, *args, **kwargs):  #cls为元类创建的类
        print('元类__init__')
        super().__init__(*args, **kwargs)


class Foo(metaclass=SingletonType):
    def __init__(self, name):
        print("Foo __init__")
        self.name = name

    def __new__(cls, *args, **kwargs):
        print('Foo __new__')
        return object.__new__(cls)

obj = Foo('name')
print(obj)

obj2 = Foo('name')
print(obj2)


# 输出:
# 元类__init__
# 元类__call__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f5da0368048>
# 元类__call__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7f5da0368080>



Code3中,元类的__call__中返回type.__call__(cls, *args, **kwargs),则type创建的对象,里面会调用Foo__new__方法,和__init__方法,而不会被屏蔽。

 

 

Code4: 元类 改写__call__特殊方法,也改写__new__方法

class SingletonType(type):
    def __new__(cls, class_name, class_parents, class_attr):  # cls为元类
        print('元类__new__')
        return type(class_name, class_parents, class_attr)


    def __init__(cls, *args, **kwargs):  #cls为元类创建的类
        print('元类__init__')
        super().__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):  # cls为元类创建的类
        print('元类__call__')
        # obj = cls.__new__(cls, *args, **kwargs)  # 手动调用Foo.__new__(obj)
        # cls.__init__(obj, *args, **kwargs)  # 手动调用Foo.__init__()
        # obj = object.__new__(cls)
        obj = type.__call__(cls, *args, **kwargs)
        return obj


class Foo(metaclass=SingletonType):
    def __init__(self, name):
        print("Foo __init__")
        self.name = name

    def __new__(cls, *args, **kwargs):
        print('Foo __new__')
        return object.__new__(cls)


obj = Foo('name')
print(obj)

obj2 = Foo('name')
print(obj2)

# 输出:
# 元类__new__
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7fcc476fb048>
# Foo __new__
# Foo __init__
# <__main__.Foo object at 0x7fcc476fb080>

1.执行了元类__new__方法,而__call__方法并没有执行,所以也没有因为__call__方法而屏蔽了类的__new____init__方法。

 

总结

  1. 元类的__new__方法会创建一个类并返回,类似类的new方法会创建一个实例并返回
  2. 从代码看,元类同是改写__new__和__init__时,后者不会被调用(存疑)
  3. 元类__call__方法返回的是实例和类的__new__方法返回的一样
  4. __call__方法返回值不是 type.__call__(cls, *args, **kwargs) 时,元类__call__方法会截断类的__new____init__方法,阻止其执行
  5. 如果元类的__call__返回type.__call__(cls, *args, **kwargs)type创建的对象,里面会调用类的__new__方法,和__init__方法。

对比

在元类中:

创建的对象是类,如果希望能够自定义它,一般改写__new__;
如果有需要的话,也可以在__init__中做些事情;
一些高级的用法会涉及到改写__call__特殊方法。

 

 

 

-----end-----

水平有限,出错难免,还望指正!

 

发布了50 篇原创文章 · 获赞 10 · 访问量 6601

猜你喜欢

转载自blog.csdn.net/qq_23996069/article/details/104594802