实例和类
python 中的类和实例概念和c++是一样的 。在c++中实例是类在内存中可以运行的实体,说人话就是编译器会把类当成设计图。在内存中开辟空间,这个空间存放着可以运行的代码。这片空间就是实例,实例是相对于类来说的概念。而在写代码中。我们把引用/指向实例的符号叫做对象。
而python 中的类和实例的思想和这个是一样的。但是细节上有一点不同。后面我们会说。
类的new/init/call方法
class A():
def __new__(cls,*args: Any,**kwargs: Any) -> 'A':
print("A:__new__")
print("A:__new__ arg:",cls,args,kwargs)
##object.__new__(cls) ==super().__new__(cls)
return object.__new__(cls)
def __init__(self,*args: Any,**kwargs: Any) -> None:
print("A:__init__")
print("A:__init__ arg:",self,args,kwargs)
pass
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("A:__call__")
print("A:__call__ arg:",self,args,kwds)
pass
# new/init/call这三个内部方法会在实例的生成时候调用。
a=A(1231,abc=100)
b=A()
a()
A:__new__
A:__new__ arg: <class '__main__.A'> (1231,) {'abc': 100}
A:__init__
A:__init__ arg: <__main__.A object at 0x00000144DC46C1F0> (1231,) {'abc': 100}
A:__new__
A:__new__ arg: <class '__main__.A'> () {}
A:__init__
A:__init__ arg: <__main__.A object at 0x00000144DC46C220> () {}
A:__call__
A:__call__ arg: <__main__.A object at 0x00000144DC46C1F0> () {}
- new 是python 生成对象用的。*这个方法的返回必须是生成的对象,很重要
- init 一旦对象被生成就会自动调用init 方法来初始化它
- call 当使用函数调用来调用实例的时候会使用到它,对应代码a()
因为我们不可能自己手动开辟内存。创建python 对象。所以必须调用object的new方法来帮我们完成这个基本操作。
A(1231,abc=100) 中传递的参数会被传递给new 和init 方法
object.__new__(cls)
和super().__new__(cls)
等价的。类的new 方法只需要一个类对象就能初始化。后面 args 和kwargs 是A(1231,abc=100) 传递进去的参数
元类new/init/call方法
元类(metaclass),什么元类呢?元类就是类的类。我举个例子
from typing import Any
# from typing import Self
class metaA(type):
# 生成class 对象
def __new__(cls,*args,**kwargs) -> 'metaA':
print("metaA class __new__")
print("metaA class __new__ arg:" ,cls ,*args,**kwargs)
A=super().__new__(cls,*args,**kwargs)
# A=type(*args,**kwargs)
# print(type(A))
# return type(*args)
return A
#初始化对象
def __init__(self,*args,**kwargs) -> None:
print("metaA class __init__")
# print("metaA class __init__ arg:" ,self ,*args,**kwargs)
super().__init__(self)
pass
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("metaA class __call__")
# print("metaA class __call__ arg:" ,self ,*args,**kwds)
return super().__call__(self,*args,**kwds)
class A(metaclass=metaA):
def __new__(cls,*args: Any,**kwargs: Any) -> 'A':
print("A:__new__")
return object.__new__(cls)
def __init__(self,*args: Any,**kwargs: Any) -> None:
print("A:__init__")
pass
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("A:__call__")
pass
# A=metaA()
a=A()
b=A()
a()
metaA class __new__
metaA class __new__ arg: <class '__main__.metaA'> A () {'__module__': '__main__', '__qualname__': 'A', '__new__': <function A.__new__ at 0x0000021594655160>, '__init__': <function A.__init__ at 0x00000215946551F0>, '__call__': <function A.__call__ at 0x0000021594655280>}
metaA class __init__
metaA class __call__
A:__new__
A:__init__
metaA class __call__
A:__new__
A:__init__
A:__call__
\qquad
很简单,我们给A加了一个metaA的元类。对python 稍有了解的肯定都听说过,python 中一切皆为对象。所以我们见到的和自己定义的类都是class 对象。一开始我们说过对象就指向内存中一片可以运行的代码空间。它的分配是由class 的new来分配的。然而现在你告诉我class 也是对象。这就出现一个问题,我们的class 对象在内存中的又是由谁分配的呢?
\qquad
要是像c++这样的语言,类在内存中是没有实体的,所有的类的代码逻辑都是编译时候写死在内存中的。而python不一样,python的类是一个对象。所以这个对象(也就是我们定义的类)是可以在运行时候按照特定逻辑动态生成的。即我们肯伊按照不同的需求生成不同的类。再又不同的类生成不同的对象。
\qquad
所以回到刚才的问题。我们的class 对象由谁生成的呢?这个任务就是由元类的new方法来完成的。
\qquad
上面的代码就是按照metaA => A=>a 的方式生成对象的。
在说一下细节.
- 元类的new的返回值必须是class对象。也就是内置函数type(name, bases, dict)的返回值。不然后面代码的A就是NoneType,参数cls 是metaA 这个class对象
- 元类的call 的返回值必须是普通对象也就是
object.__new__
返回值 - 你可以把元类生成类想成 A=metaA() 。这样就和类一样了。这样metaA()就是调用new方法返回A ,A()就是调用metaA的call 方法返回a对象。如果没有定义metaA的call,默认行为就是再去调用A的new来生成a对象,这样回到类的初始化流程了。
- 上面的几条总结一些的核心论据就是。想控制生成什么类就改元类的new方法,想控制类到对象的生成就改元类的call方法,
元类new中使用type()
和type.__new__()
的区别
我之前发现有人在元类的new中使用type来生成类。就像下面这样
from typing import Any
# from typing import Self
class metaA(type):
# 生成class 对象
def __new__(cls,*args,**kwargs) -> 'metaA':
print("metaA class __new__")
print("metaA class __new__ arg:" ,cls ,*args,**kwargs)
# A=super().__new__(cls,*args,**kwargs)
# A=type.__new__(cls,*args,**kwargs)
A=type(*args,**kwargs)
print("metaA class __new__ A:",A)
print("metaA class __new__ type:", type(A))
return A
#初始化对象
def __init__(self,*args,**kwargs) -> None:
print("metaA class __init__")
# print("metaA class __init__ arg:" ,self ,*args,**kwargs)
super().__init__(self)
pass
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("metaA class __call__")
# print("metaA class __call__ arg:" ,self ,*args,**kwds)
A=super().__call__(self,*args,**kwds)
return A
class A(metaclass=metaA):
def __new__(cls,*args: Any,**kwargs: Any) -> 'A':
print("A:__new__")
return object.__new__(cls)
def __init__(self,*args: Any,**kwargs: Any) -> None:
print("A:__init__")
pass
def __call__(self, *args: Any, **kwds: Any) -> Any:
print("A:__call__")
pass
# A=metaA()
a=A()
b=A()
a()
metaA class __new__
metaA class __new__ arg: <class '__main__.metaA'> A () {'__module__': '__main__', '__qualname__': 'A', '__new__': <function A.__new__ at 0x000001A6ED818E50>, '__init__': <function A.__init__ at 0x000001A6ED818EE0>, '__call__': <function A.__call__ at 0x000001A6ED818F70>}
metaA class __new__ A: <class '__main__.A'>
metaA class __new__ type: <class 'type'>
A:__new__
A:__init__
A:__new__
A:__init__
A:__call__
\qquad
你可以发现没有调用到元类的call方法。一开始我也不太明白,才写了这篇文档来理解这个。问题的原因就是元类new方法的返回值上。你可以看到的他是type的实例。而我们使用type.__new__()
返回的是metaA的实例。也就是虽然都是叫class A 但是他是不同对象的实例。所以上面的例子其实对象初始化是由type的call 来完成的。所以才不会打印我们的信息。
\qquad 所以上面这种写法极力不推荐的,或者说是一种错误。
复杂关系中的元类
当你弄懂下面两这句话证明你已经解锁前置科技。可以往后看。
- object 类没有父类,object 类是type 类的实例,
- type 类的父类是object类,type 类是type类的实例、
我们创建一下代码验证
print("----------start-------------")
#元类必须集成type类
class meta(type):
# 创建类
def __new__(cls, name, bases, attrs):
#打印类名称,父类。成员
print("meta new:",cls, name, bases,)
#传递给type函数创建类
new_cls=super().__new__(cls, name, bases, attrs)
#在看看他的类型
print("type of new_cls:",type(new_cls))
#被创建的类是不是object ,type的实例
print("isinstance of cls:",isinstance(new_cls,cls))
print("isinstance of type:",isinstance(new_cls,type))
print("isinstance of oject:",isinstance(new_cls,object))
#被创建类的父类
print("bases:",new_cls.__bases__)
return new_cls
class metaa(meta):
pass
class A(metaclass=metaa):
def __init__(self):
print("A init")
class B(A):
def __init__(self):
print("B init")
b=B()
print("------------end------------")
----------start-------------
meta new: <class '__main__.metaa'> A ()
type of new_cls: <class '__main__.metaa'>
isinstance of cls: True
isinstance of type: True
isinstance of oject: True
bases: (<class 'object'>,)
meta new: <class '__main__.metaa'> B (<class '__main__.A'>,)
type of new_cls: <class '__main__.metaa'>
isinstance of cls: True
isinstance of type: True
isinstance of oject: True
bases: (<class '__main__.A'>,)
B init
------------end------------
当python 运行代码时,解释器会扫描文件,遇到类就开始创建类对象,你可以看到整个继承链中,如果子类没有定义metaclass,所有的类都是最顶层父类的metaclass 的’new‘方法创建的,且这些被创建的类都是自定义metaclass的实例,同时自定义的metaclass是type的子类。所以这些被创建的类同时也是type的实例。而type又是object的子类。所以他们也都是object 的实例。
isinstance、 issubclass、type
- issubclass:判断2个类是不是继承关系,等于超级__base__
- isinstance 判断对象是不是某个类的实例
- type 打印对象的元类 等于__class__
简单的参考
https://blog.csdn.net/qq_23996069/article/details/104594802
https://blog.csdn.net/Spade_/article/details/113485937