Python 中 __new__的實現(xiàn)示例
__new__ 是 Python 中最底層的對象創(chuàng)建機制,它比 __init__ 更早被調(diào)用,真正負(fù)責(zé)分配并返回對象實例。理解它是掌握元類、單例、不可變類型定制等高級特性的基礎(chǔ)。
一、__new__vs__init__的本質(zhì)區(qū)別
class Foo:
def __new__(cls, *args, **kwargs):
print(f"[__new__] 分配內(nèi)存,cls={cls}")
instance = super().__new__(cls) # 創(chuàng)建實例
return instance # 必須 return!
def __init__(self, value):
print(f"[__init__] 初始化,self={self}")
self.value = value
obj = Foo(42)
# [__new__] 分配內(nèi)存,cls=<class 'Foo'>
# [__init__] 初始化,self=<__main__.Foo object>
關(guān)鍵區(qū)別:
| __new__ | __init__ | |
|---|---|---|
| 調(diào)用時機 | 實例創(chuàng)建之前 | 實例創(chuàng)建之后 |
| 第一個參數(shù) | cls(類本身) | self(已有實例) |
| 返回值 | 必須返回實例 | 必須返回 None |
| 職責(zé) | 分配空間,構(gòu)造對象 | 初始化屬性 |
__init__ 只有在 __new__ 返回了 cls 的實例時才會被調(diào)用。若返回其他類型,__init__ 會被跳過。
二、__new__的簽名與調(diào)用鏈
object.__new__(cls[, *args, **kwargs])
Python 內(nèi)部執(zhí)行 Foo(42) 的等價邏輯是:
# type.__call__ 的內(nèi)部行為(偽代碼)
def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls, *args, **kwargs)
if isinstance(obj, cls): # 只有返回 cls 的實例才調(diào) __init__
obj.__init__(*args, **kwargs)
return obj
下面是完整的對象創(chuàng)建生命周期:—
三、最重要的使用場景
1. 單例模式(Singleton)
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance # 始終返回同一個實例
a = Singleton()
b = Singleton()
print(a is b) # True
2. 定制不可變類型
str、int、tuple 等不可變類型必須在 __new__ 中完成初始化,因為一旦創(chuàng)建就無法修改:
class UpperStr(str):
def __new__(cls, value):
return super().__new__(cls, value.upper())
# ?? 不能在 __init__ 里改,str 是不可變的
s = UpperStr("hello")
print(s) # HELLO
print(type(s)) # <class '__main__.UpperStr'>
3. 工廠模式 —— 根據(jù)參數(shù)返回不同類型
class Shape:
def __new__(cls, kind, *args):
subclasses = {"circle": Circle, "rect": Rect}
target_cls = subclasses.get(kind, cls)
return super().__new__(target_cls)
class Circle(Shape): pass
class Rect(Shape): pass
s = Shape("circle")
print(type(s)) # <class '__main__.Circle'>
4. 元類(Metaclass)中的__new__
元類的 __new__ 控制類本身的創(chuàng)建(不是實例的創(chuàng)建):
class Meta(type):
def __new__(mcs, name, bases, namespace):
# 在類定義時,自動把所有方法名轉(zhuǎn)大寫
new_ns = {k.upper() if not k.startswith('_') else k: v
for k, v in namespace.items()}
return super().__new__(mcs, name, bases, new_ns)
class Foo(metaclass=Meta):
def hello(self): return "hi"
Foo().HELLO() # "hi" — 方法被重命名了
四、常見陷阱
陷阱1:忘記 return
class Bad:
def __new__(cls):
super().__new__(cls) # 沒有 return!→ 返回 None
# __init__ 不會被調(diào)用,obj 是 None
陷阱2:__init__ 的參數(shù)必須與 __new__ 一致
# Python 會同時把參數(shù)傳給 __new__ 和 __init__
# 若簽名不匹配會報 TypeError
class Ok:
def __new__(cls, x, y): # 接受 x, y
return super().__new__(cls)
def __init__(self, x, y): # 同樣接受 x, y ?
self.x, self.y = x, y
陷阱3:super().__new__ 的參數(shù)
- Python 3.3+ 后,
object.__new__和object.__init__中有一個接受了額外參數(shù),另一個就會忽略多余參數(shù)(寬容機制) - 但自定義鏈中不要依賴這一行為,始終顯式傳遞參數(shù)
五、__new__與__init_subclass__、__class_getitem__的關(guān)系
| 鉤子 | 觸發(fā)時機 | 典型用途 |
|---|---|---|
| __new__ | 每次實例化時 | 控制實例創(chuàng)建 |
| __init_subclass__ | 定義子類時 | 自動注冊子類 |
| __class_getitem__ | Foo[int] 語法 | 泛型支持 |
| 元類 __new__ | 定義類本身時 | 修改類結(jié)構(gòu) |
總結(jié)
__new__ 的核心價值在于:在對象真正存在之前介入。當(dāng)你需要控制的不是"對象初始化后的狀態(tài)",而是"對象是哪個、是否創(chuàng)建、是什么類型"時,就該用 __new__。日常開發(fā)中最實用的三個場景是:單例、不可變類型繼承、工廠方法。
到此這篇關(guān)于Python 中 __new__的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)Python __new__內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python面向?qū)ο笾衉_new__和__init__區(qū)別
- python中__new__函數(shù)的具體使用
- Python中的?__init__、__new__?和?__call__示例詳解
- Python中的魔術(shù)方法__new__詳解
- Python中__new__()方法適應(yīng)及注意事項詳解
- python中__new__和__init__的實現(xiàn)
- 詳解Python中__new__和__init__的區(qū)別與聯(lián)系
- python __init__與 __new__的區(qū)別
- 詳解Python中__new__方法的作用
- Python 中類的構(gòu)造方法 __New__的妙用
相關(guān)文章
Pandas 合并多個Dataframe(merge,concat)的方法
今天小編就為大家分享一篇Pandas 合并多個Dataframe(merge,concat)的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06

