一個稍微長的問題足以解釋背景......
假設有一個內置class A:
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
return self.a == other.a
預計以這種方式進行比較:
a1, a2 = A(1), A(2)
a1 == a2 # False
出于某種原因,該團隊在其之上引入了一個包裝器(代碼示例實際上并未包裝 A 以簡化代碼復雜性。)
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
return self.pa == other.pa
同樣,預計以這種方式進行比較:
wa1, wa2 = WrapperA(1), WrapperA(2)
wa1 == wa2 # False
雖然預期使用A或WrapperA,但問題是一些代碼庫包含這兩種用法,因此以下比較失敗:
a, wa = A(), WrapperA()
wa == a # AttributeError
a == wa # AttributeError
一個已知的解決方案是修改__eq__:
對于wa == a:
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
if isinstance(other, A):
return self.pa == other.a
return self.pa == other.pa
對于a == wa:
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
if isinstance(other, WrapperA):
return self.a == other.pa
return self.a == other.a
需要修改 WrapperA。對于A,由于它是一個內置的東西,兩個解決方案是:
- 使用 setattr 擴展 A 以支持 WrapperA。
setattr(A, '__eq__', eq_that_supports_WrapperA)
- 強制開發人員只進行比較
wa == a(然后不關心a == wa)。
第一個選項顯然是重復實作的丑陋,第二個給開發人員帶來了不必要的“驚喜”。所以我的問題是,是否有一種優雅的方法可以在內部替換Python 實作對a == watowa == a的任何使用?
uj5u.com熱心網友回復:
我真的不喜歡這整件事,因為我認為包裝一個內置函式并使用不同的屬性名稱會導致意想不到的東西,但無論如何,這對你有用
import inspect
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
return self.a == other.a
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
if isinstance(other, A):
return self.pa == other.a
return self.pa == other.pa
def __getattribute__(self, item):
# Figure out who tried to get the attribute
# If the item requested was 'a', check if A's __eq__ method called us,
# in that case return pa instead
caller = inspect.stack()[1]
if item == 'a' and getattr(caller, 'function') == '__eq__' and isinstance(caller.frame.f_locals.get('self'), A):
return super(WrapperA, self).__getattribute__('pa')
return super(WrapperA, self).__getattribute__(item)
a = A(5)
wrap_a = WrapperA(5)
print(a == wrap_a)
print(wrap_a == a)
wrap_a.pa = 7
print(a == wrap_a)
print(wrap_a == a)
print(f'{wrap_a.pa=}')
輸出:
True
True
False
False
wrap_a.pa=7
uj5u.com熱心網友回復:
類似于 Ron Serruyas 的回答:
這使用__getattr__而不是__getattribute__,其中第一個僅在第二個引發 AttributeError 或顯式呼叫它時才呼叫(ref)。這意味著如果包裝器沒有實作__eq__并且相等應該只在存盤在類物件中的底層資料結構上執行,一個作業示例如下:
class A(object):
def __init__(self, internal_data=None):
self._internal_data = internal_data
def __eq__(self, other):
return self._internal_data == other._internal_data
class WrapperA(object):
def __init__(self, a_object: A):
self._a = a_object
def __getattr__(self, attribute):
if attribute != '_a': # This is neccessary to prevent recursive calls
return getattr(self._a, attribute)
a1 = A(internal_data=1)
a2 = A(internal_data=2)
wa1 = WrapperA(a1)
wa2 = WrapperA(a2)
print(
a1 == a1,
a1 == a2,
wa1 == wa1,
a1 == wa1,
a2 == wa2,
wa1 == a1)
>>> True False True True True True
uj5u.com熱心網友回復:
參考 MisterMiyagi 在問題下的評論:
請注意, == 通常預期適用于所有型別。
A.__eq__要求 other 為 A 實際上是一個應該修復的錯誤。當它無法做出決定時,它至少應該回傳 NotImplemented
這很重要,而不僅僅是風格問題。事實上,根據檔案:
當二進制(或就地)方法回傳時
NotImplemented,解釋器將嘗試對其他型別的反射操作。
因此,如果您只是應用 MisterMiyagi 的評論并修復 的邏輯__eq__,您將看到您的代碼已經正常作業:
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
if isinstance(other, A):
return self.a == other.a
return NotImplemented
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
if isinstance(other, A):
return self.pa == other.a
elif isinstance(other, WrapperA):
return self.pa == other.pa
return NotImplemented
a = A(5)
wrap_a = WrapperA(5)
print(a == wrap_a)
print(wrap_a == a)
wrap_a.pa = 7
print(a == wrap_a)
print(wrap_a == a)
print(f'{wrap_a.pa=}')
產量:
True
True
False
False
wrap_a.pa=7
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/377533.html
上一篇:過濾numpy陣列中的資料
下一篇:如何取NaN值之間的平均值?
