Py面試必問:說說魔術(shù)方法與應用

在中高級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)方法。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容