python中init()方法和new()方法的理解与区别

本文深入解析Python中的__new__和__init__方法,探讨它们在类实例化过程中的角色和工作原理,以及如何利用这些方法实现自定义实例化流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

__new()__理解:

class A(object):
    def __init__(self,*args,**kwargs):
        print "init &&&&  %s" % self.__class__
    def __new__(cls,*args,**kwargs):
        print "new &&&&  %s" % cls
        return object.__new__(cls,*args,**kwargs)
a=A()
输出结果为:
new &&&&  <class '__main__.A'>
init &&&&  <class '__main__.A'>

如果把最后一行的return代码屏蔽掉,输出结果为:
new &&&&<class '__main__.A'>

new()是在新式类中新出现的方法,它作用在构造方法init()建造实例之前,可以这么理解,在Python 中存在于类里面的构造方法init()负责将类的实例化,而在init()调用之前,new()决定是否要使用该init()方法,因为new()可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。

new()方法的特性:
new()方法是在类准备将自身实例化时调用。
new()方法始终都是类的静态方法,即使没有被加上静态方法装饰器。

Python首先调用new()方法:

第一个参数cls是当前正在实例化的类。

def __new__(cls, *args, **kwargs):
   ...

如果要得到当前类的实例,应当在当前类中的new()方法语句中调用当前类的父类 的new()方法。
例如,如果当前类是直接继承自object,那当前类的new()方法返回的对象应该为:

def __new__(cls, *args, **kwargs):
   ...
   return object.__new__(cls)

事实上如果(新式)类中没有重写new()方法,即在定义新式类时没有重新定义new()时 ,Python默认是调用该类的直接父类的new()方法来构造该类的实例,如果该类的父类也没有重写 new(),那么将一直按此规矩追溯至object的new()方法,因为object是所有新式类的基类。
而如果新式类中重写了new()方法,那么你可以自由选择任意一个的其他的新式类(必定要是 新式类,只有新式类必定都有new(),因为所有新式类都是object的后代,而经典类则没有new() 方法)的new()方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死 循环。具体看以下代码解释:

class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)    

# 以上return等同于 
# return object.__new__(Foo, *args, **kwargs)

class Child(Foo):
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)

如果Child中没有定义new()方法,那么会自动调用其父类的new()方法来制造实例,即

Foo.__new__(cls, *args, **kwargs)

使用object或者没有血缘关系的新式类的new()是安全的,但是如果是在有继承关系的两个类之间,应避免互调造成死循环,例如:(Foo)return Child.new(cls), (Child)return Foo.new(cls)。

关于”new”方法还有一个重要的用途就是用来派生不可变类型 。

例如,python中的float类型是一个不可变类型,如果想要从float中派生出一个子类,就可以使用”new”方法:

class Round2Float(float):
    def __new__(cls,num):
        num = round(num,2)
        obj = float.__new__(Round2Float,num)
        return obj

f=Round2Float(4.324599)
print f

__init__理解:

new”决定是否要使用该类的”init”方法,因为”new” 可以调用其他类的构造方法或者直接返回别的类创建的对象来作为本类的实例。
通常来说,新式类开始实例化时,”new”方法会返回cls(cls指代当前类)的实例,然后调用该类的”init”方法作为初始化方法,该方法接收这个实例(即self)作为自己的第一个参数,然后依次传入”new”方法中接收的位置参数和命名参数。
但是,如果”new”没有返回cls(即当前类)的实例,那么当前类的”init”方法是不会被调用的。例子:

class A(object):
    def __init__(self,*args,**kwargs):
        print "calling __init__ from %s" % self.__class__

    def __new__(cls,*args,**kwargs):
        obj = object.__new__(cls,*args,**kwargs)
        print "calling __new__ from %s" % obj.__class__
        return obj

class B(A):
    def __init__(self,*args,**kwargs):
        print "calling __init__ from %s" % self.__class__
    def __new__(cls,*args,**kwargs):
        obj = object.__new__(A,*args,**kwargs)
        print "calling __new__ from %s" % obj.__class__
        return obj

b=B()
print type(b)
结果是:
calling __new__ from <class '__main__.A'>
<class '__main__.A'>

代码中,在B的”new”方法中,通过”obj = object.new(A, *args, kwargs)”创建了一个A的实例,在这种情况下,B的”init”函数就不会被调用到。

区别:

对于”new”和”init”可以概括为:
new”方法在Python中是真正的构造方法(创建并返回实例),通过这个方法可以产生一个”cls”对应的实例对象,所以说”new”方法一定要有返回。
对于”init”方法,是一个初始化的方法,”self”代表由类产生出来的实例对象,”init”将对这个对象进行相应的初始化操作。

<think>嗯,用户现在明确想了解Python中__init__方法普通实例方法区别,而且特别强调要"详解"。从用户引用的资料来看,ta可能已经看过一些关于__new____init__的文章,现在需要更聚焦在实例方法层面的对比。用户引用的4个资料里,引用14提到__new____init__的区别,引用2展示了执行顺序,引用3关于__init__.py反而当前问题无关。不过用户明确表示"不是真正的需求",说明ta很清醒地过滤了无关信息,这种聚焦问题的能力值得赞赏。深层来看,用户可能需要的不只是语法差异,而是设计哲学层面的理解。比如为什么要把初始化普通方法分开?这涉及到Python的对象模型设计。ta可能在实际编码时遇到过把业务逻辑误写在__init__里的情况。在技术要点上需要分三个层次讲透:第一层是调用时机(底层机制),必须强调__init__在实例创建后自动调用,而普通方法需显式调用。引用2的代码示例特别适合说明这点。第二层是职责分离(设计原则),用"建房子"的比喻应该能让用户印象深刻——__init__相当于毛坯房交付,普通方法是业主装修。第三层是实用场景差异,比如__init__常见于属性初始化,普通方法实现业务逻辑。需要明确告知什么时候该用哪种,这也是用户最容易困惑的实操点。要注意纠正一个潜在误区:有些初学者会把__init__当成构造函数,但其实它只是初始化已存在的对象。这个点虽然在问题里没直接问,但结合引用4的内容,应该前置说明以免理解偏差。最后规划用表格对比收尾很必要,用户提到"详解",意味着ta需要结构化信息。表格需要突出三点核心差异:触发方式、参数传递、功能约束。案例部分要展示错误写法场景,比如在__init__里放文件读写操作的反例,这正是多数人踩坑的地方。</think> 好的,我们来深入详解Python中特殊方法 `__init__` 普通实例方法区别。 **核心区别:它们的设计目的、触发时机作用完全不同。** --- ## 1. `__init__` 方法 (构造器 - 初始化方法) * **目的:** 初始化一个新创建的对象实例,为其设置初始状态(主要是属性)。 * **触发时机:** 自动调用。在对象实例通过 `类名()` 方式创建后,**内存分配完成**(由 `__new__` 完成),**立即且自动**被Python解释器调用。你不能像调用普通方法那样显式调用 `__init__` 来创建对象(虽然技术上可以 `obj.__init__(...)`,但这极其不推荐且不用于创建对象)。它是一个特殊的方法钩子(Hook)。 * **职责:** * 为实例属性赋值初始值。 * 接收参数,并用这些参数来设置对象状态(通过 `self.attribute = value`)。 * **不负责创建对象本身**,对象是由 `__new__` 方法负责在内存中创建的。`__init__` 只负责给这个已经存在的“空壳”对象添加上初始的属性值。 * 方法签名通常是:`def __init__(self, [arg1, arg2, ...]):` * **返回值:** **不应该返回任何值** (或者说显式返回 `None`)。其返回值会被Python解释器忽略。它的目的就是修改 `self` 本身的状态。 * **类比:** 想象你订购了一个家具(对象实例)。送货上门的是工厂组装好的基础结构(由 `__new__` 创建)。`__init__` 就像一个开箱仪式,你做的第一件事是根据说明书(参数)把垫脚装上、把手拧紧、调整好初始高度(设置属性)—— 进行必要的初始设置使其处于可用状态。 --- ## 2. 普通实例方法 * **目的:** 定义对象实例在生命周期中可以执行的操作或行为。它们封装了该对象相关的逻辑功能。 * **触发时机:** **必须手动显式调用**。你需要通过一个已经存在的对象实例来调用它们,语法为 `obj.method_name([arg1, ...])`。执行对象创建(即调用 `__init__`)是创建实例的行为,这个行为*完成之后*,你才能创建出一个对象实例 `obj`,然后你才能用这个 `obj` 去调用它的普通实例方法。 * **职责:** * 执行特定的任务,例如计算、修改对象状态、其他对象交互、返回信息等。 * 方法签名通常是:`def method_name(self, [arg1, arg2, ...]):` * 需要显式通过 `self` 参数访问或修改实例属性。 * **返回值:** 可以返回任何类型的值(`None`、整数、字符串、其他对象等),也可以不返回(等同于返回 `None`)。由方法逻辑决定。 * **类比:** 继续家具的例子。开箱初始化 (`__init__`) 完成后,这张桌子 `obj` 就有了它的“身高”。然后你可以: * `obj.raise_height()` (普通方法):调用这个方法*升高*桌子。 * `obj.lower_height()` (普通方法):调用这个方法*降低*桌子。 * `obj.calculate_volume()` (普通方法):调用这个方法*计算*桌子的容量。 这些调用都需要你自己主动去操作(显式调用)。 --- ## 核心差异总结表 | 特征 | `__init__` 方法 | 普通实例方法 | | :----------- | :--------------------------------------------------- | :----------------------------------------------- | | **名称** | 特殊方法名:`__init__` | 用户自定义名称 | | **触发方式** | **自动调用** (对象创建时,仅**一次**) | **手动显式调用** (对象创建后,可**无限次**) | | **目的** | **初始化**对象实例 (**设置初始属性**) | **定义行为** (**执行操作、计算、修改状态**等) | | **调用时机** | 在`__new__`创建完对象实例后**立即自动执行**,是对象创建流程的一部分 | 在对象实例**已存在**的**任何后续时间点**手动调用 | | **参数 `self`** | **必须**有,指向**刚创建的新实例** | **必须**有,指向**调用该方法的实例** | | **返回值** | **必须为 `None`** / 不返回值 (被忽略) | **可有可无**,可返回任何类型或 `None` | | **类比** | 新家具**开箱后的初始组装** | 家具**使用过程中的调节操作** | --- ## 示例代码详解 ```python class Person: # 1. __init__ 方法 (自动调用) def __init__(self, name, age): # 目的:初始化属性 print("__init__ 被自动调用:正在初始化新对象") self.name = name # 为新对象设置 name 属性 self.age = age # 为新对象设置 age 属性 # 不要 return <something> # 2. 普通实例方法 (需要显式调用) def introduce(self): # 目的:定义行为/功能 print(f"大家好,我叫{self.name},今年{self.age}岁。") # 操作:访问属性并打印信息 # 3. 另一个普通实例方法 (显式调用,修改状态) def birthday(self): print(f"过生日啦!") self.age += 1 # 操作:修改 age 属性 # 创建对象:自动触发 __init__ p1 = Person("小明", 20) # 输出: "__init__ 被自动调用:正在初始化新对象" # __init__ 自动执行完毕,p1 对象现在拥有 name="小明", age=20 # 操作对象:显式调用普通方法 p1.introduce() # 输出: "大家好,我叫小明,今年20岁。" (显式调用) p1.birthday() # 输出: "过生日啦!" (显式调用),并修改 p1.age 为 21 p1.introduce() # 输出: "大家好,我叫小明,今年21岁。" (再次显式调用) # 尝试错误用法:不能通过类名直接调用实例方法 (或__init__) 而不创建实例 # Person.introduce() # TypeError: introduce() missing 1 required positional argument: 'self' # Person.__init__() # TypeError: __init__() missing 2 required positional arguments: 'name' and 'age' ``` **关键说明:** 1. `p1 = Person("小明", 20)` 这句代码完成了: * 调用 `__new__` (默认继承自 `object`) 在内存中创建了 `Person` 类型的一个新实例 `p1`。 * **自动调用** `__init__(self, "小明", 20)`,将参数 `"小明"` `20` 分别赋给 `p1.name` `p1.age`。此时 `__init__` 结束。 2. `p1.introduce()` `p1.birthday()` 是我们显式地去调用对象 `p1` 的实例方法。它们负责 `p1` 这个已经存在的对象的具体行为状态变更(如打印信息、增加年龄)。 --- ## 总结要点 1. **`__init__` 是对象创建流程的终点(初始化),普通方法是对象使用过程的工具(行为)。** 2. **调用方式是最本质的区别:`__init__` 自动触发,一次有效;普通方法必须显式调用,随心所欲。** 3. **目的不同:`__init__` 为 *状态奠基* (设置属性),普通方法为 *行为实现* (执行逻辑)。** 4. **`__init__` 无(有意义)返回值,普通方法自由决定。** 5. 两者都需要 `self` 参数来操作实例本身,但它们的 `self` 指向的都是同一个对象实例。 6. 使用错误会带来问题:把应放在普通方法里的逻辑(如耗时的计算、文件操作、网络请求等)放在 `__init__` 里,会导致创建对象变慢且灵活性降低;尝试像普通方法一样调用 `__init__` 则破坏了对象的创建机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值