0

0

面向对象编程:__new__ 和 __init__ 方法的区别

幻影之瞳

幻影之瞳

发布时间:2025-09-06 15:46:30

|

854人浏览过

|

来源于php中文网

原创

new 方法的核心角色是创建并返回类的实例,控制对象的创建过程。它在实例化时先于 init 被调用,负责内存分配与实例生成,决定对象的类型,可实现单例、不可变对象等高级模式。

面向对象编程:__new__ 和 __init__ 方法的区别

在Python的面向对象编程中,

__new__
__init__
方法是对象生命周期中两个至关重要的阶段,它们的核心区别在于:
__new__
负责“创造”一个实例,也就是在内存中分配空间并返回这个新的对象;而
__init__
则负责“初始化”这个已经创建好的实例,为它的属性设置初始值。简单来说,
__new__
是造物主,
__init__
是装修师。

面向对象编程中,当我们通过

ClassName()
这样的方式来实例化一个对象时,背后其实经历了一个两阶段的过程。首先,Python会调用类的
__new__
方法来创建一个新的对象。这个方法是一个类方法(尽管它不需要使用
@classmethod
装饰器,它接收的第一个参数是
cls
,代表当前正在被实例化的类)。它的主要职责就是分配内存,并返回一个该类的实例。如果这个方法没有返回一个该类的实例,那么
__init__
方法就不会被调用。

一旦

__new__
方法成功返回了一个新的实例,Python接下来就会调用这个实例的
__init__
方法。
__init__
方法是一个实例方法,它接收的第一个参数是
self
,即刚刚由
__new__
创建出来的那个实例。它的任务就是接收构造函数传入的参数,并用这些参数来设置
self
对象的属性,完成对象的初始化工作。

打个比方,

__new__
就像是工厂里生产出了一辆汽车的骨架和外壳,它决定了这辆车“是”一辆车。而
__init__
则是给这辆车装上座椅、发动机、方向盘,并喷漆,让它成为一辆“特定配置”的汽车。

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("--- __new__ 方法被调用 ---")
        # 通常这里会调用父类的 __new__ 方法来实际创建实例
        instance = super().__new__(cls)
        print(f"--- __new__ 创建了实例: {instance} ---")
        return instance

    def __init__(self, name):
        print("--- __init__ 方法被调用 ---")
        self.name = name
        print(f"--- __init__ 初始化了实例 {self},名称为: {self.name} ---")

# 实例化一个对象
obj = MyClass("Python")
print(f"最终对象: {obj}, 名称: {obj.name}")

从输出中可以清晰地看到

__new__
总是先于
__init__
执行。

__new__
方法在 Python 对象创建中扮演的核心角色是什么?

在我看来,

__new__
方法在Python的对象创建机制中扮演了一个“工厂负责人”的角色,它的核心职责在于控制实例的创建过程。这个方法是真正意义上决定一个对象“如何诞生”的地方。当一个类被调用来创建实例时,
__new__
是第一个被触及的特殊方法。它接收的第一个参数是
cls
,也就是当前正在被实例化的类本身,这和
__init__
接收
self
(已创建的实例)有本质区别。

__new__
的主要任务包括:

  1. 分配内存并返回实例: 它的首要工作是在内存中为新对象分配空间。通常,我们会通过
    super().__new__(cls)
    来调用父类的
    __new__
    方法来完成这个任务,这确保了对象能被正确地创建。如果
    __new__
    没有显式地返回一个实例,那么
    __init__
    就不会被调用。
  2. 决定实例的类型: 这是一个非常强大的特性。
    __new__
    方法不一定非要返回
    cls
    类型的实例。它可以返回一个完全不同类型的对象,甚至是已经存在的对象。这意味着你可以在对象创建的最初阶段就介入,改变最终实例的类型,或者返回一个缓存中的实例。
  3. 实现单例模式等高级创建模式: 正是由于
    __new__
    可以在返回实例之前进行逻辑判断,它成为了实现单例模式(Singleton)的理想场所。我们可以在
    __new__
    中检查是否已经存在一个实例,如果存在就直接返回那个实例,而不是创建新的。

说白了,如果你想在对象被真正“出生”之前做一些事情,或者想控制对象的“出生方式”,那么

__new__
就是你唯一的入口。

class Singleton:
    _instance = None # 用于存储单例实例

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            print("--- 首次创建单例实例 ---")
            cls._instance = super().__new__(cls)
        else:
            print("--- 返回已存在的单例实例 ---")
        return cls._instance

    def __init__(self, name):
        # 注意:__init__ 每次都会被调用,所以这里需要一些判断
        # 或者确保 __init__ 是幂等的
        if not hasattr(self, '_initialized'): # 避免重复初始化
            print(f"--- 初始化单例实例: {name} ---")
            self.name = name
            self._initialized = True
        else:
            print(f"--- 单例实例已初始化,跳过重复初始化: {name} ---")


s1 = Singleton("InstanceOne")
print(f"s1 的名称: {s1.name}, ID: {id(s1)}")

s2 = Singleton("InstanceTwo") # 此时 __init__ 也会被调用,但 __new__ 返回的是s1
print(f"s2 的名称: {s2.name}, ID: {id(s2)}")

s3 = Singleton("InstanceThree")
print(f"s3 的名称: {s3.name}, ID: {id(s3)}")

print(f"s1 is s2: {s1 is s2}")
print(f"s1 is s3: {s1 is s3}")

这个例子清楚地展示了

__new__
如何控制了实例的唯一性,而
__init__
即使被多次调用,也可以通过内部逻辑避免重复初始化属性。

__init__
方法如何确保新对象的属性得到正确初始化?

__init__
方法,顾名思义,是“初始化器”。它在
__new__
方法创建并返回了一个新实例之后被调用,其核心职责就是确保这个新对象的内部状态(即属性)被正确地设置和初始化。它接收的第一个参数是
self
,这个
self
就是
__new__
方法刚刚创建并返回的那个实例。

__init__
的工作流程通常包括:

乐尚团购
乐尚团购

乐尚团购系统,是一项基于PHP+MYSQL为核心开发的一套免费 + 开源专业团购系统。软件具执行效率高、模板自由切换、后台管理功能方便等诸多优秀特点。本软件是基于Web应用的B/S架构的团购网站建设解决方案的建站系统。它可以让用户高效、快速、低成本的构建个性化、专业化、强大功能的团购网站。从技术层面来看,本程序采用目前软件开发IT业界较为流行的PHP和MYSQL数据库开发技术,基于面向对象的编程,

下载
  1. 接收构造参数:
    __init__
    方法接收用户在实例化对象时传入的所有参数(除了
    self
    )。这些参数通常用于设置对象的初始状态。
  2. 设置实例属性:
    __init__
    内部,我们会使用这些参数来创建并赋值给实例的属性。例如,
    self.name = name
    self.age = age
    等。这是最常见也是最重要的用途。
  3. 执行其他初始化逻辑: 除了设置属性,
    __init__
    还可以执行任何其他必要的初始化操作,比如打开文件、建立数据库连接、调用其他方法来计算初始值等。这些操作都围绕着“让这个新创建的对象进入一个可用状态”这个目标。
  4. 调用父类初始化器: 在继承体系中,为了确保父类的属性也能被正确初始化,我们通常会在子类的
    __init__
    方法中调用
    super().__init__(*args, **kwargs)
    。这保证了继承链上的所有初始化逻辑都能被执行。

__init__
方法不应该返回任何值(它隐式返回
None
)。它的全部目的就是修改
self
对象,使其具备完整的初始状态。如果
__init__
抛出异常,那么这个对象的创建过程就会失败。

class Car:
    def __init__(self, brand, model, year, color="白色"):
        # 验证输入,确保数据有效性
        if not isinstance(brand, str) or not brand:
            raise ValueError("品牌不能为空且必须是字符串")
        if not isinstance(year, int) or year < 1900 or year > 2024:
            raise ValueError("年份必须是有效的整数")

        self.brand = brand
        self.model = model
        self.year = year
        self.color = color
        self.engine_started = False # 默认引擎未启动

        print(f"--- {self.brand} {self.model} (年份: {self.year}, 颜色: {self.color}) 已成功初始化 ---")

    def start_engine(self):
        if not self.engine_started:
            self.engine_started = True
            print(f"{self.brand} {self.model} 引擎启动!")
        else:
            print(f"{self.brand} {self.model} 引擎已在运行。")

# 正常创建对象
my_car = Car("Tesla", "Model 3", 2023, "蓝色")
my_car.start_engine()

# 尝试创建带有无效参数的对象
try:
    bad_car = Car("Ford", "Focus", 1890)
except ValueError as e:
    print(f"创建失败: {e}")

try:
    another_bad_car = Car("", "Fiesta", 2020)
except ValueError as e:
    print(f"创建失败: {e}")

通过

__init__
,我们可以确保每个
Car
对象在被创建出来时,都拥有合法的品牌、型号、年份和颜色,并且引擎处于默认的关闭状态。这极大地提高了对象的健壮性和可用性。

哪些场景下重写
__new__
方法是不可或缺的?

虽然大多数时候我们只需要关心

__init__
,但有些特定的高级场景下,重写
__new__
方法是必不可少的,甚至是唯一解决方案。这些场景通常涉及到对对象创建过程本身的深刻干预。

  1. 实现单例模式(Singleton Pattern): 这是最常见的需要重写

    __new__
    的场景之一。单例模式要求一个类在整个应用程序生命周期中只存在一个实例。通过在
    __new__
    中检查是否已经存在实例,并决定是创建新实例还是返回现有实例,可以完美地实现这一模式。正如前面示例所示,
    __new__
    能够控制“是否创建”以及“创建哪个”。

  2. 创建不可变对象(Immutable Objects): 虽然Python本身提供了元组、字符串等不可变类型,但如果你想创建自定义的不可变类,

    __new__
    可以发挥作用。在
    __new__
    中,你可以确保对象一旦被创建,其核心属性就无法更改(配合
    __setattr__
    __delattr__
    可能会更完善)。更直接的,你可以让
    __new__
    返回一个特定类型的实例,而这个类型本身就设计为不可变的。

  3. 改变实例的类型: 这是一个比较高级且不常用的技巧,但

    __new__
    允许你返回一个与
    cls
    参数不同的类的实例。这意味着你可以创建一个类的实例,但最终得到的对象却是另一个类的实例。这在某些动态类型转换或工厂模式中可能会用到,但需要非常谨慎,因为它可能会让代码变得难以理解和维护。

    class Base:
        def __new__(cls, type_name):
            if type_name == "special":
                print("--- __new__ 返回 SpecialClass 实例 ---")
                return super().__new__(SpecialClass) # 返回不同类型的实例
            else:
                print("--- __new__ 返回 Base 实例 ---")
                return super().__new__(cls)
    
        def __init__(self, type_name):
            self.type = type_name
            print(f"--- Base 或其子类实例初始化: {self.type} ---")
    
    class SpecialClass(Base):
        def __init__(self, type_name):
            super().__init__(type_name)
            self.special_attribute = "我是特殊的!"
            print(f"--- SpecialClass 额外初始化 ---")
    
    obj1 = Base("normal")
    print(f"obj1 类型: {type(obj1)}, 属性: {obj1.type}")
    
    obj2 = Base("special")
    print(f"obj2 类型: {type(obj2)}, 属性: {obj2.type}")
    if isinstance(obj2, SpecialClass):
        print(f"obj2 拥有特殊属性: {obj2.special_attribute}")

    这个例子展示了

    Base
    __new__
    方法如何根据输入参数返回
    Base
    SpecialClass
    的实例。

  4. 元类编程(Metaclass Programming): 在更深层次的Python编程中,元类(metaclass)是用来创建类的类。当一个元类被调用来创建新的类时,它的

    __new__
    方法会被调用。这允许你在类被创建时(而不是实例被创建时)介入并修改类的行为、属性或结构。这通常是框架或库开发者才会接触到的领域,对于日常应用开发来说相对较少。

总的来说,当你需要对对象的“出生”过程本身进行干预,而不是仅仅对出生后的对象进行“装修”时,

__new__
方法就是你的利器。它赋予了你更强大的控制力,但也意味着你需要更深入地理解Python的对象模型。

相关文章

编程速学教程(入门课程)
编程速学教程(入门课程)

编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

52

2025.11.27

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

52

2025.11.27

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

52

2025.11.27

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

299

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

0

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

相关下载

更多

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 22.4万人学习

Django 教程
Django 教程

共28课时 | 3.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号