এই সাধারণ সমস্যাটি বিবেচনা করুন:
class Number:
def __init__(self, number):
self.number = number
n1 = Number(1)
n2 = Number(1)
n1 == n2 # False -- oops
সুতরাং, পাইথন ডিফল্টরূপে তুলনা ক্রিয়াকলাপের জন্য অবজেক্ট শনাক্তকারী ব্যবহার করে:
id(n1) # 140400634555856
id(n2) # 140400634555920
__eq__
ফাংশনটি ওভাররাইড করা সমস্যার সমাধান বলে মনে হচ্ছে:
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
ইন পাইথন 2 , সবসময় ওভাররাইড মনে রাখবেন __ne__
পাশাপাশি ফাংশন, যেমন ডকুমেন্টেশন পদ বলে:
তুলনা অপারেটরদের মধ্যে কোনও অন্তর্নিহিত সম্পর্ক নেই। এর সত্যটি মিথ্যা x==y
বলে বোঝায় না x!=y
। তদনুসারে, সংজ্ঞা দেওয়ার সময় __eq__()
, একটিও নির্ধারণ করা উচিত __ne__()
যাতে অপারেটররা প্রত্যাশা অনুযায়ী আচরণ করবে।
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
return not self.__eq__(other)
n1 == n2 # True
n1 != n2 # False
ইন পাইথন 3 , এই আর প্রয়োজনীয়, যেমন ডকুমেন্টেশন পদ বলে:
ডিফল্টরূপে, __ne__()
প্রতিনিধিদের __eq__()
এবং inverts ফলাফলের যদি না তা হয় NotImplemented
। তুলনা অপারেটরগুলির মধ্যে অন্য কোনও অন্তর্নিহিত সম্পর্ক নেই, উদাহরণস্বরূপ, সত্যটি (x<y or x==y)
বোঝায় না x<=y
।
কিন্তু এটি আমাদের সমস্ত সমস্যার সমাধান করে না। একটি সাবক্লাস যুক্ত করা যাক:
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
দ্রষ্টব্য: পাইথন 2 এর দুটি ধরণের ক্লাস রয়েছে:
ক্লাসিক-শৈলী (বা পুরাতন-শৈলী ) ক্লাস, যাউত্তরাধিকার সূত্রে প্রাপ্ত হয় নাobject
এবং যা হিসাবে ঘোষিত হয়class A:
,class A():
বাক্লাসিক-শৈলীর শ্রেণিclass A(B):
কোথায়B
;
নতুন স্টাইলের ক্লাসগুলি, যেগুলি উত্তরাধিকার সূত্রে প্রাপ্ত হয়object
এবং যা হিসাবে ঘোষণা করা হয়class A(object)
বাclass A(B):
যেখানেB
একটি নতুন শৈলীর শ্রেণি রয়েছে। পাইথন 3-এ কেবলমাত্র নতুন-স্টাইলের ক্লাস রয়েছে যা হিসাবে ঘোষণা করা হয়েছেclass A:
,class A(object):
বাclass A(B):
।
ক্লাসিক-স্টাইলের ক্লাসগুলির জন্য, একটি তুলনা অপারেশন সর্বদা প্রথম অপারেন্ডের পদ্ধতিটিকে কল করে, যখন নতুন ধাঁচের ক্লাসগুলির জন্য, এটি সর্বদা সাবক্লাস অপারেন্ডের পদ্ধতিটিকে কল করে, অপারেণ্ডগুলির ক্রম নির্বিশেষে ।
সুতরাং এখানে, যদি Number
একটি ক্লাসিক-শৈলী শ্রেণি হয়:
n1 == n3
কল n1.__eq__
;
n3 == n1
কল n3.__eq__
;
n1 != n3
কল n1.__ne__
;
n3 != n1
কল n3.__ne__
।
এবং যদি Number
একটি নতুন-শৈলীর শ্রেণি হয়:
- উভয়
n1 == n3
এবং n3 == n1
কল n3.__eq__
;
- উভয়
n1 != n3
এবং n3 != n1
কল n3.__ne__
।
পাইথন 2 ক্লাসিক-শৈলীর ক্লাসগুলির জন্য অপারেটরগুলির ==
এবং !=
অপারেটরগুলির নন-কমিউটিটিভিটি ইস্যুটি ঠিক করার জন্য, যখন অপারেন্ড টাইপ সমর্থিত না হয় তখন __eq__
এবং __ne__
পদ্ধতিগুলি NotImplemented
মানটি ফিরিয়ে দেয় । ডকুমেন্টেশন সংজ্ঞায়িত NotImplemented
হিসাবে মান:
সংখ্যক পদ্ধতিগুলি এবং সমৃদ্ধ তুলনা পদ্ধতিগুলি যদি প্রদত্ত অপারেন্ডগুলির জন্য ক্রিয়াকলাপটি বাস্তবায়ন না করে তবে এই মানটি ফিরে আসতে পারে। (দোভাষী তখন অপারেটরের উপর নির্ভর করে প্রতিফলিত অপারেশন বা অন্য কোনও ফলব্যাক চেষ্টা করবেন try) এর সত্য মানটি সত্য।
এই ক্ষেত্রে অপারেটর প্রতিনিধিদের তুলনায় অপারেশন প্রতিফলিত পদ্ধতি এর অন্যান্য অপার্যান্ড। ডকুমেন্টেশন সংজ্ঞায়িত হিসাবে প্রতিফলিত পদ্ধতি:
এই পদ্ধতির কোনও অদল-বদল সংস্করণ নেই (যখন বাম আর্গুমেন্ট অপারেশন সমর্থন করে না তবে ডান আর্গুমেন্টটি ব্যবহার করে); বরং, __lt__()
এবং __gt__()
একে অপরের প্রতিবিম্ব, __le__()
এবং __ge__()
একে অপরের প্রতিবিম্ব, এবং
__eq__()
এবং __ne__()
তাদের নিজস্ব প্রতিবিম্ব হয়।
ফলাফলটি এরকম দেখাচ্ছে:
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
রিটার্নিং NotImplemented
পরিবর্তে মান False
যদি নতুন স্টাইলের শ্রেণীর এমনকি করতে ডান জিনিস commutativity এর ==
এবং !=
অপারেটরদের যখন operands সম্পর্কহীন ধরনের (কোন উত্তরাধিকার) হয় আকাঙ্ক্ষিত হয়।
আমরা কি এখনও সেখানে আছি? বেশ না। আমাদের কত অনন্য নম্বর আছে?
len(set([n1, n2, n3])) # 3 -- oops
সেটগুলি অবজেক্টের হ্যাশ ব্যবহার করে এবং ডিফল্টরূপে পাইথন বস্তুর সনাক্তকারীটির হ্যাশ দেয় returns আসুন এটি ওভাররাইড করার চেষ্টা করুন:
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
len(set([n1, n2, n3])) # 1
শেষ ফলাফলটি দেখতে দেখতে (বৈধতার জন্য আমি শেষে কিছু যুক্তি যুক্ত করেছি):
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
অপেক্ষাকৃত পরিচয়কে মান তুলনা থেকে আলাদা করতে অপারেটর রয়েছে ।