Python中的__init__和__new__方法解析

Python中的__init__和__new__方法解析

在Python的面向对象编程(OOP)设计时,类中一般都有一个__init__方法,还可能看到__new__方法------__new__方法比较少见,通常没有显式定义,但其作用不小。它们有什么用呢?

__init__和__new__方法解析

在 Python 的面向对象编程中:

init 方法几乎总是会被显式定义------被显式重写,用来初始化对象的属性。

new 方法很少显式定义(默认继承自基类 object,大多数场景无需重写),但其作用不小------在 init 之前被调用,负责实际创建对象实例。

init 方法

1. 作用

  • 初始化对象:负责设置实例的初始状态(给对象的属性赋值)。
  • 不负责创建对象 :此时对象已经通过 new 被创建,self 参数就是这个已经存在的实例。

2. 特点

  • 必须返回 None :如果尝试返回其他值会触发 TypeError。若显式返回非None值(如return 1),会抛出TypeError: init() should return None。
  • 最常用的方法 :几乎每个自定义类都会重写 init

它的语法如下:

def init(self, 参数1, 参数2, ...):

初始化语句

self 参数是必须的,它代表创建的对象本身,在方法内部可以通过 self 来引用对象的属性和方法。除了 self 以外的其他参数是可选的,根据实际需求定义。

self并非关键字,只是约定俗成的第一个参数名。它实际指向实例本身,通过self可以绑定属性。

即使没有显式定义__init__,Python也会提供空实现。

new 方法

1. 作用

  • 真正创建对象:负责构造并返回类的实例(一个未被初始化的"裸对象")。
  • 控制对象创建过程:可以干预实例的创建逻辑(例如单例模式、继承不可变类型)。

2. 特点

  • 本质是类方法(相当于隐式使用 @classmethod **装饰):**第一个参数是当前类(cls),必须返回类的实例。
  • 默认继承自 object :如果不显式定义,Python 会使用基类的 new
  • 必须返回一个实例 :通常返回 super().new(cls)

new 的语法结构如下:

def new(cls, *args, **kwargs):

1. 必须调用父类的 new 方法创建实例

instance = super().new(cls)

2. 可在此处修改实例属性或添加逻辑

...

3. 必须返回实例对象

return instance

__new__方法的参数:第一个参数是类(cls,即class)------cls并非关键字,只是约定俗成的第一个参数名。*args和**kwargs为创建实例时传入的参数。

new__接收的*args和**kwargs会原封不动传递给__init,用于初始化实例属性。若__new__返回其他类的实例,则当前类的__init__方法不会被执行,因为实例不属于当前类,self 不是当前类的实例,初始化逻辑无法应用。

通过重写(Override)这两个方法(__init__和__new__方法),我们可以自定义对象的创建和初始化过程,从而实现更加灵活和强大的功能。

没有显式定义__new__()时是什么情况?在定义子类时没有重新定义__new__()时,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。

在 Python 中,super().__new__调用父类的__new__方法以创建子类实例(内存分配)。它是 new 方法中创建实例的核心逻辑,尤其在继承场景下至关重要。

__new__方法的调用是发生在__init__之前的,即初始化方法在 new 之后调用。__new__创建实例(返回未初始化的实例)→ __init__初始化实例(设置属性)。两者共同来创建和初始化一个对象的。

newinit 的差异:

方法 调用时机 功能 返回值
new 实例化第一步 创建实例(内存分配) 必须返回实例对象
init 实例化第二步 初始化实例属性 无(None)

__init__和__new__方法用法示例

init 方法用法示例

init 方法基础示例:初始化实例属性,源码如下:

python 复制代码
class Person:
    def __init__(self, name, age):
        # 绑定实例属性(通过 self 引用)
        self.name = name  # 实例变量:存储姓名
        self.age = age    # 实例变量:存储年龄

    def introduce(self):
        return f"我是 {self.name},今年 {self.age} 岁。"

# 创建实例并初始化
p = Person("Alice", 30)
print(p.introduce())  # 输出:我是 Alice,今年 30 岁。

说明:定义一个 Person 类,通过 init 初始化姓名和年龄属性。

init 在实例创建后自动调用,负责设置属性值。

self 指向已创建的实例,通过 self.属性名 绑定属性。

含有super().init()的 示例,源码如下:

python 复制代码
class Parent:
    def __init__(self):
        print("Parent.__init__ called")
        self.parent_attr = "parent"

class Child(Parent):
    def __init__(self):
        print("Child.__init__ called")
        super().__init__()  # 调用 Parent.__init__
        self.child_attr = "child"

obj = Child()
# 输出:
# Child.__init__ called
# Parent.__init__ called
print(obj.parent_attr)  # 输出:parent
print(obj.child_attr)   # 输出:child

说明:

调用父类的 init 方法:

super().init() 会向上委托父类的初始化逻辑,确保父类的属性也被正确设置。

关于__init__方法特别提示两点:

__init__方法在对象创建时自动调用,无需手动显式调用。

__init__方法中的 self.变量名=变量名,self.变量名 中的变量名(实例变量名) 和作为参数的 变量名 是两个不同的变量,两者标识符通常相同,但不必如此。保持名称一致是惯例,但技术上允许不同。例如:

python 复制代码
class Person:
    def __init__(self, param_name):
        # self.name 是实例变量,存在于整个实例的生命周期
        # param_name 是参数变量(局部变量),只在__init__方法内有效
        self.name = param_name

    def greet(self):
        return f"Hello, my name is {self.name}."

# 创建Person实例并调用方法
p = Person("Alice")
print(p.greet())  # 输出: Hello, my name is Alice.

这个例子中,__init__方法的参数名是param_name,但实例变量名是self.name。当创建Person类的实例时,传入的参数值会被赋给实例变量self.name,并且可以通过实例的方法访问这个变量。

变量生命周期:

实例变量(self.name):与实例对象同寿,可以在类的任何方法中访问

参数变量(name):仅在方法执行期间存在的局部变量

为什么通常使用相同的名字:

符合惯例

减少混淆,若名称不同,可能引发误解。

代码更直观、易读,一致的名称让代码更易理解。例如 self.name = name 明确表示"将参数 name 的值赋给实例变量 name"。

new 方法示例

new 方法通常不需要手动显式调用。在极少数情况下,你可能需要显式调用 new,但这非常罕见。例如,在实现某些元类或进行底层操作时可能会这样做(但属于高级技巧,使用需谨慎)。

new 方法基础示例:控制实例创建(返回自定义实例),源码如下:

python 复制代码
class UpperCaseString(str):
    def __new__(cls, value):
        # 1. 调用父类 __new__ 创建实例(必须返回实例)
        # 2. 在创建实例前处理输入值(转为大写)
        upper_value = value.upper()  # 不可变类型需在 __new__ 中修改值
        return super().__new__(cls, upper_value)  # 返回处理后的实例

    # __init__ 可选(此处无需额外初始化)
    def __init__(self, value):
        pass  # 不可变类型初始化时无法修改属性

# 创建实例
s = UpperCaseString("hello world")
print(s)  # 输出:HELLO WORLD(自动转为大写)

说明:定义一个 UpperCaseString 类,继承自 str,强制将输入字符串转为大写。

继承不可变类型(如 str、int)时,需通过 new 修改实例值,因为 init 无法改变已创建的不可变实例。

new 的返回值必须是当前类(cls)的实例,否则 init 不会被调用。

new 进阶示例:单例模式(确保类仅创建一个实例),源码如下:

python 复制代码
class Singleton:
    _instance = None  # 类变量,存储唯一实例

    def __new__(cls, *args, **kwargs):
        # 检查是否已创建实例
        if cls._instance is None:
            # 调用父类 __new__ 创建新实例(仅传递 cls 参数)
            cls._instance = super().__new__(cls)
        # 返回唯一实例
        return cls._instance

    def __init__(self, name):
        # 仅在首次初始化时设置属性
        if not hasattr(self, '_initialized'):
            self.name = name
            self._initialized = True  # 标记已初始化

# 测试单例
obj1 = Singleton("Instance 1")
obj2 = Singleton("Instance 2")
print(obj1 is obj2)  # 输出:True
print(obj1.name)     # 输出:Instance 1
print(obj2.name)     # 输出:Instance 1(未被"Instance 2"覆盖)

说明:使用 new 实现单例模式,保证类在全局只有一个实例。

通过类变量 _instance 记录实例,在 new 中判断是否已创建实例,避免重复创建。

init 会在首次创建实例时正常调用,但后续获取实例时不再调用(因实例已存在)。

newinit 协同示例:复杂对象初始化,源码如下:

python 复制代码
class Vector:
    def __new__(cls, x, y):
        # 1. 在 __new__ 中验证参数(创建实例前的预处理)
        if not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
            raise TypeError("坐标必须为数值类型")
        return super().__new__(cls)  # 创建实例

    def __init__(self, x, y):
        # 2. 在 __init__ 中初始化属性(实例已创建)
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector(x={self.x}, y={self.y})"

# 正常创建
v = Vector(3, 4)
print(v)  # 输出:Vector(x=3, y=4)

# 非法参数(触发 __new__ 中的验证)
# v = Vector("a", 5)  # 抛出 TypeError: 坐标必须为数值类型

说明:

定义一个 Vector 类,通过 new 验证输入参数合法性,通过 init 完成属性初始化。

new 负责实例创建前的逻辑(如参数校验、类型转换)。

init 负责实例创建后的初始化(如设置属性、建立资源连接)。

附录:

Python Data Model(数据模型)官方文档有关部分https://docs.python.org/3/reference/datamodel.html

关于 new 方法:

new() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

new() 主要用于允许不可变类型(如 int、str 或 tuple)的子类自定义实例创建过程。它也常在自定义元类中被重写,以便自定义类的创建过程。

关于 init 方法:

Called after the instance has been created (by new()), but before it is returned to the caller. The arguments are those passed to the class constructor expression.

在实例被创建之后(由 new() 创建)但在返回给调用者之前被调用。传递给它的参数是那些传递给类构造器表达式的参数。

其他相关官方文档:

https://docs.python.org/3/tutorial/classes.html

我以前的博文

Python类中self和__init__()介绍https://blog.csdn.net/cnds123/article/details/130506922

python里的super().init()有什么作用 https://blog.csdn.net/cnds123/article/details/127623653

Python面向对象程序设计讲座 https://blog.csdn.net/cnds123/article/details/108354860

Python青少年简明教程:类和对象入门 https://blog.csdn.net/cnds123/article/details/141953553

相关推荐
kkai人工智能10 分钟前
解决开发者技能差距:AI 在提升效率与技能培养中的作用
开发语言·人工智能·ai·chatgpt·媒体
赴前尘20 分钟前
Go 中 `json.NewEncoder/Decoder` 与 `json.Marshal/Unmarshal` 的区别与实践
开发语言·golang·json
꧁༺摩༒西༻꧂24 分钟前
Python生成日历导出Excel
java·前端·python
gou1234123425 分钟前
【Golang入门】第一章:环境搭建与Hello World
开发语言·后端·golang
铭....1 小时前
word批量导出visio图
开发语言·c#·word
2501_911828501 小时前
Python训练营---Day40
python·深度学习·机器学习
寻星探路1 小时前
JAVA与C语言之间的差异(一)
java·开发语言
代码的乐趣1 小时前
支持selenium的chrome driver更新到136.0.7103.113
chrome·python·selenium
1001101_QIA1 小时前
【QT】理解QT机制之“元对象系统”
开发语言·c++·qt·算法
爱上语文2 小时前
MyBatisPlus(1):快速入门
java·开发语言·数据库·后端·mybatis