पायथन में एब्सट्रैक्ट बेस क्लास का उपयोग क्यों करें?


213

क्योंकि मैं अजगर में बतख टाइपिंग के पुराने तरीकों के लिए उपयोग किया जाता हूं, मैं एबीसी (अमूर्त आधार वर्ग) की आवश्यकता को समझने में विफल हूं। इनका उपयोग कैसे किया जाए, इस पर मदद अच्छी है।

मैंने पीईपी में तर्क को पढ़ने की कोशिश की , लेकिन यह मेरे सिर पर चला गया। यदि मैं एक उत्परिवर्तनीय अनुक्रम कंटेनर की तलाश में था, तो मैं __setitem__इसे ( EAFP ) उपयोग करने की कोशिश करूंगा । मैं संख्या मॉड्यूल के लिए एक वास्तविक जीवन का उपयोग नहीं कर रहा हूं , जो एबीसी का उपयोग करता है, लेकिन यह सबसे करीब है जिसे मुझे समझना होगा।

क्या कोई मुझे औचित्य समझा सकता है, कृपया?

जवाबों:


162

लघु संस्करण

एबीसी ग्राहकों और कार्यान्वित वर्गों के बीच उच्च स्तर के शब्दार्थ अनुबंध की पेशकश करते हैं।

दीर्घ संस्करण

एक वर्ग और इसके कॉल करने वालों के बीच एक अनुबंध होता है। वर्ग कुछ चीजें करने का वादा करता है और कुछ गुण रखता है।

अनुबंध के विभिन्न स्तर हैं।

बहुत निचले स्तर पर, अनुबंध में एक विधि का नाम या इसके मापदंडों की संख्या शामिल हो सकती है।

स्थिर रूप से टाइप की गई भाषा में, वह अनुबंध वास्तव में कंपाइलर द्वारा लागू किया जाएगा। पायथन में, आप ईएएफपी का उपयोग कर सकते हैं पुष्टि करने के लिए कि यह अपेक्षित अनुबंध मिलता है, पुष्टि करने के लिए या टाइप आत्मनिरीक्षण का ।

लेकिन अनुबंध में उच्च-स्तरीय, शब्दार्थ वादे भी हैं।

उदाहरण के लिए, यदि कोई __str__()विधि है, तो यह ऑब्जेक्ट के एक स्ट्रिंग प्रतिनिधित्व को वापस करने की उम्मीद है। यह ऑब्जेक्ट की सभी सामग्री को हटा सकता है , लेन-देन कर सकता है और प्रिंटर से एक खाली पृष्ठ थूक सकता है ... लेकिन पायथन मैनुअल में वर्णित यह क्या करना चाहिए, इसकी एक सामान्य समझ है।

यह एक विशेष मामला है, जहां मैनुअल में सिमेंटिक अनुबंध का वर्णन किया गया है। print()विधि क्या करना चाहिए ? क्या यह एक प्रिंटर या स्क्रीन के लिए एक लाइन, या कुछ और के लिए वस्तु लिखना चाहिए? यह निर्भर करता है - आपको यहां पूर्ण अनुबंध को समझने के लिए टिप्पणियों को पढ़ने की आवश्यकता है। ग्राहक कोड का एक टुकड़ा जो बस जांचता है किprint() विधि मौजूद है, अनुबंध का हिस्सा है - एक विधि कॉल की पुष्टि की जा सकती है, लेकिन ऐसा नहीं है कि कॉल के उच्च स्तर के शब्दार्थ पर सहमति है।

एब्सट्रैक्ट बेस क्लास (एबीसी) को परिभाषित करना क्लास के कार्यान्वयनकर्ताओं और कॉल करने वालों के बीच एक अनुबंध बनाने का एक तरीका है। यह केवल विधि नामों की सूची नहीं है, बल्कि उन तरीकों का एक साझा बोध होना चाहिए। यदि आप इस एबीसी से विरासत में लेते हैं, तो आप print()विधि के शब्दार्थ सहित टिप्पणियों में वर्णित सभी नियमों का पालन करने का वादा कर रहे हैं ।

स्थैतिक-टाइपिंग से अधिक लचीलेपन में पायथन के डक-टाइपिंग के कई फायदे हैं, लेकिन यह सभी समस्याओं को हल नहीं करता है। एबीसी पायथन के मुक्त रूप और एक स्थिर टाइप की भाषा के बंधन-और-अनुशासन के बीच एक मध्यवर्ती समाधान प्रदान करते हैं।


12
मुझे लगता है कि आपके पास एक बिंदु है, लेकिन मैं आपका अनुसरण नहीं कर सकता। तो क्या अंतर है, अनुबंध के संदर्भ में, एक वर्ग के बीच जो लागू करता है __contains__और एक वर्ग जो विरासत में मिला है collections.Container? आपके उदाहरण में, पायथन में हमेशा एक साझा समझ थी __str__। लागू करना __str__कुछ एबीसी से विरासत के रूप में एक ही वादे करता है और फिर लागू करना __str__। दोनों मामलों में आप अनुबंध को तोड़ सकते हैं; स्थैतिक टाइपिंग में हमारे पास कोई सिद्ध शब्द नहीं है।
मुहम्मद अल्करौरी

15
collections.Containerएक पतित मामला है, जिसमें केवल शामिल है \_\_contains\_\_, और केवल पूर्वनिर्धारित सम्मेलन का मतलब है। ABC का उपयोग करना अपने आप में बहुत अधिक मूल्य नहीं जोड़ता है, मैं सहमत हूं। मुझे संदेह है कि इसे (उदाहरण के लिए) Setसे विरासत में मिला था। जब तक आप Setएबीसी से संबंधित होते हैं, तब तक काफी शब्दार्थ होता है। एक आइटम दो बार संग्रह से संबंधित नहीं हो सकता। यह तरीकों के अस्तित्व से पता लगाने योग्य नहीं है।
Oddthinking

3
हां, मुझे लगता Setहै कि इससे बेहतर उदाहरण है print()। मैं एक विधि का नाम खोजने का प्रयास कर रहा था जिसका अर्थ अस्पष्ट था, और अकेले नाम से नहीं टटोला जा सकता था, इसलिए आप यह सुनिश्चित नहीं कर सकते कि यह अपने नाम और पायथन मैनुअल द्वारा ही सही काम करेगा।
Oddthinking

3
Setइसके बजाय उदाहरण के साथ उत्तर को फिर से लिखने का कोई मौका print? Setबहुत समझदारी है, @Oddthinking।
एहतेश चौधरी

1
मुझे लगता है कि यह लेख इसे बहुत अच्छी तरह से समझाता है: dbader.org/blog/abstract-base-classes-in-python
szabgab

228

@ ओडिन्थंकिंग का उत्तर गलत नहीं है, लेकिन मुझे लगता है कि यह वास्तविक , व्यावहारिक कारण याद करता है , अजगर टाइपिंग की दुनिया में एबीसी है।

सार विधियां साफ-सुथरी हैं, लेकिन मेरी राय में वे वास्तव में डक टाइपिंग द्वारा कवर किए गए किसी भी उपयोग के मामलों को नहीं भरती हैं। सार आधार वर्ग 'में वास्तविक शक्ति निहित है जिस तरह से वे आप के व्यवहार को अनुकूलित करने के लिए अनुमति देते हैं isinstanceऔरissubclass । ( __subclasshook__मूल रूप से पायथन __instancecheck__और__subclasscheck__ हुक के शीर्ष पर एक फ्रेंडली एपीआई है ।) कस्टम प्रकारों पर काम करने के लिए निर्मित निर्माणों को अपनाना पायथन के दर्शन का बहुत हिस्सा है।

पायथन का स्रोत कोड अनुकरणीय है। यहाँ बताया collections.Containerगया है कि मानक पुस्तकालय में कैसे लिखा जाता है (लेखन के समय):

class Container(metaclass=ABCMeta):
    __slots__ = ()

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if any("__contains__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

इस परिभाषा में __subclasshook__कहा गया है कि किसी भी वर्ग की __contains__विशेषता को कंटेनर का उपवर्ग माना जाता है, भले ही वह इसे सीधे उप-वर्ग न करे। इसलिए मैं यह लिख सकता हूं:

class ContainAllTheThings(object):
    def __contains__(self, item):
        return True

>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True

दूसरे शब्दों में, यदि आप सही इंटरफ़ेस लागू करते हैं तो आप एक उपवर्ग हैं! एबीसी, पाइकॉन में इंटरफेस को परिभाषित करने का एक औपचारिक तरीका प्रदान करते हैं, जबकि बतख-टाइपिंग की भावना के लिए सही रहते हैं। इसके अलावा, यह एक तरह से काम करता है जो ओपन-क्लोज्ड सिद्धांत का सम्मान करता है ।

पायथन का ऑब्जेक्ट मॉडल सतही रूप से "अधिक" पारंपरिक "OO प्रणाली (जिससे मैं जावा * का अर्थ है) के समान दिखता हूं - हमें yer कक्षाएं, yer ऑब्जेक्ट्स, yer तरीके मिले - लेकिन जब आप सतह को खरोंच करते हैं तो आपको कुछ और समृद्ध और मिलेंगे।" अधिक लचीला। इसी तरह, पायथन की अमूर्त आधार कक्षाओं की धारणा एक जावा डेवलपर के लिए पहचानी जा सकती है, लेकिन व्यवहार में वे एक बहुत ही अलग उद्देश्य के लिए अभिप्रेत हैं।

मैं कभी-कभी खुद को बहुरूपतापूर्ण कार्य लिखने के लिए पाता हूं जो किसी एक आइटम या वस्तुओं के संग्रह पर कार्य कर सकता है, और मुझे लगता है isinstance(x, collections.Iterable)hasattr(x, '__iter__') एक समान try...exceptब्लॉक की तुलना में बहुत अधिक पठनीय लगता है । (यदि आप पायथन को नहीं जानते हैं, तो उन तीनों में से कौन सा कोड का इरादा स्पष्ट करेगा?)

उस ने कहा, मुझे लगता है कि मुझे शायद ही कभी अपनी एबीसी लिखने की जरूरत है और मैं आमतौर पर रिफैक्टिंग के माध्यम से एक की आवश्यकता की खोज करता हूं। अगर मैं एक बहुरूपिक फ़ंक्शन को बहुत अधिक विशेषता जांच करता हुआ देखता हूं, या बहुत सारे कार्य एक ही विशेषता जांच करता है, तो गंध एक एबीसी के अस्तित्व को निकालने का इंतजार करने का सुझाव देता है।

* इस बात पर बहस किए बिना कि क्या जावा एक "पारंपरिक" OO प्रणाली है ...


परिशिष्ट : भले ही एक सार आधार वर्ग के व्यवहार को ओवरराइड कर सकता है isinstanceऔर issubclass, यह अभी भी वर्चुअल उपवर्ग के MRO में प्रवेश नहीं करता है । यह ग्राहकों के लिए एक संभावित नुकसान है: प्रत्येक वस्तु जिसके isinstance(x, MyABC) == Trueलिए विधियों को परिभाषित नहीं किया गया है MyABC

class MyABC(metaclass=abc.ABCMeta):
    def abc_method(self):
        pass
    @classmethod
    def __subclasshook__(cls, C):
        return True

class C(object):
    pass

# typical client code
c = C()
if isinstance(c, MyABC):  # will be true
    c.abc_method()  # raises AttributeError

दुर्भाग्य से उन लोगों में से एक "बस ऐसा नहीं करता है" जाल (जिनमें से पायथन अपेक्षाकृत कम है!): एबीसी __subclasshook__और गैर-सार दोनों तरीकों से एबीसी को परिभाषित करने से बचें । इसके अलावा, आपको __subclasshook__अपने एबीसी परिभाषित अमूर्त तरीकों के सेट के अनुरूप अपनी परिभाषा बनानी चाहिए ।


21
"यदि आप सही इंटरफ़ेस लागू करते हैं, तो आप एक उपवर्ग हैं" इसके लिए बहुत बहुत धन्यवाद। मुझे नहीं पता कि क्या ओडिन्थिंग ने इसे याद किया, लेकिन मैंने निश्चित रूप से किया। FWIW, isinstance(x, collections.Iterable)मेरे लिए स्पष्ट है, और मैं पायथन को जानता हूं।
मुहम्मद अलकरौरी

बहुत बढ़िया पोस्ट। धन्यवाद। मुझे लगता है कि परिशिष्ट, "बस ऐसा न करें" जाल, थोड़ा सा सामान्य उपवर्ग विरासत करने जैसा है, लेकिन फिर Cउपवर्ग को नष्ट करना (या मरम्मत से परे पेंच) से abc_method()विरासत में मिला है MyABC। मुख्य अंतर यह है कि यह सुपरक्लास है जो विरासत अनुबंध को खराब कर रहा है, न कि उपवर्ग।
माइकल स्कॉट कुथबर्ट

क्या आपको Container.register(ContainAllTheThings)दिए गए उदाहरण के लिए काम नहीं करना पड़ेगा ?
BoZenKhaa

1
@BoZenKhaa उत्तर में कोड काम करता है! कोशिश करो! का अर्थ __subclasshook__है "कोई भी वर्ग जो इस विधेय को संतुष्ट करता है, को एबीसी के साथ पंजीकृत किया गया था या नहीं , इसके प्रयोजनों isinstanceऔर issubclassजांच के लिए एक उपवर्ग माना जाता है , और क्या यह एक प्रत्यक्ष उपवर्ग है की परवाह किए बिना "। जैसा कि मैंने उत्तर में कहा, यदि आप सही इंटरफ़ेस लागू करते हैं
बेंजामिन हॉजसन

1
आपको इटैलिक्स से स्वरूप को बोल्ड में बदलना चाहिए। "यदि आप सही इंटरफ़ेस लागू करते हैं, तो आप एक उपवर्ग हैं!" बहुत संक्षिप्त व्याख्या। धन्यवाद!
मार्टी

108

एबीसी की एक विशेषता यह है कि यदि आप सभी आवश्यक तरीकों (और गुणों) को लागू नहीं करते हैं, तो आपको तत्काल के बजाय एक त्रुटि मिलती है AttributeError, संभावित रूप से बहुत बाद में, जब आप वास्तव में लापता पद्धति का उपयोग करने का प्रयास करते हैं।

from abc import ABCMeta, abstractmethod

# python2
class Base(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

# python3
class Base(object, metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass

    @abstractmethod
    def bar(self):
        pass

class Concrete(Base):
    def foo(self):
        pass

    # We forget to declare `bar`


c = Concrete()
# TypeError: "Can't instantiate abstract class Concrete with abstract methods bar"

उदाहरण से https://dbader.org/blog/abstract-base-classes-in-python से

संपादित करें: python3 सिंटैक्स को शामिल करने के लिए, धन्यवाद @ PandasRocks


5
उदाहरण और लिंक दोनों बहुत मददगार थे। धन्यवाद!
nalyd88

यदि आधार को एक अलग फ़ाइल में परिभाषित किया गया है, तो आपको इसे Base.Base से इनहेरिट करना होगा या आयात लाइन को 'आधार आयात बेस से' में
बदलना होगा

यह ध्यान दिया जाना चाहिए कि पायथन 3 में वाक्य रचना थोड़ा अलग है। इस उत्तर को देखें: stackoverflow.com/questions/28688784/…
PandasRocks

7
एक सी # पृष्ठभूमि से आ रहा है, यह है कारण सार वर्गों का उपयोग करने के। आप कार्यक्षमता प्रदान कर रहे हैं, लेकिन यह बताते हुए कि कार्यक्षमता को और क्रियान्वयन की आवश्यकता है। अन्य उत्तर इस बिंदु को याद करने लगते हैं।
जोश नू

18

यह निर्धारित करेगा कि क्या कोई ऑब्जेक्ट प्रोटोकॉल में सभी तरीकों की उपस्थिति की जांच किए बिना या बिना अधिक समर्थन के कारण "दुश्मन" क्षेत्र में एक अपवाद को गहराई से ट्रिगर किए बिना एक आसान प्रोटोकॉल का समर्थन करता है।


6

एब्सट्रैक्ट मेथड यह सुनिश्चित करता है कि आप पैरेंट क्लास में किस तरीके से कॉल कर रहे हैं, चाइल्ड क्लास में दिखना है। नीचे अमूर्त को बुलाने और उपयोग करने का तरीका है। पायथन 3 में लिखा गया कार्यक्रम

कॉल करने का सामान्य तरीका

class Parent:
def methodone(self):
    raise NotImplemented()

def methodtwo(self):
    raise NotImplementedError()

class Son(Parent):
   def methodone(self):
       return 'methodone() is called'

c = Son()
c.methodone()

'मेथडोन () कहा जाता है

c.methodtwo()

NotImplementedError

सार विधि के साथ

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'

c = Son()

TypeError: अमूर्त वर्ग बेटा नहीं कर सकता अमूर्त तरीकों के साथ बेटा methodtwo।

चूँकि मेथडोवो को चाइल्ड क्लास में नहीं कहा जाता है इसलिए हमें त्रुटि हुई। उचित कार्यान्वयन नीचे है

from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def methodone(self):
        raise NotImplementedError()
    @abstractmethod
    def methodtwo(self):
        raise NotImplementedError()

class Son(Parent):
    def methodone(self):
        return 'methodone() is called'
    def methodtwo(self):
        return 'methodtwo() is called'

c = Son()
c.methodone()

'मेथडोन () कहा जाता है


1
धन्यवाद। क्या एक ही बिंदु ऊपर दिए गए सेर्बेरोज़ में नहीं है?
मुहम्मद अलकरौरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.