পাইথনে কীভাবে __eq__ পরিচালনা করা হয় এবং কোন ক্রমে?


106

পাইথন যেহেতু তার তুলনা অপারেটরগুলির বাম / ডান সংস্করণ সরবরাহ করে না, তাই কোন ফাংশনটি কল করতে হবে তা কীভাবে সিদ্ধান্ত নেবে?

class A(object):
    def __eq__(self, other):
        print "A __eq__ called"
        return self.value == other
class B(object):
    def __eq__(self, other):
        print "B __eq__ called"
        return self.value == other

>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False

এটি উভয় __eq__ফাংশন কল বলে মনে হচ্ছে ।

আমি সরকারী সিদ্ধান্ত গাছ খুঁজছি।

উত্তর:


126

a == bঅভিব্যক্তি এমন কিছুকে ডাকে, A.__eq__, যেহেতু এটি বিদ্যমান। এর কোড অন্তর্ভুক্ত self.value == other। যেহেতু ইনটস নিজেকে বি এর সাথে কীভাবে তুলনা করতে জানে না, পাইথন B.__eq__এটি দেখার চেষ্টা করে যে এটি কীভাবে নিজেকে কোন ইন্টের সাথে তুলনা করতে জানে।

কোন মানগুলি তুলনা করা হচ্ছে তা দেখানোর জন্য আপনি যদি আপনার কোডটি সংশোধন করেন:

class A(object):
    def __eq__(self, other):
        print("A __eq__ called: %r == %r ?" % (self, other))
        return self.value == other
class B(object):
    def __eq__(self, other):
        print("B __eq__ called: %r == %r ?" % (self, other))
        return self.value == other

a = A()
a.value = 3
b = B()
b.value = 4
a == b

এটি মুদ্রণ করবে:

A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?

69

পাইথন 2.x যখন দেখতে পাবে তখন a == bএটি নীচের চেষ্টা করে।

  • যদি type(b)একটি নতুন-শৈলীর শ্রেণি হয়, এবং type(b)এর একটি উপশ্রেণী হয় type(a)এবং type(b)এটি ওভাররাইড __eq__হয়ে যায় তবে ফলাফলটি b.__eq__(a)
  • যদি type(a)ওভাররাইড হয়ে থাকে __eq__( তবে type(a).__eq__তা নয় object.__eq__) তবে ফলাফলটি হবে a.__eq__(b)
  • যদি type(b)ওভাররাইড করা থাকে __eq__, তবে ফলাফলটি হবে b.__eq__(a)
  • যদি উপরের কোনওটি না হয় তবে পাইথন অনুসন্ধানের প্রক্রিয়াটি পুনরাবৃত্তি করবে __cmp__। যদি এটি উপস্থিত থাকে তবে অবজেক্টগুলি সমান হয় যদি এটি ফিরে আসে zero
  • চূড়ান্ত ফ্যালব্যাক হিসাবে পাইথন কল করে object.__eq__(a, b), যা Trueiff aএবং bএকই বস্তু।

যদি কোনও বিশেষ পদ্ধতি ফিরে আসে NotImplementedতবে পাইথন এমনভাবে কাজ করে যা পদ্ধতিটির অস্তিত্ব ছিল না।

মনে রাখবেন যে, শেষ পদক্ষেপ সাবধানে যদি তন্ন তন্ন aনা boverloads ==, তারপর a == bহিসাবে একই a is b


Https://eev.ee/blog/2012/03/24/python-faq-equality/ থেকে


4
আহহ মনে হচ্ছে অজগর 3 টি ডক্স ভুল ছিল। স্পষ্টতার জন্য bugs.python.org/issue4395 এবং প্যাচ দেখুন । টিএলডিআর: সাবক্লাসগুলি এখনও প্রথমে তুলনা করে, এমনকি এটি আরএইচএসে থাকলেও।
সর্বাধিক

হাই কেভ, সুন্দর পোস্ট প্রথম বুলেট পয়েন্টটি কোথায় নথিবদ্ধ করা হয়েছে এবং কেন এটির মতো ডিজাইন করা হয়েছে তা আপনি ব্যাখ্যা করতে পারেন?
Wim

4
হ্যাঁ অজগর 2 এর জন্য এই নথিভুক্ত কোথায়? এটা কি পিইপি?
মিস্টার_আর_আমস_ডি

এই উত্তরের ভিত্তিতে এবং মন্তব্য সহ, এটি আমাকে আগের চেয়ে আরও বিভ্রান্ত করেছিল।
সাজুউক

এবং বিটিডব্লিউ, __eq__ কেবলমাত্র কোনও ধরণের উদাহরণে একটি বাউন্ড পদ্ধতিটি সংজ্ঞায়িত করা হবে যা == ওভারড্রেনের জন্য যথেষ্ট নয়?
সাজুউক

11

আমি এই প্রশ্নের পাইথন 3 এর জন্য একটি আপডেট উত্তর লিখছি।

__eq__পাইথনে কীভাবে পরিচালনা করা হয় এবং কোন ক্রমে?

a == b

এটি সাধারণত বোঝা যায়, তবে সবসময় তা হয় না, যা a == bডাকে a.__eq__(b)বা type(a).__eq__(a, b)

স্পষ্টতই, মূল্যায়নের ক্রমটি হ'ল:

  1. bএর ধরণ যদি ধরণের ধরণের কঠোর সাবক্লাস হয় (একই ধরণের নয়) aএবং এর একটি __eq__থাকে তবে তা কল করুন এবং তুলনাটি কার্যকর হলে মানটি ফিরিয়ে দিন,
  2. অন্যথায়, যদি aএটি থাকে __eq__, কল করুন এবং যদি তুলনাটি বাস্তবায়িত হয় তবে তা ফিরিয়ে দিন,
  3. অন্যথায় দেখুন, আমরা বি'কে কল করি নি __eq__এবং এটি রয়েছে, তারপরে কল করুন এবং তুলনা কার্যকর হলে এটি ফিরিয়ে দিন,
  4. অন্যথায়, অবশেষে, পরিচয়ের জন্য তুলনা করুন, একই তুলনা করুন is

পদ্ধতিটি ফিরে আসে যদি আমরা কোনও তুলনা কার্যকর না করা হয় তা জানি NotImplemented

(পাইথন 2 এ, এমন একটি __cmp__পদ্ধতি ছিল যা সন্ধান করা হয়েছিল, তবে এটি পাইথন 3 এ অবহেলা ও অপসারণ করা হয়েছিল)

আসুন বি সাবক্লাস এ দিয়ে নিজের জন্য প্রথম চেকের আচরণটি পরীক্ষা করি, যা দেখায় যে গৃহীত উত্তরটি এই গণনায় ভুল:

class A:
    value = 3
    def __eq__(self, other):
        print('A __eq__ called')
        return self.value == other.value

class B(A):
    value = 4
    def __eq__(self, other):
        print('B __eq__ called')
        return self.value == other.value

a, b = A(), B()
a == b

যা B __eq__ calledফিরে আসার আগে কেবল মুদ্রণ করে False

আমরা কীভাবে এই সম্পূর্ণ অ্যালগরিদম জানি?

এখানে অন্যান্য উত্তরগুলি অসম্পূর্ণ এবং পুরানো বলে মনে হচ্ছে, তাই আমি তথ্য আপডেট করতে যাচ্ছি এবং কীভাবে আপনি নিজের জন্য এটি দেখতে পারেন তা আপনাকে দেখাব।

এটি সি স্তরে পরিচালনা করা হয়।

আমাদের এখানে দুটি ভিন্ন বিটের কোড দেখতে হবে - __eq__শ্রেণীর অবজেক্টের জন্য ডিফল্ট object, এবং কোডটি যেটি দেখায় এবং __eq__পদ্ধতিটি এটি ডিফল্ট __eq__বা কাস্টম ব্যবহার করে তা নির্বিশেষে কল করে ।

ডিফল্ট __eq__

প্রাসঙ্গিক সি এপিআই ডক্সে সন্ধান __eq__করা আমাদের দেখায় যে এটি দ্বারা পরিচালিত হয় - যা টাইপ সংজ্ঞায়িতটির জন্য সংজ্ঞায়িত হয় ।__eq__tp_richcompare"object"cpython/Objects/typeobject.cobject_richcomparecase Py_EQ:

    case Py_EQ:
        /* Return NotImplemented instead of False, so if two
           objects are compared, both get a chance at the
           comparison.  See issue #1393. */
        res = (self == other) ? Py_True : Py_NotImplemented;
        Py_INCREF(res);
        break;

সুতরাং এখানে, যদি self == otherআমরা ফিরে True, অন্যথায় আমরা NotImplementedবস্তু ফিরে । এটি কোনও অবজেক্টের সাবক্লাসের জন্য ডিফল্ট আচরণ যা তার নিজস্ব __eq__পদ্ধতি প্রয়োগ করে না ।

কিভাবে __eq__ডাকা হয়

তারপরে আমরা সিআই এপি ডক্স পাই পাইবজেক্ট_রিচকম্পের ফাংশনটি পাই যা কল করে do_richcompare

তারপরে আমরা দেখতে পেলাম যে সি সংজ্ঞার tp_richcompareজন্য তৈরি ফাংশনটি "object"ডেকে আনে do_richcompare, সুতরাং আসুন আমরা এটি আরও ঘনিষ্ঠভাবে দেখি।

এই ফাংশনটির প্রথম চেকটি বস্তুর তুলনা করার শর্তের জন্য:

  • হয় না একই ধরনের, কিন্তু
  • দ্বিতীয় প্রকারটি প্রথম ধরণের একটি উপশ্রেণী, এবং
  • দ্বিতীয় ধরণের একটি __eq__পদ্ধতি আছে,

তারপরে অন্যটির পদ্ধতিটি কল করে আর্গুমেন্টগুলি অদলবদল করে, প্রয়োগ করা হলে মানটি ফিরিয়ে দেয়। যদি সেই পদ্ধতিটি প্রয়োগ না করা হয়, তবে আমরা চালিয়ে যা ...

    if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
        PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
        (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        checked_reverse_op = 1;
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

পরবর্তী আমরা দেখতে পাই যে আমরা __eq__প্রথম ধরণটি থেকে পদ্ধতিটি অনুসন্ধান করতে পারি এবং এটি কল করতে পারি। যতক্ষণ না ফলাফলটি NotImplemented হয় না, অর্থাৎ এটি কার্যকর করা হয়, আমরা এটি ফিরিয়ে দিই।

    if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
        res = (*f)(v, w, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

অন্যথায় যদি আমরা অন্য ধরণের পদ্ধতিটি চেষ্টা না করে এবং এটি সেখানে থাকে তবে আমরা তারপরে চেষ্টা করে থাকি এবং যদি তুলনাটি বাস্তবায়িত হয় তবে আমরা এটিকে ফিরিয়ে দিই।

    if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }

শেষ অবধি, কারওর জন্য এটি প্রয়োগ না করা হলে আমরা ফ্যালব্যাক পাই।

ফলব্যাকটি বস্তুর সনাক্তকরণের জন্য যাচাই করে, এটি মেমরির একই জায়গায় একই জিনিস কিনা - এটি একই পরীক্ষার জন্য self is other:

    /* If neither object implements it, provide a sensible default
       for == and !=, but raise an exception for ordering. */
    switch (op) {
    case Py_EQ:
        res = (v == w) ? Py_True : Py_False;
        break;

উপসংহার

তুলনায় আমরা প্রথমে তুলনার সাবক্লাস বাস্তবায়নকে সম্মান করি।

তারপরে আমরা প্রথম অবজেক্টের প্রয়োগের সাথে তুলনা করার চেষ্টা করি, তারপরে দ্বিতীয়টিকে যদি এটি না বলা হয় with

সমাপ্তির জন্য তুলনার জন্য অবশেষে আমরা পরিচয়ের জন্য একটি পরীক্ষা ব্যবহার করি।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.