本篇章的很多魔术方法都是跟class的建立有关的
4,类构建篇
- __init_subclass__
- __set_name__
- __class_getitem__和__mro_entries__
- __prepare__
- __instancecheck__和__subclasscheck__
__init_subclass__方法
__init_subclass__这个方法你要定义在基类里面,然后当你以这个类为基类,定义一个衍生类的时候,这个方法就会被调用,我们尝试一个简单的例子,就是打印这个class,然后这里我们以base类为基类建立衍生类A,那运行结果呢就打印出来了这个class A,这个传进去的参数cls,是你刚刚建立的衍生类
class Base: def __init_subclass__(cls): print(cls) class A(Base): pass
<class '__main__.A'>
这里我们可以把这个例子稍微做复杂一点点,我们让这个衍生类里面增加一个attribute x,让它是一个空的Dictionary,我们看在我们定义了A之后,这个A.x就是一个空的Dictionary,
class Base: def __init_subclass__(cls): cls.x = {} class A(Base): pass print(A.x)
# {}
那这个方法呢还可以传参数,比如下面我们可以要求它传一个name,然后在定义衍生类的时候,我们把这个Jack作为name传进去,可以看到这个衍生类A的name attribute就被赋值了
class Base: def __init_subclass__(cls, name): cls.x = {} cls.name = name class A(Base, name="Jack"): pass print(A.x) print(A.name) # {} # Jack
__set_name__方法
__set_name__方法更多的是定义在这个descriptor class 里,即使这个class它不是一个descriptor class,它依然起作用,这个方法相当于一个hook,当你在类的定义里去构建一个这个class的instance的时候,这个方法就会被调用,执行一下下面的程序,我们看owner 是A,也就是在哪个class定义里面,去构建的这个instance,而name是它赋值的这个变量的名字。那这个方法呢,有的时候我们在做log的时候可以使用
class D: def __set_name__(self, owner, name): print(owner, name) class A: x = D()
<class '__main__.A'> x
__class_getitem__方法
接下来的两个方法都是在强化typing的时候被引入的,第一个叫做__class_getitem__,__getitem__是这个class的object用方括号尝试access value的时候使用的魔术方法,那__class_getitem__就是这个类用方括号做 subscribe 的时候会调用的方法,下面我们写了一个简单的__class_getitem__函数,把这个item打印出来,然后返回abc,在下面我们打印一个A[0],注意这个A是class而不是class A的object,我们会发现item是0被打印出来了,然后A[0]是abc也被打印出来了
class A: def __class_getitem__(cls, item): print(item) return "abc" print(A[0]) # 0 # abc
这个东西跟typing的关系,这个list[int]表示我这个type是一个由integer组成的list,我们可以把它赋值到一个变量上,然后用这个变量再给其他的list做type hint,而type hint本身就是python代码,所以这个list[int]就必然有其实现的方式,我们观察一下它就是一个class然后方括号里面方东西,那它就是用__class_getitem__实现的。
class A: def __class_getitem__(cls, item): print(item) return "abc" print(A[0]) int_arr_type = list[int] lst1: int_arr_type = [] lst2: int_arr_type = []
__mro_entries__方法
另外一个更晦涩一点的函数叫做__mro_entries__,我们在B做继承A的时候,更多的时候括号里面是A是一个class object,而现在我们使用的是A(),也就是class A的一个object,如果我们直接这么写的话会报错
class A: pass # def __mro_entries__(self, bases): # print(bases) # return () class B(A()): pass
TypeError: A() takes no arguments
这里为什么会报错呢,简单来说就是建立B的时候它寻找自己的基类出现了问题,那这个__mro_entries__呢就是帮他去找基类的,它需要返回一个tuple,就是你去哪找你的基类去,那我们加上这个方法之后,它就能运行了
class A: def __mro_entries__(self, bases): print(bases) return () class B(A()): pass
(<__main__.A object at 0x000001A3D34DFCD0>,)
我们在下面这种情况下这个__mro_entries__返回的是一个空的tuple,这个的意思就是说你从我这找不到任何基类,别地儿找去,正因如此,这个时候如果你去打印issubclass(B,A),你就会发现A它并不是B的基类,因为B试图官这个A的object去问 你这个object 我怎么拿到你的基类啊 它说这里没有你的基类啊 一边呆着去
class A: def __mro_entries__(self, bases): print(bases) return () class B(A()): pass print(issubclass(B, A))
(<__main__.A object at 0x000002847D3CFCD0>,) False
我们现在把这个代码稍微作点改变,现在这个__mro_entries__返回一个带A的tuple,这就相当于B问这个A的object说 你能给我什么基类啊
然后它说就把A当成你的基类吧,这种情况下 issubclass(B,A)就是True了,因为B就会认为A是自己的基类,
class A: def __mro_entries__(self, bases): print(bases) return (A, ) class B(A()): pass print(issubclass(B, A))
(<__main__.A object at 0x000001298760FCD0>,) True
__prepare__方法
接下来看几个与metaclass相关的魔术方法,首先是这个__prepare__方法,它是用来准备你要构建的这个class的命令空间的,这里注意一下你需要手动的给这个__prepare__方法加上classmethod这个decorator,我们简单运行一下,可以看到当我们用这个metaclass Meta来构建A的时候,__prepare__方法被运行了,然后打印了A bases没有 keywords也没有
class Meta(type): @classmethod def __prepare__(cls, name, bases, **kwds): print(name, bases, kwds) return {} class A(metaclass=Meta): pass
A () {}
这里我们可以在这个__prepare__函数里面,返回一个Dictionary,这个Dictionary里面是x:10,那在构建这个class A之后,A就会有x这个attribute,然后值是10
class Meta(type): @classmethod def __prepare__(cls, name, bases, **kwds): return {"x": 10} class A(metaclass=Meta): pass print(A.x) # 10
__instancecheck__方法和__subclasscheck__方法
__instancecheck__和__subclasscheck__它们对应的函数就是isinstance跟issubclass这个两个函数,我们在做isinstance(123, A)的时候,就调用了A的元类的__instancecheck__这个函数,同样的当我们做issubclass(int, A)的时候,就调用了A的元类的__subclasscheck__函数,这个函数我们稍微写得复杂了一点点,说如果传进来的class是int的话,返回True否则返回False,可以看到由于传进来的这个type是int,所以返回了True,那这两个方法一般也是在你做metaclass的时候才需要用到的
class Meta(type): def __instancecheck__(self, instance): print("Instance Check") return True def __subclasscheck__(self, subclass): print("Subclass Check") if subclass is int: return True return False class A(metaclass=Meta): pass o = A() print(isinstance(123, A)) print(issubclass(int, A))
Instance Check True Subclass Check True
标签:__,这个,name,python,魔术,构建,print,class,def From: https://www.cnblogs.com/jiushao-ing/p/17556708.html