在中高級Python開發(fā)面試中,面試官為了考察面試者是否了解面向?qū)ο缶幊蹋瑫ㄟ^Python魔術(shù)方法相關(guān)概念來變向提問
引言
定義和基本概念
魔術(shù)方法(Magic Methods) 是 Python 中一種特殊的函數(shù),它們用于定義類的特定行為和屬性。這些方法通常由 Python 解釋器自動調(diào)用,而不是顯式地在代碼中調(diào)用。
它們允許你自定義類與內(nèi)置操作符、函數(shù)和語法結(jié)構(gòu)的交互方式,使得自定義對象能夠像內(nèi)置類型一樣自然地使用。
命名約定
魔術(shù)方法的命名有特定的格式要求,它們都以雙下劃線__開頭和結(jié)尾。這種命名約定使得這些方法在類中具有特殊的用途,不會與普通的方法名發(fā)生沖突。例如:
學習原因
- 提升代碼可讀性和可維護性
- 實現(xiàn)類的特殊行為和高級功能
常見魔術(shù)方法
初始化、刪除、調(diào)用
-
__init__(self, ...):對象初始化方法(構(gòu)造函數(shù)) -
__new__(cls, ...):創(chuàng)建對象實例的方法,通常與__init__搭配使用 -
__del__(self):對象銷毀方法(析構(gòu)函數(shù)) -
__call__(self, ...):對象被調(diào)用時,比如xxx()這樣調(diào)用形式,就會調(diào)用__call__方法的內(nèi)容
字符串表示
-
__str__(self):定義對象的字符串表示,適用于str()和print() -
__repr__(self):定義對象的官方字符串表示,適用于repr(),通??赏ㄟ^eval()函數(shù)重建對象 -
__format__(self, format_spec):定義對象的格式化表示,適用于format()函數(shù)和格式化字符串 -
__bytes__(self):定義對象的字節(jié)串表示,適用于bytes()
數(shù)值運算
-
__neg__(self):一元負號(取負),即-self -
__pos__(self):一元正號,即+self -
__abs__(self):絕對值,即abs(self) -
__invert__(self):按位取反,即~self -
__add__(self, other):加法,即self + other -
__sub__(self, other):減法,即self - other -
__mul__(self, other):乘法,即self * other -
__matmul__(self, other):矩陣乘法,即self @ other -
__truediv__(self, other):真除法,即self / other -
__floordiv__(self, other):取整除法,即self // other -
__mod__(self, other):取模,即self % other -
__divmod__(self, other):同時計算取整除法和取模,返回(self // other, self % other) -
__pow__(self, other, modulo=None):冪運算,即self ** other -
__lshift__(self, other):左移位運算,即self << other -
__rshift__(self, other):右移位運算,即self >> other -
__and__(self, other):按位與,即self & other -
__xor__(self, other):按位異或,即self ^ other -
__or__(self, other):按位或,即self | other -
__radd__(self, other):反向加法,即other + self -
__rsub__(self, other):反向減法,即other - self -
__rmul__(self, other):反向乘法,即other * self -
__rmatmul__(self, other):反向矩陣乘法,即other @ self -
__rtruediv__(self, other):反向真除法,即other / self -
__rfloordiv__(self, other):反向取整除法,即other // self -
__rmod__(self, other):反向取模,即other % self -
__rdivmod__(self, other):反向同時計算取整除法和取模,返回(other // self, other % self) -
__rpow__(self, other, modulo=None):反向冪運算,即other ** self -
__rlshift__(self, other):反向左移位運算,即other << self -
__rrshift__(self, other):反向右移位運算,即other >> self -
__rand__(self, other):反向按位與,即other & self -
__rxor__(self, other):反向按位異或,即other ^ self -
__ror__(self, other):反向按位或,即other | self -
__iadd__(self, other):增量加法賦值,即self += other -
__isub__(self, other):增量減法賦值,即self -= other -
__imul__(self, other):增量乘法賦值,即self *= other -
__imatmul__(self, other):增量矩陣乘法賦值,即self @= other -
__itruediv__(self, other):增量真除法賦值,即self /= other -
__ifloordiv__(self, other):增量取整除法賦值,即self //= other -
__imod__(self, other):增量取模賦值,即self %= other -
__ipow__(self, other, modulo=None):增量冪運算賦值,即self **= other -
__ilshift__(self, other):增量左移位賦值,即self <<= other -
__irshift__(self, other):增量右移位賦值,即self >>= other -
__iand__(self, other):增量按位與賦值,即self &= other -
__ixor__(self, other):增量按位異或賦值,即self ^= other -
__ior__(self, other):增量按位或賦值,即self |= other
類型轉(zhuǎn)換
-
__int__(self):定義對象到整數(shù)的轉(zhuǎn)換,適用于int() -
__float__(self):定義對象到浮點數(shù)的轉(zhuǎn)換,適用于float() -
__complex__(self):定義對象到復數(shù)的轉(zhuǎn)換,適用于complex() -
__bool__(self):定義對象的布爾值轉(zhuǎn)換,適用于bool() -
__index__(self):定義對象作為索引的轉(zhuǎn)換,適用于切片操作和bin()、hex()、oct()函數(shù)
集合操作
-
__len__(self):定義對象的長度,適用于len() -
__getitem__(self, key):定義按鍵訪問,適用于obj[key] -
__setitem__(self, key, value):定義按鍵賦值,適用于obj[key] = value -
__delitem__(self, key):定義按鍵刪除,適用于del obj[key] -
__contains__(self, item):定義成員測試,適用于item in obj
迭代協(xié)議
-
__iter__(self):定義返回迭代器,適用于iter() -
__next__(self):定義返回下一個元素,適用于next()
上下文管理
-
__enter__(self):定義進入上下文管理時的行為,適用于with語句 -
__exit__(self, exc_type, exc_value, traceback):定義退出上下文管理時的行為,適用于with語句
屬性訪問
-
__getattr__(self, name):定義訪問不存在的屬性時的行為 -
__getattribute__(self, name):定義訪問任何屬性時的行為 -
__setattr__(self, name, value):定義設(shè)置屬性時的行為 -
__delattr__(self, name):定義刪除屬性時的行為
描述符協(xié)議
-
__get__(self, instance, owner):定義描述符的獲取行為 -
__set__(self, instance, value):定義描述符的設(shè)置行為 -
__delete__(self, instance):定義描述符的刪除行為
比較操作
-
__lt__(self, other):小于,適用于< -
__le__(self, other):小于等于,適用于<= -
__eq__(self, other):等于,適用于== -
__ne__(self, other):不等于,適用于!= -
__gt__(self, other):大于,適用于> -
__ge__(self, other):大于等于,適用于>=
常見應用示例
字符串表示
__str__ 方法用于定義對象的字符串表示,通常用于用戶友好的輸出,而 __repr__ 方法用于定義對象的官方字符串表示,通??梢杂脕碇亟▽ο?。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"({self.x}, {self.y})"
def __repr__(self):
return f"Point({self.x}, {self.y})"
point = Point(3, 4)
print(str(point)) # 輸出:(3, 4)
print(repr(point)) # 輸出:Point(3, 4)
排序和比較
通過實現(xiàn)一系列比較運算的魔術(shù)方法,可以定義自定義類的排序和比較行為。
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __lt__(self, other):
return self.score < other.score
def __eq__(self, other):
return self.score == other.score
def __repr__(self):
return f"Student({self.name}, {self.score})"
students = [
Student("Alice", 85),
Student("Bob", 75),
Student("Charlie", 90)
]
# 根據(jù)分數(shù)排序
sorted_students = sorted(students)
print(sorted_students)
# 輸出:[Student(Bob, 75), Student(Alice, 85), Student(Charlie, 90)]
在這個例子中,通過實現(xiàn) __lt__ 方法,定義了對象的小于比較。因此可以使用 sorted() 函數(shù)對學生列表進行排序。
實現(xiàn)數(shù)值類
通過重載算術(shù)運算符的魔術(shù)方法,可以實現(xiàn)自定義數(shù)值類,如復數(shù)或矩陣。
class ComplexNumber:
def __init__(self, real, imag):
self.real = real
self.imag = imag
def __add__(self, other):
return ComplexNumber(
self.real + other.real,
self.imag + other.imag
)
def __mul__(self, other):
return ComplexNumber(
self.real * other.real - self.imag * other.imag,
self.real * other.imag + self.imag * other.real
)
def __repr__(self):
return f"{self.real} + {self.imag}i"
# 創(chuàng)建兩個復數(shù)
c1 = ComplexNumber(2, 3)
c2 = ComplexNumber(4, 5)
# 復數(shù)加法
print(c1 + c2) # 輸出:6 + 8i
# 復數(shù)乘法
print(c1 * c2) # 輸出:-7 + 22i
在這個例子中,通過重載 __add__ 和 __mul__ 方法,實現(xiàn)了復數(shù)的加法和乘法
集合與序列協(xié)議
通過實現(xiàn)集合與序列協(xié)議相關(guān)的魔術(shù)方法,可以定義自定義容器類,使其支持索引和迭代。
比如我們要實現(xiàn)一個既能排序又能去重的SortedSet類:
class SortedSet:
def __init__(self, items=None):
self._items = sorted(set(items)) if items else []
def __repr__(self):
return f"SortedSet({self._items})"
def __len__(self):
return len(self._items)
def __contains__(self, item):
return item in self._items
def __iter__(self):
return iter(self._items)
def __getitem__(self, index):
return self._items[index]
def add(self, item):
if item not in self._items:
self._items.append(item)
self._items.sort()
def remove(self, item):
if item in self._items:
self._items.remove(item)
# 測試 SortedSet 類
s = SortedSet([3, 1, 2, 3, 4])
print(s) # 輸出:SortedSet([1, 2, 3, 4])
print(len(s)) # 輸出:4
print(2 in s) # 輸出:True
print(s[0]) # 輸出:1
s.add(5)
print(s) # 輸出:SortedSet([1, 2, 3, 4, 5])
s.remove(2)
print(s) # 輸出:SortedSet([1, 3, 4, 5])
屬性訪問控制
通過實現(xiàn)屬性訪問相關(guān)的魔術(shù)方法,可以實現(xiàn)動態(tài)屬性和代理模式。
class DynamicAttribute:
def __getattr__(self, name):
if name == "age":
return 25
elif name == "name":
return "Alice"
else:
raise AttributeError(
f"'DynamicAttribute' object has no attribute '{name}'"
)
obj = DynamicAttribute()
print(obj.age) # 輸出:25
print(obj.name) # 輸出:Alice
在這個例子中,通過實現(xiàn) __getattr__ 方法,動態(tài)地為對象添加屬性。如果屬性不存在,則會調(diào)用這個方法來獲取屬性值,這在Python一些的ORM框架中很常見。
構(gòu)造查詢語句
python中的運算符|、&其實可以對標SQL中的or、and,所以我們可以通過魔術(shù)方法來實現(xiàn)一些類似SQL語句拼接
# 注:代碼塊暫不考慮太多異常情況,比如not操作
class Q:
def __init__(self, *expressions, operator="and", **kwargs):
self.operator = operator
self._expressions = expressions
self._kwargs = kwargs
def __or__(self, other):
return Q(self, other, operator="or")
def __str__(self):
return self.translate()
def translate(self):
kv_sql = " and ".join([f"{k}={v}" for k, v in self._kwargs.items()])
expr_sql = f" {self.operator} ".join([expr.translate() for expr in self._expressions])
return expr_sql + (f" {self.operator} " + kv_sql if expr_sql and kv_sql else kv_sql)
# 示例用法
q1 = Q(name='Alice', height=168)
q2 = Q(age=25, gender=0)
q3 = Q(q1, age=25)
# 實現(xiàn) OR 運算符
print(q1 | q2)
# 輸出:name=Alice and height=168 or age=25 and gender=0
print(q3)
# 輸出:name=Alice and height=168 and age=25
上面例子中kwargs參數(shù)代表的是參與and操作的拼接鍵值對參數(shù),而|則表示兩個對象要進行or操作的拼接,這樣就可以實現(xiàn)類似ORM框架里的還原SQL字符串 拼接操作了
上下文管理
上下文管理是 Python 中用于管理資源的一種機制,它可以確保在進入和離開代碼塊時資源得到正確的管理和釋放。在使用 with 語句時,Python 會調(diào)用對象的 __enter__ 方法來獲取上下文管理器,并在離開代碼塊時調(diào)用 __exit__ 方法來釋放資源。
class MyResource:
def __enter__(self):
print("Entering the resource")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting the resource")
def operation(self):
print("Performing operation")
# 使用上下文管理器
with MyResource() as resource:
resource.operation()
# Outputs
# Entering the resource
# Performing operation
# Exiting the resource
在這個示例中,MyResource 類實現(xiàn)了上下文管理器。當進入 with 代碼塊時,Python 會調(diào)用 __enter__ 方法,然后執(zhí)行其中的代碼;當離開 with 代碼塊時,Python 會調(diào)用 __exit__ 方法,以確保資源被正確釋放。
可調(diào)用對象
__call__ 方法使得一個對象可以像函數(shù)一樣被調(diào)用,這對于實現(xiàn)可調(diào)用對象非常有用。當調(diào)用對象時,Python 解釋器會自動調(diào)用對象的 __call__ 方法。
class MyCallable:
def __call__(self, x):
return x * 2
# 創(chuàng)建可調(diào)用對象
callable_obj = MyCallable()
# 調(diào)用可調(diào)用對象
result = callable_obj(5)
print(result) # 輸出:10
在這個示例中,MyCallable 類實現(xiàn)了 __call__ 方法,使得它的實例可以像函數(shù)一樣被調(diào)用。當調(diào)用 callable_obj(5) 時,Python 實際上調(diào)用了 callable_obj.__call__(5)。
__new__ 和 __init__
__new__ 方法和 __init__ 方法都是 Python 中用于創(chuàng)建類實例的特殊方法,但它們的作用略有不同。
__new__ 方法在實例創(chuàng)建之前調(diào)用,用于創(chuàng)建實例對象,并返回一個新創(chuàng)建的實例。
__init__ 方法在實例創(chuàng)建之后調(diào)用,用于初始化實例對象的屬性。
class MyClass:
def __new__(cls, *args, **kwargs):
print("Creating instance")
instance = super().__new__(cls)
return instance
def __init__(self, x):
print("Initializing instance")
self.x = x
# 創(chuàng)建實例
obj = MyClass(5)
# Outputs
# Creating instance
# Initializing instance
在這個示例中,__new__ 方法在實例創(chuàng)建之前被調(diào)用,用于創(chuàng)建實例對象,而 __init__ 方法在實例創(chuàng)建之后被調(diào)用,用于初始化實例對象的屬性。
總結(jié)
Python 魔術(shù)方法是一種特殊的命名約定,用雙下劃線包圍,用于實現(xiàn)對象的特殊行為和操作。它們包括但不限于初始化、字符串表示、比較、算術(shù)運算、上下文管理等方面。通過合理使用魔術(shù)方法,可以提高代碼的可讀性、可維護性,并實現(xiàn)更靈活的編程邏輯,是面向?qū)ο缶幊汤镆环N非常重要的實現(xiàn)方法。