Python类的__new__,__init__,__getitem__,__setitem__,__setattr__,__getattr__等魔术方法

class my_meta(type):
    def __init__(self, *args):
        print('__init__')

    def __call__(self, *args, **kwargs):
        print('__call__')
        return super(my_meta, self).__call__()

    def __new__(cls, *args, **kwargs):
        print('__new__')
        return super(my_meta, cls).__new__(cls, *args, **kwargs)


class T(metaclass=my_meta):
    def __init__(self):
        print("init")
        pass

    def __call__(self, *args, **kwargs):
        print("call")

    def __new__(cls, *args, **kwargs):
        print("new")
        return super().__new__(cls, *args, **kwargs)

    def __setattr__(self, key, value):
        print('setattr')
        super(T, self).__setattr__(key, value)

    def __getattr__(self, item):
        print('getattr')
        return self.__dict__.get(item)

    def __setitem__(self, key, value):
        print('setitem')
        super(T, self).__setattr__(key, value)

    def __getitem__(self, item):
        print('getitem')
        return self.__dict__.get(item)

    def __getattribute__(self, item):
        print('get_attribute')
        return super(T, self).__getattribute__(item)

if __name__ == '__main__':
    t = T()
    print('-' * 90)
    t.name = 'z'
    t['nn'] = 'nn'
    print(t.nn)
    print('x'*99)
    print(t.nn)
    print(t['nn'])
    print('-' * 90)
    print(t.z)
    # print(t['z'])

程序运行结果为

__new__
__init__
__call__
new
init
------------------------------------------------------------------------------------------
setattr
setitem
get_attribute
nn
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
get_attribute
nn
getitem
get_attribute
nn
------------------------------------------------------------------------------------------
get_attribute
getattr
get_attribute
None

    接下来来分析结果,首先,类是由元类(metaclass)创建的,当Python解释器运行到 class T时,会调用元类的__new__()来创建一个类,然后调用__init__()实例化对象,这个就是T类。当解释器执行到 t=T()时,类名加括号会调用元类的__call__()方法(因为T是由my_meta创建的,T是它的一个实例,实例加括号会调用它类的call方法),再调用类的__new__创建、__init__初始化并返回创建的实例,也就是t。

    类实例的所有属性都会保存在__dict__中。

    当使用 类实例.xx时(t.xx)会调用类的__getattribute__()函数,如果报错或者为找到则会继续调用getattr方法,xx会当作getattr函数的key参数传进去。

    类实例['xx'](t['xx'])会调用getitem方法,参数与getattr一致,当未找到值时也调用__getattribute__方法。

    使用赋值语句时(t['xx']='xx' \ t.xx=‘xxx')会调用对应的set方法(setitem或setattr)setattr可调用父类的setattr来将值设置进去。

你可能感兴趣的