Python:__init__() 不是唯一的构造函数

2025-05-25

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
Enter fullscreen mode Exit fullscreen mode

__new__()方法在执行之前被调用__init__()。这是因为在调用__new__()之前分配了内存。__init__()

def __new__(cls, *args, **kwargs)
Enter fullscreen mode Exit fullscreen mode

需要牢记的事项__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
Enter fullscreen mode Exit fullscreen mode

输出:

>>> p2 = Point(3,4)
From new
<class '__main__.Point'>
(3, 4)
{}
From init
Enter fullscreen mode Exit fullscreen mode

我们看到,在初始化对象__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)
Enter fullscreen mode Exit fullscreen mode

样本运行。

>>> 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
Enter fullscreen mode Exit fullscreen mode

简单的单例

在此你必须小心__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
Enter fullscreen mode Exit fullscreen mode

完全返回一个新对象

在这个例子int.__init__()中将被称为,而不是Example.__init__(),因为返回的对象不是类型Example而是int

class Example:
    def __new__(cls):
        return 3

type(Example())
# output: int
Enter fullscreen mode Exit fullscreen mode

因此,它们都像其他面向对象语言(例如 C++)的构造函数__new__()一样__init__()工作,用于分配和初始化对象。但正如__init__()上面提到的,我们通常将其称为__init__()“构造函数”,因为我们不关心分配。Python 是一种非常抽象的语言,我们不需要太关心内存。

我希望我的帖子能够很好地澄清这一点,如果有任何问题,请不要忘记发表评论。

文章来源:https://dev.to/delta456/python-init-is-not-a-constructor-12on
PREV
大多数教程中不会出现的 CSS 技巧
NEXT
Rust Easy!现代跨平台命令行工具,助你增强终端功能