পাইথন, আমার __ne__()উপর নির্ভর করে অপারেটরটি প্রয়োগ করা উচিত __eq__?
সংক্ষিপ্ত উত্তর: এটি বাস্তবায়ন করবেন না, তবে আপনাকে অবশ্যই ব্যবহার করুন ==, না__eq__
পাইথন 3-এ, ডিফল্টরূপে !=অবহেলা করা ==হয়, সুতরাং আপনার এমনকি একটি __ne__লেখারও প্রয়োজন হয় না এবং ডকুমেন্টেশন আর লেখার পক্ষে মতামত দেয় না।
সাধারণভাবে বলতে গেলে পাইথন 3-কেবল কোডের জন্য, পিতা-মাতার বাস্তবায়নকে যেমন ছড়িয়ে দেওয়া দরকার না, যেমন বিল্টিন অবজেক্টের জন্য write
এটি হ'ল রেমন্ড হেটেঙ্গারের মন্তব্যটি মনে রাখবেন :
__ne__পদ্ধতি থেকে স্বয়ংক্রিয়ভাবে অনুসরণ __eq__যদি শুধু
__ne__ইতিমধ্যে একটি সুপারক্লাস সংজ্ঞায়িত করা হয় না। সুতরাং, যদি আপনি কোনও বিল্টিন থেকে উত্তরাধিকার সূত্রে পান তবে উভয়কেই ওভাররাইড করা ভাল।
পাইথন 2 এ কাজ করার জন্য যদি আপনার কোডের প্রয়োজন হয় তবে পাইথন 2 এর জন্য প্রস্তাবটি অনুসরণ করুন এবং এটি পাইথন 3 এ কাজ করবে।
পাইথন 2, পাইথন নিজেই স্বয়ংক্রিয়ভাবে অন্য পরিপ্রেক্ষিতে কোনো অপারেশন বাস্তবায়ন না - তাই আপনি সংজ্ঞায়িত করা উচিত __ne__পরিপ্রেক্ষিতে ==পরিবর্তে __eq__। ইজি
class A(object):
def __eq__(self, other):
return self.value == other.value
def __ne__(self, other):
return not self == other
প্রমাণ দেখুন
__ne__()উপর ভিত্তি করে বাস্তবায়নকারী অপারেটর __eq__এবং
__ne__পাইথন 2 এ মোটেই বাস্তবায়ন করা হচ্ছে না
নীচের প্রদর্শনীতে ভুল আচরণ সরবরাহ করে।
দীর্ঘ উত্তর
ডকুমেন্টেশন পাইথন 2 জন্য বলেছেন:
তুলনা অপারেটরদের মধ্যে কোনও অন্তর্নিহিত সম্পর্ক নেই। এর সত্যটি মিথ্যা x==yবলে বোঝায় না x!=y। তদনুসারে, সংজ্ঞা দেওয়ার সময় __eq__(), একটিও নির্ধারণ করা উচিত __ne__()যাতে অপারেটররা প্রত্যাশা অনুযায়ী আচরণ করবে।
সুতরাং এর অর্থ হ'ল আমরা যদি __ne__উল্টো দিকের দিক দিয়ে সংজ্ঞায়িত করি তবে আমরা __eq__ধারাবাহিক আচরণ পেতে পারি।
ডকুমেন্টেশনের এই বিভাগটি পাইথন 3 এর জন্য আপডেট করা হয়েছে :
ডিফল্টরূপে, __ne__()প্রতিনিধিরা __eq__()ফলাফলটি না করে উল্টে দেয় NotImplemented।
এবং "নতুন কী" বিভাগে আমরা দেখি যে এই আচরণটি পরিবর্তিত হয়েছে:
!===যদি না ==ফেরায় তবে এখন এর বিপরীতে ফিরে আসে NotImplemented।
বাস্তবায়নের জন্য __ne__, আমরা==__eq__ সরাসরি পদ্ধতিটি ব্যবহার না করে অপারেটরটি ব্যবহার করতে পছন্দ করি যাতে self.__eq__(other)কোনও সাবক্লাস যদি NotImplementedপরীক্ষিত প্রকারের জন্য ফিরে আসে , পাইথন other.__eq__(self) ডকুমেন্টেশন থেকে যথাযথভাবে পরীক্ষা করবে :
NotImplementedউদ্দেশ্য
এই ধরণের একক মান রয়েছে। এই মান সহ একটি একক অবজেক্ট আছে। এই অবজেক্টটি বিল্ট-ইন নামের মাধ্যমে অ্যাক্সেস করা হয়েছে
NotImplemented। সংখ্যার পদ্ধতি এবং সমৃদ্ধ তুলনা পদ্ধতিগুলি যদি প্রদত্ত অপারেন্ডগুলির জন্য ক্রিয়াকলাপ বাস্তবায়ন না করে তবে এই মানটি ফিরিয়ে দিতে পারে। (দোভাষী তারপরে অপারেটরের উপর নির্ভর করে প্রতিফলিত অপারেশন বা অন্য কোনও ফ্যালব্যাক চেষ্টা করবেন)) এর সত্য মানটি সত্য।
যখন একটি সমৃদ্ধ তুলনা অপারেটর প্রদত্ত, যদি তারা একই ধরনের, নও পাইথন চেক যদি otherএকটি উপপ্রকার হয়, এবং যদি এটা যে অপারেটর সংজ্ঞায়িত আছে, এটি ব্যবহার করে otherএর পদ্ধতি প্রথম (বিপরীত জন্য <, <=, >=এবং >)। যদি NotImplementedফিরে আসে, তবে এটি বিপরীত পদ্ধতিটি ব্যবহার করে। (এটি একই পদ্ধতির জন্য দুবার পরীক্ষা করে না )) ==অপারেটরটি ব্যবহারের ফলে এই যুক্তিটি সংঘটিত হওয়ার অনুমতি দেয়।
প্রত্যাশা
শব্দার্থকভাবে, আপনার __ne__সমতার জন্য চেকের শর্তাবলী কার্যকর করা উচিত কারণ আপনার শ্রেণীর ব্যবহারকারীরা নিম্নলিখিত ফাংশনগুলি এ এর সমস্ত দৃষ্টান্তের জন্য সমতুল্য আশা করবে:
def negation_of_equals(inst1, inst2):
"""always should return same as not_equals(inst1, inst2)"""
return not inst1 == inst2
def not_equals(inst1, inst2):
"""always should return same as negation_of_equals(inst1, inst2)"""
return inst1 != inst2
যে, উপরের উভয় ফাংশন সর্বদা একই ফলাফল প্রদান করা উচিত । তবে এটি প্রোগ্রামারের উপর নির্ভরশীল।
__ne__উপর নির্ভর করে সংজ্ঞায়িত করার সময় অপ্রত্যাশিত আচরণের প্রদর্শন __eq__:
প্রথম সেটআপ:
class BaseEquatable(object):
def __init__(self, x):
self.x = x
def __eq__(self, other):
return isinstance(other, BaseEquatable) and self.x == other.x
class ComparableWrong(BaseEquatable):
def __ne__(self, other):
return not self.__eq__(other)
class ComparableRight(BaseEquatable):
def __ne__(self, other):
return not self == other
class EqMixin(object):
def __eq__(self, other):
"""override Base __eq__ & bounce to other for __eq__, e.g.
if issubclass(type(self), type(other)): # True in this example
"""
return NotImplemented
class ChildComparableWrong(EqMixin, ComparableWrong):
"""__ne__ the wrong way (__eq__ directly)"""
class ChildComparableRight(EqMixin, ComparableRight):
"""__ne__ the right way (uses ==)"""
class ChildComparablePy3(EqMixin, BaseEquatable):
"""No __ne__, only right in Python 3."""
অ-সমতুল্য উদাহরণগুলি ইনস্ট্যান্ট করুন:
right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)
প্রত্যাশিত আচরণ:
(দ্রষ্টব্য: নীচের প্রত্যেকটির প্রতিটি দ্বিতীয় বক্তব্য সমতুল্য এবং অতএব যুক্তিযুক্তভাবে এর আগে যেটির কাছে তা অপ্রয়োজনীয়, আমি তাদের যুক্ত করছি যাতে এই আদেশটি প্রদর্শিত হয় যে যখন অন্যের সাবক্লাস হয় তখন কিছু যায় আসে না। )
এই দৃষ্টান্তগুলি __ne__প্রয়োগ করে ==:
assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1
পাইথন 3 এর অধীনে এই উদাহরণগুলিও সঠিকভাবে কাজ করে:
assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1
এবং প্রত্যাহার করুন যে এগুলি __ne__বাস্তবায়িত করেছে __eq__- যদিও এটি প্রত্যাশিত আচরণ, বাস্তবায়নটি ভুল:
assert not wrong1 == wrong2
assert not wrong2 == wrong1
অপ্রত্যাশিত আচরণ:
নোট করুন যে এই তুলনাটি উপরের তুলনাগুলির সাথে বিরোধী ( not wrong1 == wrong2)।
>>> assert wrong1 != wrong2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
এবং,
>>> assert wrong2 != wrong1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
__ne__পাইথন 2 এ এড়িয়ে যাবেন না
__ne__পাইথন 2 এ প্রয়োগ করা এড়িয়ে যাবেন না তার প্রমাণের জন্য , এই সমতুল্য বস্তুগুলি দেখুন:
>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child
True
উপরের ফলাফল হওয়া উচিত False!
পাইথন 3 উত্স
ডিফল্ট CPython বাস্তবায়ন __ne__হয় typeobject.cমধ্যেobject_richcompare :
case Py_NE:
if (Py_TYPE(self)->tp_richcompare == NULL) {
res = Py_NotImplemented;
Py_INCREF(res);
break;
}
res = (*Py_TYPE(self)->tp_richcompare)(self, other, Py_EQ);
if (res != NULL && res != Py_NotImplemented) {
int ok = PyObject_IsTrue(res);
Py_DECREF(res);
if (ok < 0)
res = NULL;
else {
if (ok)
res = Py_False;
else
res = Py_True;
Py_INCREF(res);
}
}
break;
কিন্তু ডিফল্ট __ne__ব্যবহার করে __eq__?
__ne__সি স্তরে পাইথন 3 এর ডিফল্ট বাস্তবায়ন বিশদটি ব্যবহার করে __eq__কারণ উচ্চতর স্তর ==( পাইওবজেক্ট_রিচকম্পার ) কম দক্ষ হবে - এবং তাই এটি অবশ্যই পরিচালনা করতে হবে NotImplemented।
যদি __eq__সঠিকভাবে প্রয়োগ করা হয়, তবে এর অস্বীকারও ==সঠিক - এবং এটি আমাদের আমাদের নিম্ন স্তরের প্রয়োগের বিশদ এড়াতে দেয় __ne__।
ব্যবহার ==আমাদের মধ্যে আমাদের নিম্ন স্তরের যুক্তিবিজ্ঞান রাখার অনুমতি দেয় এক জায়গা, এবং এড়ানো অ্যাড্রেসিং NotImplementedমধ্যে __ne__।
কেউ ভুল করে ধরে নিতে পারে যে ==ফিরে আসতে পারে NotImplemented।
এটি আসলে ডিফল্ট বাস্তবায়ন হিসাবে একই যুক্তি ব্যবহার করে __eq__, যা সনাক্তকরণের জন্য পরীক্ষা করে (দেখুন do_richcompare এবং নীচে আমাদের প্রমাণ দেখুন)
class Foo:
def __ne__(self, other):
return NotImplemented
__eq__ = __ne__
f = Foo()
f2 = Foo()
এবং তুলনা:
>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True
কর্মক্ষমতা
আমার শব্দটির জন্য এটি গ্রহণ করবেন না, আসুন আরও বেশি পারফরম্যান্স দেখি:
class CLevel:
"Use default logic programmed in C"
class HighLevelPython:
def __ne__(self, other):
return not self == other
class LowLevelPython:
def __ne__(self, other):
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
def c_level():
cl = CLevel()
return lambda: cl != cl
def high_level_python():
hlp = HighLevelPython()
return lambda: hlp != hlp
def low_level_python():
llp = LowLevelPython()
return lambda: llp != llp
আমি মনে করি এই পারফরম্যান্স সংখ্যাগুলি তাদের জন্য কথা বলে:
>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029
আপনি যখন বিবেচনা করেন যে low_level_pythonপাইথনে যুক্তি করছেন যা অন্যথায় সি স্তরে পরিচালিত হবে।
কিছু সমালোচকদের প্রতিক্রিয়া
অন্য উত্তরদাতা লিখেছেন:
হারুন হলের বাস্তবায়ন not self == otherএর __ne__পদ্ধতি সঠিক নয় যেমন কখনো আসতে পারেন NotImplemented( not NotImplementedহয় False) এবং সেইজন্য __ne__পদ্ধতি অগ্রাধিকার আছে যা ফিরে পড়া না করতে পারেন __ne__পদ্ধতি যা অগ্রাধিকার নেই।
রয়ে __ne__কখনো ফেরত NotImplementedএটা ভুল দেখা যায় না। পরিবর্তে, আমরা এর সাথে NotImplementedসাম্যের জন্য চেকের মাধ্যমে অগ্রাধিকারটি পরিচালনা করি ==। ধরে ==নেওয়া সঠিকভাবে কার্যকর হয়েছে, আমরা সম্পন্ন করেছি।
not self == other__ne__পদ্ধতির ডিফল্ট পাইথন 3 বাস্তবায়ন হিসাবে ব্যবহৃত হত তবে এটি একটি বাগ ছিল এবং শেডোরেঞ্জার লক্ষ্য করেই জানুয়ারি 2015-তে পাইথন 3.4-এ এটি সংশোধন করা হয়েছিল (সমস্যা # 21408 দেখুন)।
ঠিক আছে, এর ব্যাখ্যা করা যাক।
যেমন পূর্বে উল্লেখ করা হয়েছে, পাইথন 3 ডিফল্টরূপে হ্যান্ডল __ne__করে প্রথমে self.__eq__(other)রিটার্ন NotImplemented(সিঙ্গলটন) যাচাই করা উচিত - যা দিয়ে পরীক্ষা করা উচিত isএবং যদি তা ফিরে আসে তবে অন্যথায় এটি বিপরীতটি ফিরে আসে return ক্লাস মিক্সিন হিসাবে লেখা সেই যুক্তিটি এখানে:
class CStyle__ne__:
"""Mixin that provides __ne__ functionality equivalent to
the builtin functionality
"""
def __ne__(self, other):
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
সি স্তরের পাইথন এপিআইয়ের নির্ভুলতার জন্য এটি প্রয়োজনীয়, এবং এটি পাইথন 3-এ তৈরি হয়েছিল
অপ্রয়োজনীয় সমস্ত প্রাসঙ্গিক __ne__পদ্ধতিগুলি সরানো হয়েছে যার মধ্যে তাদের নিজস্ব চেক প্রয়োগ করা হয়েছে এবং সেই সাথে __eq__সরাসরি বা মাধ্যমে প্রতিনিধি দেওয়া হয়েছে ==- এবং ==এটি করার সবচেয়ে সাধারণ উপায় ছিল।
প্রতিসম গুরুত্বপূর্ণ কি?
আমাদের ক্রমাগত সমালোচক পরিচালনা করার জন্য কেস করতে একটি আবেগপূর্ণ উদাহরণ NotImplementedমধ্যে __ne__, সব অন্য উপরে প্রতিসাম্য valuing। এর সুস্পষ্ট উদাহরণ সহ যুক্তিটিকে স্টিল-ম্যান করি:
class B:
"""
this class has no __eq__ implementation, but asserts
any instance is not equal to any other object
"""
def __ne__(self, other):
return True
class A:
"This class asserts instances are equivalent to all other objects"
def __eq__(self, other):
return True
>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)
সুতরাং, এই যুক্তি দ্বারা, প্রতিসাম্যতা বজায় রাখতে, __ne__পাইথন সংস্করণ নির্বিশেষে আমাদের জটিলটি লিখতে হবে ।
class B:
def __ne__(self, other):
return True
class A:
def __eq__(self, other):
return True
def __ne__(self, other):
result = other.__eq__(self)
if result is NotImplemented:
return NotImplemented
return not result
>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)
স্পষ্টতই আমাদের মনে করা উচিত নয় যে এই দৃষ্টান্তগুলি উভয়ই সমান এবং সমান নয়।
আমি প্রস্তাব দিচ্ছি যে সংবেদনশীল কোড অনুমানের চেয়ে ডকুমেন্টেশনের পরামর্শ অনুসরণ করার চেয়ে প্রতিসামগ্রী কম গুরুত্বপূর্ণ।
তবে, যদি ক এর বুদ্ধিমান বাস্তবায়ন হয় __eq__, তবে আমরা এখনও আমার দিকনির্দেশনাটি এখানে অনুসরণ করতে পারতাম এবং এখনও আমাদের প্রতিসাম্যতা থাকতে পারে:
class B:
def __ne__(self, other):
return True
class A:
def __eq__(self, other):
return False
>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)
উপসংহার
পাইথন 2 সামঞ্জস্যপূর্ণ কোডের জন্য, ==প্রয়োগ করতে ব্যবহার করুন __ne__। এটি আরও:
পাইথন 3-তে কেবলমাত্র সি স্তরে নিম্ন-স্তরের অবহেলা ব্যবহার করুন - এটি আরও সাধারণ এবং পারফরম্যান্ট (যদিও প্রোগ্রামার এটি সঠিক কিনা তা নির্ধারণের জন্য দায়বদ্ধ )।
আবার উচ্চ স্তরের পাইথনে নিম্ন-স্তরের যুক্তি লিখবেন না ।
__ne____eq__