Python:__init__() 不是唯一的构造函数
许多初学者,甚至是 Python 程序员,都认为它__init__()
是一个构造函数,因为它在调用时会初始化值。但它__init__()
是一个特殊的方法,用于初始化对象。它在对象创建并初始化时会立即被调用,并且不会分配内存。所以它并不是构造函数的唯一方法。
class Foo:
def __init__(self, value_a, value_b):
self.a = value_a
self.b = value_b
foo = Foo(7, 9) # __init__ is called
print(foo.a , foo.b) # 7 , 9
__new__()
方法在执行之前被调用__init__()
。这是因为在调用__new__()
之前分配了内存。__init__()
def __new__(cls, *args, **kwargs)
需要牢记的事项__new__()
:
__new__()
总是在之前被调用__init__()
。- 第一个参数是隐式传递的类本身。
- new ()总是返回一个有效的对象。这不是强制性的,但这就是重点。
__new__()
控制对象的创建。这意味着,在极端情况下,你可以在最后返回一个完全不同类型的对象。我稍后会给出一个例子。
class Point():
def __new__(cls,*args,**kwargs):
print("From new")
print(cls)
print(args)
print(kwargs)
# create our object and return it
obj = super().__new__(cls)
return obj
def __init__(self, x = 0, y = 0):
print("From init")
self.x = x
self.y = y
输出:
>>> p2 = Point(3,4)
From new
<class '__main__.Point'>
(3, 4)
{}
From init
我们看到,在初始化对象__new__()
之前会调用,并且可以看到中的参数是类本身(Point)。最后,通过调用object 基类的方法来创建对象。在 Python 中,object 是所有其他类都派生自的基类。在上面的例子中,我们使用 来实现这一点。__init__()
cls
__new__()
__new__()
super()
何时使用__init__()
和__new__()
?
您可能已经看到__init__()
比更频繁地使用,__new__()
因为我们主要需要初始化对象而不是如何控制对象,尽管我们可以使用__new__()
来初始化对象,但在中初始化它们更有意义__init__()
。
一些实际用途__new__()
是限制从一个类创建的对象的数量,创建一个单例并在最后返回一个完全不同的对象。
限制创建的对象数量
假设我们要创建一个类,RectPoint
用于创建表示正方形四个顶点的实例。我们可以继承之前的 Point 类(本文的第一个示例),并使用new () 来实现此限制。以下示例将一个类限制为只能有四个实例。
class RectPoint(Point):
MAX_Inst = 4
Inst_created = 0
def __new__(cls,*args,**kwargs):
if (cls.Inst_created >= cls.MAX_Inst):
raise ValueError("Cannot create more objects")
cls.Inst_created += 1
return super().__new__(cls)
样本运行。
>>> p1 = RectPoint(0,0)
>>> p2 = RectPoint(1,0)
>>> p3 = RectPoint(1,1)
>>> p4 = RectPoint(0,1)
>>>
>>> p5 = RectPoint(2,2)
Traceback (most recent call last):
...
ValueError: Cannot create more objects
简单的单例
在此你必须小心__init__()
,因为每次你“实例化”对象(即你执行a = Example()
)时都会调用它,即使它们返回同一个对象。
_singleton = None
class Example:
def __new__(cls):
global _singleton
if _singleton is None:
_singleton = super(Example, cls).__new__(cls)
return _singleton
a = Example()
b = Example()
a is b
# output: True
完全返回一个新对象
在这个例子int.__init__()
中将被称为,而不是Example.__init__()
,因为返回的对象不是类型Example
而是int
。
class Example:
def __new__(cls):
return 3
type(Example())
# output: int
因此,它们都像其他面向对象语言(例如 C++)的构造函数__new__()
一样__init__()
工作,用于分配和初始化对象。但正如__init__()
上面提到的,我们通常将其称为__init__()
“构造函数”,因为我们不关心分配。Python 是一种非常抽象的语言,我们不需要太关心内存。
我希望我的帖子能够很好地澄清这一点,如果有任何问题,请不要忘记发表评论。
文章来源:https://dev.to/delta456/python-init-is-not-a-constructor-12on