পাইথনে অ্যাবস্ট্রাক্ট বেস ক্লাস কেন ব্যবহার করবেন?


213

পাইথনে হাঁসের টাইপিংয়ের পুরানো পদ্ধতিগুলিতে আমি অভ্যস্ত থাকায় আমি এবিসির (বিমূর্ত বেস ক্লাস) প্রয়োজনীয়তা বুঝতে ব্যর্থ হয়েছি। সেগুলি কীভাবে ব্যবহার করা যায় সে সম্পর্কে সহায়তা ভাল।

আমি পিইপিতে যুক্তিটি পড়ার চেষ্টা করেছি , তবে এটি আমার মাথায় চলে গেছে। যদি আমি কোনও পরিবর্তনীয় সিকোয়েন্সের ধারকটি সন্ধান করতাম তবে আমি এটি পরীক্ষা করতাম __setitem__বা সম্ভবত এটি ( ইএএফপি ) ব্যবহার করার চেষ্টা করতাম । আমি সংখ্যার মডিউলের জন্য বাস্তব জীবনের ব্যবহার করতে পারিনি , যা এবিসি ব্যবহার করে, তবে এটি আমার কাছে সবচেয়ে কাছের।

দয়া করে কেউ কি আমাকে যুক্তি ব্যাখ্যা করতে পারেন?

উত্তর:


162

সংক্ষিপ্ত সংস্করণ

এবিসি ক্লায়েন্ট এবং বাস্তবায়িত ক্লাসগুলির মধ্যে উচ্চ স্তরের শব্দার্থক চুক্তির অফার দেয়।

দীর্ঘ সংস্করণ

একটি শ্রেণি এবং তার কলকারীদের মধ্যে একটি চুক্তি রয়েছে। শ্রেণি নির্দিষ্ট কিছু করার প্রতিশ্রুতি দেয় এবং নির্দিষ্ট বৈশিষ্ট্য রাখে।

চুক্তির বিভিন্ন স্তর রয়েছে।

খুব নিম্ন স্তরে, চুক্তিতে কোনও পদ্ধতির নাম বা তার পরামিতিগুলির সংখ্যা অন্তর্ভুক্ত থাকতে পারে।

একটি স্থির-টাইপিত ভাষায়, চুক্তিটি আসলে সংকলক দ্বারা কার্যকর করা হবে। পাইথনে, আপনি ইএএফপি ব্যবহার করতে পারেন বা আত্মপরিচয় টাইপ করতে পারেন তা নিশ্চিত করার জন্য যে অজানা অবজেক্টটি এই প্রত্যাশিত চুক্তিটি পূরণ করেছে।

তবে চুক্তিতে উচ্চ-স্তরের, শব্দাবদ্ধ প্রতিশ্রুতিও রয়েছে।

উদাহরণস্বরূপ, যদি কোনও __str__()পদ্ধতি থাকে তবে এটি প্রত্যাশার একটি স্ট্রিং প্রতিনিধিত্ব প্রত্যাশা করে। এটি অবজেক্টের সমস্ত বিষয়বস্তু মুছতে পারে , লেনদেন করতে পারে এবং প্রিন্টারের বাইরে একটি ফাঁকা পৃষ্ঠা ছিটিয়ে দেয় ... তবে পাইথন ম্যানুয়ালটিতে বর্ণিত এটি কী করা উচিত তা সম্পর্কে সাধারণ ধারণা রয়েছে।

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

অ্যাবস্ট্রাক্ট বেস ক্লাস (এবিসি) সংজ্ঞায়িত করা শ্রেণি প্রয়োগকারী এবং কলকারীদের মধ্যে একটি চুক্তি উত্পাদন করার একটি উপায়। এটি কেবল পদ্ধতির নামের তালিকা নয়, তবে এই পদ্ধতিগুলি কী করা উচিত তার একটি ভাগ করে নেওয়া বোঝা। আপনি যদি এই এবিসি থেকে উত্তরাধিকারী হন তবে আপনি মন্তব্যগুলির মধ্যে বর্ণিত সমস্ত বিধিগুলি অনুসরণ করার প্রতিশ্রুতি দিচ্ছেন, এর শব্দার্থক সহprint() পদ্ধতির ।

পাইথনের হাঁস-টাইপিংয়ের স্ট্যাটিক-টাইপিংয়ের চেয়ে নমনীয়তার অনেক সুবিধা রয়েছে তবে এটি সমস্ত সমস্যার সমাধান করে না। এবিসিরা পাইথনের মুক্ত-রূপ এবং একটি স্থির-টাইপিত ভাষার বন্ধন এবং শৃঙ্খলার মধ্যে একটি মধ্যবর্তী সমাধান সরবরাহ করে।


12
আমি মনে করি যে আপনার সেখানে একটি বক্তব্য আছে, তবে আমি আপনাকে অনুসরণ করতে পারি না। সুতরাং চুক্তির শর্তাবলী, প্রয়োগকারী __contains__শ্রেণি এবং উত্তরাধিকার সূত্রে প্রাপ্ত একটি শ্রেণীর মধ্যে কী পার্থক্য রয়েছে collections.Container? আপনার উদাহরণস্বরূপ, পাইথনে সর্বদা একটি ভাগ করা বোঝা ছিল __str__। বাস্তবায়ন __str__কিছু এবিসির উত্তরাধিকার সূত্রে এবং তারপরে বাস্তবায়ন হিসাবে একই প্রতিশ্রুতি দেয় __str__। উভয় ক্ষেত্রেই আপনি চুক্তিটি ভঙ্গ করতে পারেন; স্ট্যাটিক টাইপিংয়ের মতো কোনও প্রমাণযোগ্য শব্দার্থবিজ্ঞান নেই।
মুহাম্মদ আলকারৌরি

15
collections.Containerএকটি হ্রাসযুক্ত কেস, এটি কেবলমাত্র অন্তর্ভুক্ত করে \_\_contains\_\_এবং কেবলমাত্র পূর্বনির্ধারিত কনভেনশন বোঝায়। একটি এবিসি ব্যবহার করা নিজের দ্বারা খুব বেশি মূল্য যুক্ত করে না, আমি সম্মত। আমার সন্দেহ হয় এটি Setথেকে উত্তরাধিকারী হওয়ার জন্য (উদাহরণস্বরূপ) এটি যুক্ত করা হয়েছিল। আপনি যে সময়টি পেয়েছেন Set, হঠাৎ করে এবিসির অন্তর্গত যথেষ্ট শব্দার্থক শব্দ রয়েছে। কোনও আইটেম দুইবার সংগ্রহের সাথে সম্পর্কিত হতে পারে না। এটি পদ্ধতির অস্তিত্ব দ্বারা সনাক্তযোগ্য নয়।
26:55

3
হ্যাঁ, আমি মনে করি এর Setচেয়ে ভাল উদাহরণ print()। আমি এমন একটি পদ্ধতির নাম সন্ধান করার চেষ্টা করছিলাম যার অর্থ অস্পষ্ট ছিল এবং একা নামটি দিয়ে ছাঁটাই করা যায় না, তাই আপনি নিশ্চিত হতে পারবেন না যে এটি ঠিক তার নাম এবং পাইথন ম্যানুয়াল দ্বারা সঠিক কাজ করবে।
অদ্ভুতভাবে ভাবনা

3
Setপরিবর্তে উদাহরণ হিসাবে উত্তর দিয়ে পুনরায় লেখার কোন সুযোগ print? Set@ অডডিংকিং, প্রচুর অর্থ তৈরি করে।
এহতেশ চৌধুরী

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

অন্য কথায়, আপনি যদি সঠিক ইন্টারফেস প্রয়োগ করেন তবে আপনি একটি সাবক্লাস! হাঁস-টাইপিংয়ের মনোভাবের প্রতি সত্য হয়ে ওঠার সাথে সাথে এবিসি পাইথনে ইন্টারফেসগুলি সংজ্ঞায়িত করার আনুষ্ঠানিক উপায় সরবরাহ করে। তদ্ব্যতীত, এটি এমনভাবে কাজ করে যা ওপেন-বদ্ধ নীতিটিকে সম্মান করে ।

পাইথনের অবজেক্টের মডেল দেখতে আরও বেশি "traditionalতিহ্যবাহী" ওও সিস্টেমের (যেমনটি আমি জাভা *) এর সাথে অনুরূপ দেখতে পেয়েছি - আমরা ইয়ার ক্লাস, ইয়ার অবজেক্টস, ইয়ার পদ্ধতিগুলি পেয়েছি - তবে আপনি যখন পৃষ্ঠটি স্ক্র্যাচ করবেন তখন আপনি আরও সমৃদ্ধ কিছু খুঁজে পাবেন এবং আরো নমনীয়. তেমনি, অ্যাজস্ট্রাক্ট বেস ক্লাসগুলির পাইথনের ধারণাটি কোনও জাভা বিকাশকারীকে স্বীকৃত হতে পারে, তবে বাস্তবে তারা একে অন্যরকম উদ্দেশ্যে উদ্দিষ্ট।

আমি মাঝে মাঝে নিজেকে পলিমারফিক ফাংশনগুলি লিখতে দেখি যা একক আইটেম বা আইটেমের সংকলনে কাজ isinstance(x, collections.Iterable)করতে পারে এবং আমি তার চেয়ে অনেক বেশি পঠনযোগ্য hasattr(x, '__iter__')বা সমতুল্য try...exceptব্লক বলে মনে করি। (যদি আপনি পাইথন না জানতেন তবে এই তিনজনের মধ্যে কোনটি কোডটির উদ্দেশ্যটি পরিষ্কার করে দেবে?)

এটি বলেছিল, আমি দেখতে পেয়েছি যে আমার নিজের কদাচিৎ আমার নিজের এবিসি লিখতে হবে এবং আমি সাধারণত রিফ্যাক্টরিংয়ের মাধ্যমে একটিটির প্রয়োজনীয়তা আবিষ্কার করি। যদি আমি দেখতে পাই যে বহুগুণিত ক্রিয়াকলাপটি অনেকগুলি অ্যাট্রিবিউট চেক তৈরি করে, বা একই বৈশিষ্ট্যযুক্ত চেক তৈরি করে প্রচুর ফাংশন, তবে এই গন্ধটি উত্তোলনের জন্য অপেক্ষা করা একটি এবিসির অস্তিত্বের পরামর্শ দেয়।

* জাভা একটি "traditionalতিহ্যবাহী" ওও সিস্টেম কিনা তা নিয়ে বিতর্কে না গিয়ে ...


সংযোজন : এমনকি যদিও একটি বিমূর্ত বেস ক্লাসের আচরণ পাল্টাতে পারেন isinstanceএবং issubclass, এটা এখনও প্রবেশ করে না ম্রো ভার্চুয়াল উপশ্রেণী করুন। এটি ক্লায়েন্টদের জন্য একটি সম্ভাব্য সমস্যা: প্রতিটি বস্তুর জন্য নয় যার জন্য 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
"আপনি যদি সঠিক ইন্টারফেস প্রয়োগ করেন তবে আপনি একটি সাবক্লাস" এর জন্য আপনাকে অনেক ধন্যবাদ। আমি জানি না অডথিংকিং এটি মিস করেছে কিনা তবে আমি অবশ্যই তা করেছি। এফডাব্লুআইডাব্লু, isinstance(x, collections.Iterable)আমার পক্ষে আরও পরিষ্কার এবং আমি পাইথনকে জানি।
মুহাম্মদ আলকারৌরি

দুর্দান্ত পোস্ট। ধন্যবাদ. আমি মনে করি যে অ্যাডেন্ডাম, "কেবল এটি করবেন না" ফাঁদটি কিছুটা সাবক্লাসের উত্তরাধিকার হিসাবে করার মতো তবে তারপরে প্রাপ্ত উত্তরাধিকার সূত্রে Cসাবক্লাসটি মুছে ফেলা (বা মেরামতির বাইরেও স্ক্রু করা) । মূল পার্থক্য হ'ল এটি সুপারক্লাস যা উত্তরাধিকারের চুক্তিটি স্ক্রু করছে, সাবক্লাসটি নয়। abc_method()MyABC
মাইকেল স্কট কুথবার্ট

Container.register(ContainAllTheThings)কাজের জন্য প্রদত্ত উদাহরণের জন্য আপনাকে কি করতে হবে না ?
বোজেনখা

1
@ বোজেনখা উত্তরে কোডটি কাজ করে! চেষ্টা করে দেখুন! অর্থ __subclasshook__"কোনো বর্গ যা সন্তুষ্ট এই বিধেয় উদ্দেশ্যে একটি উপশ্রেণী হিসেবে গণ্য করা হয় হয় isinstanceএবং issubclassচেক, কিনা এটা এবিসি সাথে নিবন্ধিত ছিল নির্বিশেষে , এবং তা সরাসরি উপশ্রেণী এর নির্বিশেষে "। আমি উত্তরে যেমন বলেছি, আপনি যদি সঠিক ইন্টারফেসটি প্রয়োগ করেন তবে আপনি একটি সাবক্লাস!
বেঞ্জামিন হজসন

1
আপনার ফর্ম্যাটটি ইটালিকগুলি থেকে গা bold়ে পরিবর্তন করা উচিত। "আপনি যদি সঠিক ইন্টারফেস প্রয়োগ করেন তবে আপনি একটি সাবক্লাস!" খুব সংক্ষিপ্ত ব্যাখ্যা। ধন্যবাদ!
Marti

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 থেকে উদাহরণ

সম্পাদনা করুন: পাইথন 3 সিনট্যাক্স অন্তর্ভুক্ত করতে, ধন্যবাদ @ প্যান্ডাসরকস


5
উদাহরণ এবং লিঙ্ক উভয়ই খুব সহায়ক ছিল। ধন্যবাদ!
nalyd88

যদি বেসটি একটি পৃথক ফাইলে সংজ্ঞায়িত করা হয় তবে আপনাকে অবশ্যই এটি থেকে বেস হিসাবে উত্তরাধিকারী হতে হবে বা আমদানি লাইনটি 'বেস আমদানি বেস থেকে' করতে হবে
রায়ান টেনিল

এটি লক্ষ করা উচিত যে পাইথন 3 এ সিনট্যাক্সটি কিছুটা আলাদা। : এই উত্তর দেখার stackoverflow.com/questions/28688784/...
PandasRocks

7
একটি সি # ব্যাকগ্রাউন্ড থেকে আসা, এটি বিমূর্ত ক্লাস ব্যবহার করার কারণ। আপনি কার্যকারিতা সরবরাহ করছেন, তবে উল্লেখ করে যে কার্যকারিতাটির আরও বাস্তবায়ন প্রয়োজন। অন্যান্য উত্তরগুলি এই পয়েন্টটি মিস করবে বলে মনে হচ্ছে।
জোশ নো

18

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


6

বিমূর্ত পদ্ধতিটি নিশ্চিত করুন যে আপনি পিতামাতার ক্লাসে যে কোনও পদ্ধতিটি কল করছেন তা শিশু শ্রেণিতে উপস্থিত হতে হবে। নীচে কল এবং বিমূর্ত ব্যবহার করার নরমাল উপায় রয়েছে are অজগর 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()

প্রকারের ত্রুটি: বিমূর্ত পদ্ধতি পদ্ধতিতে বিমূর্ত শ্রেণির পুত্রকে তাত্পর্যপূর্ণ করতে পারে না।

যেহেতু শিশু শ্রেণিতে মেথডটিউ না বলা হয় আমরা ত্রুটি পেয়েছি। সঠিক প্রয়োগটি নীচে রয়েছে

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
ধন্যবাদ। উপরের সেরবেরোস উত্তরে কি একই পয়েন্টটি তৈরি করা হয়নি?
মুহাম্মদ আলকারৌরি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.