文章目錄 Python類中魔術方法(Magic Methods)完全指南:從入門到精通 一、魔術方法基礎 二、常用魔術方法分類詳解 1. 對象創建與初始化 2. 對象表示與字符串轉換 3. 比較運算符重載 4. 算術運算符重載 5. 容器類型模擬 6. 上下文管理器 7. 可調用對象 三、高級魔術方法 1. 屬性訪問控制 2. 描述符協議 3. 數值類型轉換 四、魔術方法最佳實踐 五、綜合案例:自定義分數類
Python類中魔術方法(Magic Methods)完全指南:從入門到精通
本文全面介紹了Python中特殊的魔術方法,這些以雙下劃線開頭和結尾的方法(如__init__)為類提供了"魔法"般的行為。主要內容包括: 基礎知識:魔術方法由Python自動調用,用于實現各種內置操作,如對象初始化(init )、字符串表示(str , repr )等。 核心分類: 對象生命周期方法(new , del ) 比較運算符(eq , __lt__等) 算術運算(add , __mul__等) 容器模擬(len , __getitem__等) 實際應用:通過豐富的代碼示例展示了如何利用魔術方法實現自定義類的高級行為,如向量運算、購物車容器等。 魔術方法使Python的面向對象編程更加強大和靈活,是構建專業級Python類的關鍵工具。
魔術方法(Magic Methods)是Python面向對象編程中的特殊方法,它們賦予類"魔法"般的行為。本文將全面解析Python中的魔術方法,通過豐富的示例和實際應用場景,帶你深入理解這一重要概念。
一、魔術方法基礎
1. 什么是魔術方法?
魔術方法是以雙下劃線開頭和結尾的特殊方法(如__init__
),Python會在特定時機自動調用它們。它們不是用來直接調用的,而是讓類能夠支持Python的各種內置操作。
class MyClass : def __init__ ( self, value) : self. value = valuedef __str__ ( self) : return f"MyClass with value: { self. value} " obj = MyClass( 42 )
print ( obj)
2. 魔術方法的特點
命名規則 :雙下劃線開頭和結尾,如__method__
自動調用 :由Python解釋器在特定情況下調用豐富功能 :實現運算符重載、對象生命周期控制等性能優化 :比普通方法調用更快(直接由解釋器處理)
二、常用魔術方法分類詳解
1. 對象創建與初始化
方法 調用時機 典型用途 __new__
創建實例時 控制實例創建過程(單例模式等) __init__
初始化實例時 設置初始屬性 __del__
對象銷毀時 清理資源
class Resource : def __new__ ( cls, * args, ** kwargs) : print ( "__new__ called - creating instance" ) instance = super ( ) . __new__( cls) return instancedef __init__ ( self, name) : print ( "__init__ called - initializing" ) self. name = namedef __del__ ( self) : print ( f"__del__ called - cleaning up { self. name} " ) res = Resource( "File" )
del res
2. 對象表示與字符串轉換
方法 調用時機 區別 __str__
str(obj)
, print(obj)
用戶友好的字符串表示 __repr__
repr(obj)
, 交互式環境明確的、可eval的表示 __format__
format(obj)
, f-string自定義格式化輸出
class Point : def __init__ ( self, x, y) : self. x = xself. y = ydef __str__ ( self) : return f"( { self. x} , { self. y} )" def __repr__ ( self) : return f"Point( { self. x} , { self. y} )" def __format__ ( self, format_spec) : if format_spec == 'r' : return f" { self. x} × { self. y} " return str ( self) p = Point( 3 , 4 )
print ( str ( p) )
print ( repr ( p) )
print ( f" { p} " )
print ( f" { p: r } " )
3. 比較運算符重載
方法 對應運算符 __lt__
<
__le__
<=
__eq__
==
__ne__
!=
__gt__
>
__ge__
>=
class Student : def __init__ ( self, name, score) : self. name = nameself. score = scoredef __eq__ ( self, other) : return self. score == other. scoredef __lt__ ( self, other) : return self. score < other. scoredef __le__ ( self, other) : return self. score <= other. scorealice = Student( "Alice" , 85 )
bob = Student( "Bob" , 90 )
print ( alice < bob)
print ( alice == bob)
4. 算術運算符重載
方法 對應運算符 反向方法 __add__
+
__radd__
__sub__
-
__rsub__
__mul__
*
__rmul__
__truediv__
/
__rtruediv__
__floordiv__
//
__rfloordiv__
__mod__
%
__rmod__
__pow__
**
__rpow__
class Vector : def __init__ ( self, x, y) : self. x = xself. y = ydef __add__ ( self, other) : return Vector( self. x + other. x, self. y + other. y) def __mul__ ( self, scalar) : if isinstance ( scalar, ( int , float ) ) : return Vector( self. x * scalar, self. y * scalar) return NotImplementeddef __rmul__ ( self, scalar) : return self. __mul__( scalar) def __str__ ( self) : return f"Vector( { self. x} , { self. y} )" v1 = Vector( 2 , 3 )
v2 = Vector( 5 , 7 )
print ( v1 + v2)
print ( v1 * 3 )
print ( 2 * v1)
5. 容器類型模擬
方法 用途 __len__
len(obj)
__getitem__
obj[key]
__setitem__
obj[key] = value
__delitem__
del obj[key]
__contains__
item in obj
__iter__
迭代對象時
class ShoppingCart : def __init__ ( self) : self. items = [ ] def __len__ ( self) : return len ( self. items) def __getitem__ ( self, index) : return self. items[ index] def __setitem__ ( self, index, value) : self. items[ index] = valuedef __delitem__ ( self, index) : del self. items[ index] def __contains__ ( self, item) : return item in self. itemsdef __iter__ ( self) : return iter ( self. items) def add ( self, item) : self. items. append( item) cart = ShoppingCart( )
cart. add( "蘋果" )
cart. add( "香蕉" )
cart. add( "橙子" ) print ( len ( cart) )
print ( cart[ 1 ] )
print ( "蘋果" in cart) for item in cart: print ( item)
6. 上下文管理器
方法 調用時機 __enter__
進入with
塊時 __exit__
退出with
塊時
class Timer : def __enter__ ( self) : import timeself. start = time. time( ) return selfdef __exit__ ( self, exc_type, exc_val, exc_tb) : import timeself. end = time. time( ) print ( f"耗時: { self. end - self. start: .2f } 秒" ) def elapsed ( self) : return self. end - self. startwith Timer( ) as t: sum ( range ( 1000000 ) )
7. 可調用對象
class Adder : def __init__ ( self, n) : self. n = ndef __call__ ( self, x) : return self. n + xadd5 = Adder( 5 )
print ( add5( 3 ) )
三、高級魔術方法
1. 屬性訪問控制
方法 調用時機 __getattr__
訪問不存在的屬性時 __getattribute__
訪問任何屬性時 __setattr__
設置屬性時 __delattr__
刪除屬性時
class AttributeLogger : def __init__ ( self) : self. data = { } def __getattr__ ( self, name) : print ( f"訪問不存在的屬性: { name} " ) return None def __setattr__ ( self, name, value) : print ( f"設置屬性: { name} = { value} " ) super ( ) . __setattr__( name, value) def __delattr__ ( self, name) : print ( f"刪除屬性: { name} " ) super ( ) . __delattr__( name) obj = AttributeLogger( )
obj. x = 10
print ( obj. x)
print ( obj. y)
del obj. x
2. 描述符協議
方法 調用時機 __get__
獲取描述符值時 __set__
設置描述符值時 __delete__
刪除描述符值時
class Celsius : def __get__ ( self, instance, owner) : return instance. _celsiusdef __set__ ( self, instance, value) : if value < - 273.15 : raise ValueError( "溫度不能低于絕對零度" ) instance. _celsius = valueclass Temperature : celsius = Celsius( ) def __init__ ( self, celsius) : self. celsius = celsius temp = Temperature( 25 )
print ( temp. celsius)
temp. celsius = 30
3. 數值類型轉換
方法 調用時機 __int__
int(obj)
__float__
float(obj)
__bool__
bool(obj)
__complex__
complex(obj)
class Percentage : def __init__ ( self, value) : self. value = valuedef __int__ ( self) : return int ( self. value) def __float__ ( self) : return float ( self. value / 100 ) def __bool__ ( self) : return self. value != 0 def __str__ ( self) : return f" { self. value} %" p = Percentage( 75 )
print ( int ( p) )
print ( float ( p) )
print ( bool ( p) )
print ( bool ( Percentage( 0 ) ) )
四、魔術方法最佳實踐
謹慎使用 :只在確實需要時實現魔術方法保持一致性 : 實現__eq__
時也應實現__hash__
實現比較運算符時最好實現全套 性能考慮 :魔術方法會被頻繁調用,應保持高效文檔說明 :明確記錄每個魔術方法的行為避免過度使用 :不是所有類都需要成為"全能選手"
五、綜合案例:自定義分數類
class Fraction : """自定義分數類,演示多種魔術方法""" def __init__ ( self, numerator, denominator= 1 ) : if denominator == 0 : raise ValueError( "分母不能為零" ) common = self. gcd( numerator, denominator) self. num = numerator // commonself. den = denominator // common@staticmethod def gcd ( a, b) : """計算最大公約數""" while b: a, b = b, a % breturn adef __add__ ( self, other) : """重載+運算符""" if isinstance ( other, int ) : other = Fraction( other) new_num = self. num * other. den + other. num * self. dennew_den = self. den * other. denreturn Fraction( new_num, new_den) __radd__ = __add__ def __sub__ ( self, other) : """重載-運算符""" return self. __add__( - other) def __neg__ ( self) : """重載負號""" return Fraction( - self. num, self. den) def __mul__ ( self, other) : """重載*運算符""" if isinstance ( other, int ) : other = Fraction( other) return Fraction( self. num * other. num, self. den * other. den) __rmul__ = __mul__ def __truediv__ ( self, other) : """重載/運算符""" if isinstance ( other, int ) : other = Fraction( other) return Fraction( self. num * other. den, self. den * other. num) def __eq__ ( self, other) : """重載==運算符""" if isinstance ( other, int ) : other = Fraction( other) return self. num == other. num and self. den == other. dendef __lt__ ( self, other) : """重載<運算符""" return self. num * other. den < other. num * self. dendef __le__ ( self, other) : """重載<=運算符""" return self. __lt__( other) or self. __eq__( other) def __str__ ( self) : """字符串表示""" if self. den == 1 : return str ( self. num) return f" { self. num} / { self. den} " def __repr__ ( self) : """解釋器表示""" return f"Fraction( { self. num} , { self. den} )" def __float__ ( self) : """轉換為浮點數""" return self. num / self. den
f1 = Fraction( 3 , 4 )
f2 = Fraction( 2 , 5 ) print ( f1 + f2)
print ( f1 - f2)
print ( f1 * f2)
print ( f1 / f2)
print ( f1 == Fraction( 6 , 8 ) )
print ( f1 < f2)
print ( float ( f1) )
print ( 2 + f1)
通過這個完整的分數類實現,我們綜合運用了多種魔術方法,使自定義類能夠像內置類型一樣自然地參與各種運算和操作。
魔術方法是Python強大而靈活的特性,合理使用它們可以讓你的類更加Pythonic,與Python語言的其他特性無縫集成。記住,能力越大責任越大,魔術方法應該用來增強代碼的清晰度和可用性,而不是制造"魔法"般的復雜性。