Bu basit sorunu düşünün:
class Number:
def __init__(self, number):
self.number = number
n1 = Number(1)
n2 = Number(1)
n1 == n2 # False -- oops
Bu nedenle, Python varsayılan olarak karşılaştırma işlemleri için nesne tanımlayıcılarını kullanır:
id(n1) # 140400634555856
id(n2) # 140400634555920
Fonksiyonu geçersiz __eq__
kılmak sorunu çözüyor gibi görünüyor:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return False
n1 == n2 # True
n1 != n2 # True in Python 2 -- oops, False in Python 3
In Python 2 , her zaman geçersiz kılmak için hatırlamak __ne__
gibi, hem de işlevi belgeleri devletler:
Karşılaştırma işleçleri arasında herhangi bir zımni ilişki yoktur. Gerçek, bunun yanlış olduğu x==y
anlamına gelmez x!=y
. Buna göre, tanımlarken __eq__()
, __ne__()
operatörlerin beklendiği gibi davranması için de tanımlanmalıdır .
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
return not self.__eq__(other)
n1 == n2 # True
n1 != n2 # False
In Python 3 , bu gibi artık gerekli değildir belgeleri devletler:
Varsayılan olarak, sonuca __ne__()
yetki __eq__()
verilmez ve sonuç ters çevrilir NotImplemented
. Karşılaştırma operatörleri arasında başka hiçbir zımni ilişki yoktur, örneğin, gerçeği (x<y or x==y)
ima etmez x<=y
.
Ancak bu tüm sorunlarımızı çözmez. Bir alt sınıf ekleyelim:
class SubNumber(Number):
pass
n3 = SubNumber(1)
n1 == n3 # False for classic-style classes -- oops, True for new-style classes
n3 == n1 # True
n1 != n3 # True for classic-style classes -- oops, False for new-style classes
n3 != n1 # False
Not: Python 2'de iki tür sınıf vardır:
Klasik tarzdaki (veya eski tarz do) sınıfları, değil devralmakobject
ve ilan edilirclass A:
,class A():
ya daclass A(B):
neredeB
bir klasik tarzda sınıftır;
Yeni tarzı devralan do sınıfları,object
olarak, beyanclass A(object)
veyaclass A(B):
neredeB
yeni tarzı sınıftır. Python 3 olarak ilan edilmiştir yalnızca yeni tarzı sınıfları vardırclass A:
,class A(object):
ya daclass A(B):
.
Klasik stil sınıfları için, karşılaştırma işlemi her zaman ilk işlenenin yöntemini çağırırken, yeni stil sınıfları için, işlenenlerin sırasından bağımsız olarak her zaman alt sınıf işleneninin yöntemini çağırır .
Burada Number
klasik tarzda bir sınıf varsa:
n1 == n3
çağrılar n1.__eq__
;
n3 == n1
çağrılar n3.__eq__
;
n1 != n3
çağrılar n1.__ne__
;
n3 != n1
çağrıları n3.__ne__
.
Ve eğer Number
yeni bir sınıf ise:
- her ikisi de
n1 == n3
ve n3 == n1
çağrı n3.__eq__
;
- hem
n1 != n3
ve n3 != n1
çağrı n3.__ne__
.
Python 2 klasik tarzı sınıfların ==
ve !=
işleçlerinin değişmezlik sorununu gidermek için , işlenen türü desteklenmediğinde __eq__
ve __ne__
yöntemlerinin NotImplemented
değeri döndürmesi gerekir . Belgeler tanımlar NotImplemented
değeri olarak:
Sayısal yöntemler ve zengin karşılaştırma yöntemleri, sağlanan işlenenler için işlemi uygulamazlarsa bu değeri döndürebilir. (Daha sonra yorumlayıcı, operatöre bağlı olarak yansıtılan işlemi veya başka bir geri dönüşü dener.) Gerçek değeri doğrudur.
Bu durumda, operatör delegelerinin karşılaştırma işlemi yöntemi yansıyan ait diğer işlenen. Belgeler tanımlar yöntemleri olarak yansıtılır:
Bu yöntemlerin takas argümanı sürümleri yoktur (sol argüman işlemi desteklemiyor, ancak sağ argüman destekliyorsa kullanılır); daha doğrusu, __lt__()
ve __gt__()
birbirlerinin yansımasıdır, __le__()
ve __ge__()
birbirlerinin yansımasıdır ve
__eq__()
ve __ne__()
kendi yansımasıdır.
Sonuç şuna benzer:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is NotImplemented:
return NotImplemented
return not x
Geri dönen NotImplemented
yerine değerini False
eğer yeni tarzı sınıflar için bile yapılacak doğru şey olduğunu Yerdeğiştirme ait ==
ve !=
işlenenler ilgisiz türleri (hiçbir miras) ait olduğunda operatörleri arzu edilir.
Henüz varmadık mı? Pek değil. Kaç tane benzersiz numaramız var?
len(set([n1, n2, n3])) # 3 -- oops
Kümeler nesnelerin karmasını kullanır ve varsayılan olarak Python nesne tanımlayıcısının karmasını döndürür. Geçersiz kılmaya çalışalım:
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
len(set([n1, n2, n3])) # 1
Sonuç şu şekilde görünüyor (Doğrulama için bazı iddialar ekledim):
class Number:
def __init__(self, number):
self.number = number
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is not NotImplemented:
return not x
return NotImplemented
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
class SubNumber(Number):
pass
n1 = Number(1)
n2 = Number(1)
n3 = SubNumber(1)
n4 = SubNumber(4)
assert n1 == n2
assert n2 == n1
assert not n1 != n2
assert not n2 != n1
assert n1 == n3
assert n3 == n1
assert not n1 != n3
assert not n3 != n1
assert not n1 == n4
assert not n4 == n1
assert n1 != n4
assert n4 != n1
assert len(set([n1, n2, n3, ])) == 1
assert len(set([n1, n2, n3, n4])) == 2
is
nesne kimliğini değer karşılaştırmasından ayırmak için operatöre sahiptir .