GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • 卡码笔记-最强八股文
  • 计算机基础
  • Python
  • C++
  • Java
  • Go
  • ChatGPT充值
  • Claude充值
  • 国内使用Claude
  • 中转站推荐
  • 🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经

This view

  • Original page
  • New address

GeezerVison is showing notes.kamacoder.com — Python _new和init_的区别:对象创建和初始化的两个阶段

# __new__和__init__有什么区别?

面试官问:"Python的__new__和__init__有什么区别?"

很多人只知道__init__是初始化方法,不知道还有__new__。真正要讲清楚的是:__new__负责创建对象并返回它,__init__负责初始化已经创建好的对象。 __new__先执行,返回值是对象本身;__init__后执行,在对象上添加属性,没有返回值。单例模式和自定义不可变类型时会重写__new__。

# 简要回答

__new__是类方法,负责创建对象。 它的第一个参数是cls(类本身),返回一个新创建的对象。如果__new__返回的是这个类的实例,Python会自动调用__init__初始化它。

__init__是实例方法,负责初始化对象。 它的第一个参数是self(刚创建好的对象),用于设置对象的初始属性。__init__不需要返回值,返回None。

调用顺序:obj = Class(args) → __new__(cls, args) → 返回对象 → __init__(self, args) → 返回给调用方。

# 详细回答

# 调用过程

当你写obj = MyClass(a, b)时,Python实际执行:

  1. 调用MyClass.__new__(MyClass, a, b),创建对象。
  2. 如果__new__返回的是MyClass的实例,**调用obj.__init__(a, b)**初始化。
  3. 返回初始化后的对象。
class Person:
    def __new__(cls, name):
        print(f"__new__ called with cls={cls}, name={name}")
        instance = super().__new__(cls)  # 调用object.__new__创建对象
        print(f"__new__ returns {instance}")
        return instance
    
    def __init__(self, name):
        print(f"__init__ called with self={self}, name={name}")
        self.name = name
        print("__init__ done")

p = Person("Alice")
# 输出:
# __new__ called with cls=<class '__main__.Person'>, name=Alice
# __new__ returns <__main__.Person object at 0x...>
# __init__ called with self=<__main__.Person object at 0x...>, name=Alice
# __init__ done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 参数和返回值

__new__:

  • 第一个参数是cls,是类本身,不是对象。
  • 必须返回一个对象。通常返回super().__new__(cls)。
  • 如果返回的不是当前类的实例,__init__不会被调用。

__init__:

  • 第一个参数是self,是已经创建好的对象。
  • 不应该有返回值(或返回None)。如果返回其他值,Python会报错。
  • 只负责设置属性,不负责创建对象。
class Test:
    def __new__(cls):
        print("Creating object")
        return super().__new__(cls)
    
    def __init__(self):
        print("Initializing object")
        self.value = 42

t = Test()
# 输出:
# Creating object
# Initializing object
1
2
3
4
5
6
7
8
9
10
11
12
13

# 为什么要分两个阶段

大多数情况下,你只需要重写__init__。但有些场景需要在对象创建时做特殊处理,这时就要重写__new__:

  1. 单例模式:确保类只创建一个实例。
  2. 继承不可变类型:int、str、tuple等不可变对象在__init__时已经不能修改,必须在__new__阶段定制。
  3. 元类和特殊对象:控制对象的创建过程。

__new__和__init__的执行流程

# 单例模式的实现

单例模式要求类只能创建一个实例。 通过重写__new__,检查是否已经创建过对象,如果有就返回已有对象:

class Singleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            print("Creating the only instance")
            cls._instance = super().__new__(cls)
        else:
            print("Returning existing instance")
        return cls._instance
    
    def __init__(self):
        print("__init__ called")

s1 = Singleton()  # Creating the only instance, __init__ called
s2 = Singleton()  # Returning existing instance, __init__ called
print(s1 is s2)   # True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意:__init__每次都会被调用,即使返回的是同一个对象。如果不想重复初始化,需要加个标志位:

class Singleton:
    _instance = None
    _initialized = False
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not Singleton._initialized:
            print("First init")
            self.value = 42
            Singleton._initialized = True
        else:
            print("Already initialized")

s1 = Singleton()  # First init
s2 = Singleton()  # Already initialized
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 继承不可变类型

不可变类型(int、str、tuple)的值在创建后不能改变,所以不能在__init__里修改。必须在__new__阶段定制:

class PositiveInt(int):
    def __new__(cls, value):
        if value < 0:
            raise ValueError("Value must be positive")
        return super().__new__(cls, value)

p = PositiveInt(10)
print(p)  # 10

# p = PositiveInt(-5)  # ValueError: Value must be positive
1
2
3
4
5
6
7
8
9
10

int.__new__需要接收值作为参数,所以我们传递value。创建后,这个对象的值就固定了,__init__无法修改。

同样的,自定义tuple子类时,也要在__new__里定制:

class NamedPoint(tuple):
    def __new__(cls, x, y):
        return super().__new__(cls, (x, y))
    
    @property
    def x(self):
        return self[0]
    
    @property
    def y(self):
        return self[1]

point = NamedPoint(3, 4)
print(point)    # (3, 4)
print(point.x)  # 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# __new__返回其他类型会怎样

如果__new__返回的不是当前类的实例,__init__不会被调用:

class A:
    def __new__(cls):
        print("A.__new__")
        return "not an A instance"
    
    def __init__(self):
        print("A.__init__")

obj = A()
print(obj)       # not an A instance
print(type(obj)) # <class 'str'>
# 注意:A.__init__没有被调用
1
2
3
4
5
6
7
8
9
10
11
12

# 知识拓展

Q1:为什么大多数时候只重写__init__?

因为object.__new__已经够用了,它会创建一个空对象。我们只需要在__init__里添加属性。只有需要控制对象创建过程时(单例、不可变类型、工厂模式),才需要重写__new__。

Q2:__new__是类方法还是静态方法?

它是个特殊方法,行为像类方法(第一个参数是cls),但不需要@classmethod装饰器。Python解释器会自动把类传给__new__。

Q3:能在__init__里返回一个值吗?

不能。__init__返回除None以外的值会报TypeError:

class Test:
    def __init__(self):
        return 42  # TypeError: __init__() should return None
1
2
3

Q4:super().__new__(cls)为什么要传cls?

因为object.__new__是个静态方法风格的特殊方法,需要知道创建哪个类的实例。传入cls,它会返回cls类型的对象。

Q5:多继承时__new__和__init__怎么调用?

按照MRO(方法解析顺序)调用。如果多个父类都重写了__new__或__init__,Python会按C3线性化的顺序调用。使用super()可以确保按MRO调用下一个类的方法。


上一篇:实例方法、类方法和静态方法有什么区别?
下一篇:Python如何实现封装、继承和多态?

← 实例方法、类方法和静态方法有什么区别? Python如何实现封装、继承和多态? →

评论

验证登录状态...

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.