নতুন স্টাইলের ক্লাসে পদ্ধতি রেজোলিউশন অর্ডার (এমআরও)?


99

পাইথন ইন এ সংক্ষেপে (২ য় সংস্করণ) বইতে একটি উদাহরণ রয়েছে যা
ক্লাসিক রেজোলিউশন ক্রমে কীভাবে পদ্ধতিগুলি সমাধান
করা হয় এবং কীভাবে এটি নতুন ক্রমের সাথে পৃথক হয় তা প্রদর্শন করতে পুরানো স্টাইলের ক্লাস ব্যবহার করা হয়।

উদাহরণটি নতুন স্টাইলে পুনরায় লিখে আমি একই উদাহরণটি চেষ্টা করেছি তবে পুরানো স্টাইলের ক্লাসগুলির সাথে ফলাফলটি আলাদা নয়। উদাহরণটি চালাতে আমি যে পাইথন সংস্করণটি ব্যবহার করছি তা হ'ল 2.5.2। নীচে উদাহরণ দেওয়া হল:

class Base1(object):  
    def amethod(self): print "Base1"  

class Base2(Base1):  
    pass

class Base3(object):  
    def amethod(self): print "Base3"

class Derived(Base2,Base3):  
    pass

instance = Derived()  
instance.amethod()  
print Derived.__mro__  

কল instance.amethod()প্রিন্টগুলি Base1, তবে আমার এমআরও সম্পর্কে নতুন স্টাইলের ক্লাসের আউটপুটটি হওয়া উচিত Base3। কল Derived.__mro__প্রিন্ট:

(<class '__main__.Derived'>, <class '__main__.Base2'>, <class '__main__.Base1'>, <class '__main__.Base3'>, <type 'object'>)

আমি নিশ্চিত নই যে নতুন স্টাইলের ক্লাসগুলির সাথে এমআরও সম্পর্কে আমার বুঝতে ভুল হয়েছে বা আমি নির্লিপ্ত ভুল করছি যা আমি সনাক্ত করতে সক্ষম নই। এমআরও সম্পর্কে আরও ভাল বোঝার জন্য দয়া করে আমাকে সহায়তা করুন।

উত্তর:


186

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

>>> class A: x = 'a'
... 
>>> class B(A): pass
... 
>>> class C(A): x = 'c'
... 
>>> class D(B, C): pass
... 
>>> D.x
'a'

এখানে, উত্তরাধিকার-শৈলীতে, রেজোলিউশন অর্ডারটি ডি - বি - এ - সি - এ: তাই যখন Dx সন্ধান করবেন তখন এটিকে সমাধানের জন্য প্রথমে বেস হয়, যার ফলে সিটিতে সংজ্ঞাটি লুকানো হয়:

>>> class A(object): x = 'a'
... 
>>> class B(A): pass
... 
>>> class C(A): x = 'c'
... 
>>> class D(B, C): pass
... 
>>> D.x
'c'
>>> 

এখানে, নতুন ধাঁচের, ক্রমটি হল:

>>> D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, 
    <class '__main__.A'>, <type 'object'>)

সঙ্গে Aশুধুমাত্র একবার এবং তার উপশ্রেণী সব পরে রেজল্যুশন অনুক্রমে আসতে বাধ্য তাই ওভাররাইড (অর্থাত, সদস্য সি এর ওভাররাইড যে x) প্রকৃতপক্ষে বুদ্ধিমানের কাজ।

পুরানো স্টাইলের ক্লাসগুলি এড়ানো উচিত এটির অন্যতম কারণ: "হীরকের মতো" নিদর্শনগুলির সাথে একাধিক উত্তরাধিকার কেবল তাদের সাথে সংবেদনশীলভাবে কাজ করে না, যখন এটি নতুন শৈলীর সাথে ঘটে।


4
"[পূর্বপুরুষ শ্রেণি] এ [তার] সাবক্লাসগুলির কেবল একবার এবং পরে রেজোলিউশন আদেশে আসতে বাধ্য হয়, যাতে ওভাররাইড (যেমন, সি এর সদস্য x এর ওভাররাইড) সংবেদনশীলভাবে কাজ করে।" - এপিফ্যানি! এই বাক্যটির জন্য ধন্যবাদ, আমি আবার আমার মাথায় এমআরও করতে পারি। / o / আপনাকে অনেক ধন্যবাদ
এস্তেইস

25

পাইথনের পদ্ধতির রেজোলিউশন অর্ডারটি কেবল হীরার ধরণটি বোঝার চেয়ে জটিল। করতে সত্যিই এটা বুঝতে, কটাক্ষপাত করা C3 এ একরৈখিকরণ । আমি খুঁজে পেয়েছি এটি অর্ডার ট্র্যাক করার পদ্ধতিগুলি প্রসারিত করার সময় মুদ্রণ বিবৃতিগুলি ব্যবহার করতে সত্যই সহায়তা করে। উদাহরণস্বরূপ, আপনি কী ভাবেন যে এই প্যাটার্নটির আউটপুট হবে? (দ্রষ্টব্য: 'এক্স' মনে করা হয় দুটি ক্রসিং প্রান্ত হতে হবে, নোড নয় এবং methods এমন পদ্ধতিগুলি নির্দেশ করে যা সুপারকে কল করে) ())

class G():
    def m(self):
        print("G")

class F(G):
    def m(self):
        print("F")
        super().m()

class E(G):
    def m(self):
        print("E")
        super().m()

class D(G):
    def m(self):
        print("D")
        super().m()

class C(E):
    def m(self):
        print("C")
        super().m()

class B(D, E, F):
    def m(self):
        print("B")
        super().m()

class A(B, C):
    def m(self):
        print("A")
        super().m()


#      A^
#     / \
#    B^  C^
#   /| X
# D^ E^ F^
#  \ | /
#    G

আপনি ABDCEFG পেয়েছেন?

x = A()
x.m()

অনেক পরীক্ষার পরেও ত্রুটি হয়েছে, আমি সি 3 লিনিয়ারীকরণের একটি অনানুষ্ঠানিক গ্রাফ তত্ত্বের ব্যাখ্যাটি নিয়ে এলাম: (কেউ দয়া করে আমাকে জানাবেন যে এটি ভুল কিনা))

এই উদাহরণ বিবেচনা করুন:

class I(G):
    def m(self):
        print("I")
        super().m()

class H():
    def m(self):
        print("H")

class G(H):
    def m(self):
        print("G")
        super().m()

class F(H):
    def m(self):
        print("F")
        super().m()

class E(H):
    def m(self):
        print("E")
        super().m()

class D(F):
    def m(self):
        print("D")
        super().m()

class C(E, F, G):
    def m(self):
        print("C")
        super().m()

class B():
    def m(self):
        print("B")
        super().m()

class A(B, C, D):
    def m(self):
        print("A")
        super().m()

# Algorithm:

# 1. Build an inheritance graph such that the children point at the parents (you'll have to imagine the arrows are there) and
#    keeping the correct left to right order. (I've marked methods that call super with ^)

#          A^
#       /  |  \
#     /    |    \
#   B^     C^    D^  I^
#        / | \  /   /
#       /  |  X    /   
#      /   |/  \  /     
#    E^    F^   G^
#     \    |    /
#       \  |  / 
#          H
# (In this example, A is a child of B, so imagine an edge going FROM A TO B)

# 2. Remove all classes that aren't eventually inherited by A

#          A^
#       /  |  \
#     /    |    \
#   B^     C^    D^
#        / | \  /  
#       /  |  X    
#      /   |/  \ 
#    E^    F^   G^
#     \    |    /
#       \  |  / 
#          H

# 3. For each level of the graph from bottom to top
#       For each node in the level from right to left
#           Remove all of the edges coming into the node except for the right-most one
#           Remove all of the edges going out of the node except for the left-most one

# Level {H}
#
#          A^
#       /  |  \
#     /    |    \
#   B^     C^    D^
#        / | \  /  
#       /  |  X    
#      /   |/  \ 
#    E^    F^   G^
#               |
#               |
#               H

# Level {G F E}
#
#         A^
#       / |  \
#     /   |    \
#   B^    C^   D^
#         | \ /  
#         |  X    
#         | | \
#         E^F^ G^
#              |
#              |
#              H

# Level {D C B}
#
#      A^
#     /| \
#    / |  \
#   B^ C^ D^
#      |  |  
#      |  |    
#      |  |  
#      E^ F^ G^
#            |
#            |
#            H

# Level {A}
#
#   A^
#   |
#   |
#   B^  C^  D^
#       |   |
#       |   |
#       |   |
#       E^  F^  G^
#               |
#               |
#               H

# The resolution order can now be determined by reading from top to bottom, left to right.  A B C E D F G H

x = A()
x.m()

আপনার দ্বিতীয় কোডটি সংশোধন করা উচিত: আপনি "আই" ক্লাসটিকে প্রথম লাইন হিসাবে রেখেছেন এবং সুপারও ব্যবহার করেছেন সুতরাং এটি সুপার জি ক্লাস "জি" খুঁজে পেয়েছে তবে "আমি" প্রথম শ্রেণি তাই এটি "জি" শ্রেণিটি কখনই খুঁজে পাবে না কারণ সেখানে কোনও "G" উপরের "আমি" নয়। "G" এবং "F" এর মধ্যে "I" ক্লাস রাখুন :)
আদিত্য উরা

উদাহরণ কোডটি ভুল। superপ্রয়োজনীয় যুক্তি রয়েছে।
ড্যানি

4
শ্রেণীর সংজ্ঞার ভিতরে সুপার () এর পক্ষে আর্গুমেন্টের প্রয়োজন হয় না। Https://docs.python.org/3/library/function.html#super
বেন

আপনার গ্রাফ তত্ত্বটি অযথা জটিল। পদক্ষেপ 1 এর পরে, বাম দিকের ক্লাস থেকে ডানদিকে ক্লাসগুলি (যে কোনও উত্তরাধিকার তালিকায়) সন্নিবেশ করান এবং তারপরে একটি টপোলজিকাল সাজান এবং আপনার কাজ শেষ।
কেভিন

@ কেভিন আমি এটিকে সঠিক মনে করি না। আমার উদাহরণ অনুসরণ করে, ACDBEFGH একটি বৈধ টপোলজিকাল সাজান না? তবে এটি রেজুলেশন অর্ডার নয়।
বেন

5

আপনি যে ফলাফলটি পেয়েছেন তা সঠিক। বেস বর্গ পরিবর্তন করার চেষ্টা করুন Base3থেকে Base1এবং ক্লাসিক শ্রেণীর জন্য একই অনুক্রমের সঙ্গে তুলনা:

class Base1(object):
    def amethod(self): print "Base1"

class Base2(Base1):
    pass

class Base3(Base1):
    def amethod(self): print "Base3"

class Derived(Base2,Base3):
    pass

instance = Derived()
instance.amethod()


class Base1:
    def amethod(self): print "Base1"

class Base2(Base1):
    pass

class Base3(Base1):
    def amethod(self): print "Base3"

class Derived(Base2,Base3):
    pass

instance = Derived()
instance.amethod()

এখন এটি ফলাফল:

Base3
Base1

আরও তথ্যের জন্য এই ব্যাখ্যা পড়ুন ।


1

আপনি সেই আচরণটি দেখছেন কারণ পদ্ধতির রেজোলিউশন গভীরতা-প্রথম, প্রস্থের প্রথম নয়। ডেরভিডের উত্তরাধিকার দেখে মনে হচ্ছে

         Base2 -> Base1
        /
Derived - Base3

তাই instance.amethod()

  1. বেস 2 চেক করে, অমোথড খুঁজে পাচ্ছে না।
  2. দেখুন যে বেস 2 বেস 1 থেকে উত্তরাধিকার সূত্রে পেয়েছে এবং বেস 1 পরীক্ষা করে। বেস 1 এর একটি রয়েছে amethod, তাই এটি কল হয়ে যায়।

এটি প্রতিফলিত হয় Derived.__mro__। কেবল পুনরাবৃত্তি করুন Derived.__mro__এবং যখন আপনি পদ্ধতিটি সন্ধান করছেন তা পেয়ে যান stop


আমি সন্দেহ করি যে আমি উত্তর হিসাবে "বেস 1" পাবার কারণটি পদ্ধতিটির সমাধান গভীরতা-প্রথম, আমি মনে করি এটির গভীরতা-প্রথম পদ্ধতির চেয়ে আরও কিছু আছে। ডেনিসের উদাহরণটি দেখুন, যদি এটি গভীরতা হয় তবে প্রথমে o / p "বেস 1" হওয়া উচিত ছিল। এছাড়াও আপনি সরবরাহ করেছেন এমন লিঙ্কের প্রথম উদাহরণটি দেখুন, সেখানে দেখানো এমআরও ইঙ্গিত দেয় যে পদ্ধতিটির রেজোলিউশন কেবল গভীরতা-প্রথম ক্রমে অনুসরণ করেই নির্ধারিত হয় না।
সতীশ

দুঃখিত, এমআরও-তে নথির লিঙ্কটি ডেনিস সরবরাহ করেছেন। দয়া করে এটি যাচাই করুন, আমি ভ্রান্ত হয়েছি যে আপনি আমাকে পাইথন.অর্গের লিঙ্কটি সরবরাহ করেছেন।
সতীশ

4
এটি সাধারণত গভীরতা-প্রথম, তবে অ্যালেক্সের ব্যাখ্যা অনুসারে হীরা-জাতীয় উত্তরাধিকার পরিচালনা করার জন্য স্মার্ট রয়েছে।
জামেসান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.