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


321

পাইথনে আমি কীভাবে কোনও ক্লাস বা পদ্ধতি বিমূর্ত করতে পারি?

আমি এর __new__()মতো নতুন সংজ্ঞা দেওয়ার চেষ্টা করেছি:

class F:
    def __new__(cls):
        raise Exception("Unable to create an instance of abstract class %s" %cls)

তবে এখন যদি আমি এমন শ্রেণি তৈরি করি Gযা উত্তরের Fমতো উত্তরাধিকার সূত্রে প্রাপ্ত হয় :

class G(F):
    pass

তবে আমি তা ইনস্ট্যান্টিয়েট করতে পারি না G, কারণ এটি এর সুপার ক্লাসের __new__পদ্ধতিটিকে বলে।

একটি বিমূর্ত শ্রেণি সংজ্ঞায়নের জন্য আরও ভাল উপায় আছে?


হ্যাঁ, আপনি অ্যাবসি (অ্যাবস্ট্রাক্ট বেস ক্লাস) মডিউলটি দিয়ে পাইথনে অ্যাবস্ট্রাক্ট ক্লাস তৈরি করতে পারেন। এই সাইটটি আপনাকে এটিতে সহায়তা করবে: http://docs.python.org/2/library/abc.html
ওরিওশন

উত্তর:


551

abcবিমূর্ত ক্লাস তৈরি করতে মডিউলটি ব্যবহার করুন । abstractmethodকোনও পদ্ধতি বিমূর্ত ঘোষণার জন্য ডেকরেটার ব্যবহার করুন এবং আপনার পাইথন সংস্করণ অনুসারে তিনটি উপায়ের একটি ব্যবহার করে একটি শ্রেণি বিমূর্তি ঘোষণা করুন।

পাইথন ৩.৪ এবং এর উপরে, আপনি এর উত্তরাধিকারী হতে পারেন ABC। পাইথনের পূর্ববর্তী সংস্করণগুলিতে আপনাকে আপনার শ্রেণীর মেটাগ্লাস হিসাবে উল্লেখ করতে হবে ABCMeta। মেটাক্লাসটি পাইথন 3 এবং পাইথন 2 এ আলাদা সিনট্যাক্স উল্লেখ করে তিনটি সম্ভাবনা নীচে দেখানো হয়েছে:

# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
    @abstractmethod
    def foo(self):
        pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

আপনি যে কোনও উপায়েই ব্যবহার করুন না কেন আপনি বিমূর্ত পদ্ধতি রয়েছে এমন একটি বিমূর্ত শ্রেণিকে ইনস্ট্যান্ট করতে সক্ষম হবেন না, তবে এই পদ্ধতিগুলির সুনির্দিষ্ট সংজ্ঞা প্রদান করে এমন একটি সাবক্লাস ইনস্ট্যান্ট করতে সক্ষম হবেন:

>>> Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
...     pass
... 
>>> StillAbstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
...     def foo(self):
...         print('Hello, World')
... 
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>

9
@ অবস্ট্রাকমেডোস্ট কী করে? কেন তোমার এটা দরকার? ক্লাসটি ইতিমধ্যে বিমূর্ত হিসাবে প্রতিষ্ঠিত হলে সংকলক / ব্যাখ্যা করা উচিত নয় যে সমস্ত পদ্ধতি প্রশ্নবিদ্ধ বিমূর্ত শ্রেণি থেকে এসেছে?
চার্লি পার্কার

29
@ চর্লিপারপার্কার - @abstractmethodএটি তৈরি করে যাতে শ্রেণিটি তাত্ক্ষণিকভাবে শুরুর আগে সজ্জিত ফাংশনটি ওভাররাইড করা উচিত । দস্তাবেজগুলি থেকে:A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden.
নকল নাম

6
@ চর্লিপার্কার - মূলত, এটি আপনাকে এমন একটি শ্রেণির সংজ্ঞা দিতে দেয় যেখানে উপ-শ্রেণিকে অবশ্যই ইনস্ট্যান্টিয়েট করার জন্য একটি নির্দিষ্ট পদ্ধতির পদ্ধতি প্রয়োগ করতে হবে।
ভুয়া নাম

13
ব্যবহারকারীদের কোনও @abstractmethod পদ্ধতি ছাড়াই অ্যাবস্ট্রাক্ট () তৈরি করতে বাধা দেওয়ার কোনও উপায় আছে কি?
জো

2
@Joe এটা প্রদর্শিত করছে যে ব্যবহার করতে পারেন @abstractmethodজন্য __init__পাশাপাশি পদ্ধতি, দেখতে stackoverflow.com/q/44800659/547270
scrutari

107

পুরানো-বিদ্যালয়ের (প্রাক PEP 3119 ) উপায়টি কেবল raise NotImplementedErrorবিমূর্ত শ্রেণিতে করা যখন একটি বিমূর্ত পদ্ধতি বলা হয়।

class Abstract(object):
    def foo(self):
        raise NotImplementedError('subclasses must override foo()!')

class Derived(Abstract):
    def foo(self):
        print 'Hooray!'

>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]

abcমডিউলের মতো এটির মতো দুর্দান্ত বৈশিষ্ট্য নেই। আপনি এখনও বিমূর্ত বেস শ্রেণি নিজেই ইনস্ট্যান্ট করতে পারেন এবং রানটাইম এ বিমূর্ত পদ্ধতিটি কল না করা পর্যন্ত আপনি আপনার ভুল খুঁজে পাবেন না।

তবে আপনি যদি কয়েকটি সাধারণ ক্লাসের একটি ছোট সেট নিয়ে আলোচনা করছেন, সম্ভবত কয়েকটি বিমূর্ত পদ্ধতি ব্যবহার করে, abcডকুমেন্টেশনের মাধ্যমে বোঝা চালানোর চেষ্টা করার চেয়ে এই পদ্ধতিটি কিছুটা সহজ ।


1
এই পদ্ধতির সরলতা এবং কার্যকারিতাটির প্রশংসা করুন।
mohit6up

হাহা, আমি আমার ওএমএসসিএস কোর্সে এটি সর্বত্র দেখেছি এবং এটি কী ছিল তার কোনও ক্লু ছিল না :)
এমএল স্টুডেন্ট 33

18

এবিসি মডিউলটি মোকাবেলা না করেই খুব সহজ উপায়।

আপনি __init__যে শ্রেণীর বিমূর্ত শ্রেণি হতে চান সেই পদ্ধতিতে আপনি নিজের "প্রকার" পরীক্ষা করতে পারেন। যদি স্ব-ধরণের ধরনটি বেস শ্রেণি হয়, তবে কলার বেস ক্লাসটি ইনস্ট্যান্ট করার চেষ্টা করছে, তাই একটি ব্যতিক্রম বাড়াও। এখানে একটি সাধারণ উদাহরণ:

class Base():
    def __init__(self):
        if type(self) is Base:
            raise Exception('Base is an abstract class and cannot be instantiated directly')
        # Any initialization code
        print('In the __init__  method of the Base class')

class Sub(Base):
    def __init__(self):
        print('In the __init__ method of the Sub class before calling __init__ of the Base class')
        super().__init__()
        print('In the __init__ method of the Sub class after calling __init__ of the Base class')

subObj = Sub()
baseObj = Base()

রান করার সময় এটি তৈরি করে:

In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__  method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
    baseObj = Base()
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
    raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly

এটি দেখায় যে আপনি এমন একটি সাবক্লাস ইনস্ট্যান্ট করতে পারেন যা বেস ক্লাস থেকে উত্তরাধিকার সূত্রে আসে তবে আপনি বেস ক্লাসটি সরাসরি ইনস্ট্যান্ট করতে পারবেন না।


11

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

from abc import ABC, abstractmethod

class AbstractClass(ABC):

    def __init__(self, value):
        self.value = value
        super().__init__()

    @abstractmethod
    def eat(self):
        pass

class Parents(AbstractClass):
    def eat(self):
        return "eat solid food "+ str(self.value) + " times each day"

class Babies(AbstractClass):
    def eat(self):
        return "Milk only "+ str(self.value) + " times or more each day"

food = 3    
mom = Parents(food)
print("moms ----------")
print(mom.eat())

infant = Babies(food)
print("infants ----------")
print(infant.eat())

আউটপুট:

moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day

সম্ভবত import abcঅকেজো।
বেনিয়ামিন জাফারি

আমরা @abstractmethod লিখতে পারেন Init অনুষ্ঠানে?
পরিবর্তনশীল

+1 @ অবস্ট্রাকমিটার ডিফ কেন (স্ব)? যদি এই শ্রেণিটি বিমূর্ত হয় এবং তাই এটি তাত্ক্ষণিকভাবে বোঝানো না হয় তবে আপনি খেতে কেন (স্ব) পাস করবেন? এটি ছাড়াই এটি দুর্দান্ত কাজ করে
ভিএমএমএফ

7

এই এক পাইথন 3 কাজ করবে

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    @abstractmethod
    def foo(self):
        pass

Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo

4

অন্যান্য উত্তরে যেমন ব্যাখ্যা করা হয়েছে, হ্যাঁ আপনি abcমডিউলটি ব্যবহার করে পাইথনে অ্যাবস্ট্রাক্ট ক্লাস ব্যবহার করতে পারেন । নীচে আমি একটি প্রকৃত উদাহরণ বিমূর্ত ব্যবহার দিতে @classmethod, @propertyএবং @abstractmethod(পাইথন 3.6+ ব্যবহার করে)। আমার পক্ষে সাধারণত সহজেই অনুলিপি এবং আটকানো যায় এমন উদাহরণগুলি দিয়ে শুরু করা সহজ; আমি আশা করি এই উত্তরটি অন্যদের জন্যও কার্যকর।

প্রথমে একটি বেস ক্লাস তৈরি করা যাক Base:

from abc import ABC, abstractmethod

class Base(ABC):

    @classmethod
    @abstractmethod
    def from_dict(cls, d):
        pass

    @property
    @abstractmethod
    def prop1(self):
        pass

    @property
    @abstractmethod
    def prop2(self):
        pass

    @prop2.setter
    @abstractmethod
    def prop2(self, val):
        pass

    @abstractmethod
    def do_stuff(self):
        pass

আমাদের Baseক্লাসে সর্বদা একটি from_dict classmethod, একটি property prop1(যা কেবলমাত্র পঠনযোগ্য) এবং একটি property prop2(যা সেট করা যায়) পাশাপাশি একটি ফাংশন থাকবে do_stuff। এখন ক্লাসের উপর ভিত্তি করে যা কিছু তৈরি Baseকরা হয়েছে সেগুলি পদ্ধতি / বৈশিষ্ট্যগুলির জন্য এগুলি সমস্ত প্রয়োগ করতে হবে। দয়া করে নোট করুন যে কোনও পদ্ধতির বিমূর্ত হওয়ার জন্য, দুটি সজ্জকার প্রয়োজন - classmethodএবং বিমূর্ত property

এখন আমরা এর Aমতো একটি শ্রেণি তৈরি করতে পারি :

class A(Base):
    def __init__(self, name, val1, val2):
        self.name = name
        self.__val1 = val1
        self._val2 = val2

    @classmethod
    def from_dict(cls, d):
        name = d['name']
        val1 = d['val1']
        val2 = d['val2']

        return cls(name, val1, val2)

    @property
    def prop1(self):
        return self.__val1

    @property
    def prop2(self):
        return self._val2

    @prop2.setter
    def prop2(self, value):
        self._val2 = value

    def do_stuff(self):
        print('juhu!')

    def i_am_not_abstract(self):
        print('I can be customized')

সমস্ত প্রয়োজনীয় পদ্ধতি / বৈশিষ্ট্য বাস্তবায়ন করা হয় এবং আমরা - অবশ্যই - এছাড়াও অতিরিক্ত ফাংশন যোগ করতে পারি যা Base(এখানে i_am_not_abstract:) এর অংশ নয় ।

এখন আমরা করতে পারি:

a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})

a1.prop1
# prints 10

a1.prop2
# prints 'stuff'

পছন্দসই হিসাবে, আমরা সেট করতে পারি না prop1:

a.prop1 = 100

ফিরে আসবে

অ্যাট্রিবিউটআরার: অ্যাট্রিবিউট সেট করতে পারে না

এছাড়াও আমাদের from_dictপদ্ধতিটি ভাল কাজ করে:

a2.prop1
# prints 20

যদি আমরা এখন এটির Bমতো দ্বিতীয় শ্রেণির সংজ্ঞা দিই :

class B(Base):
    def __init__(self, name):
        self.name = name

    @property
    def prop1(self):
        return self.name

এবং এই জাতীয় কোনও বস্তুর ইনস্ট্যান্ট করার চেষ্টা করেছিল:

b = B('iwillfail')

আমরা একটি ত্রুটি পেতে হবে

প্রকারের ত্রুটি: বিমূর্ত পদ্ধতি do_stuff, from_dict, prop2 দিয়ে বিমূর্ত শ্রেণি বি ইনস্ট্যান্ট করতে পারে না

সংজ্ঞায়িত সমস্ত বিষয় তালিকাভুক্ত করা হচ্ছে Baseযা আমরা প্রয়োগ করি নি B


2

এছাড়াও এটি কাজ করে এবং সহজ:

class A_abstract(object):

    def __init__(self):
        # quite simple, old-school way.
        if self.__class__.__name__ == "A_abstract": 
            raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")

class B(A_abstract):

        pass

b = B()

# here an exception is raised:
a = A_abstract()

2

আপনি নিজের সুবিধার জন্য __ নতুন__ পদ্ধতিটিও ব্যবহার করতে পারেন। আপনি কিছু ভুলে গেছেন। __New__ পদ্ধতি সর্বদা নতুন অবজেক্টকে ফিরিয়ে দেয় যাতে আপনাকে অবশ্যই তার সুপারক্লাসের নতুন পদ্ধতিটি ফেরত দিতে হবে। নিম্নলিখিত হিসাবে না।

class F:
    def __new__(cls):
        if cls is F:
            raise TypeError("Cannot create an instance of abstract class '{}'".format(cls.__name__))
        return super().__new__(cls)

নতুন পদ্ধতিটি ব্যবহার করার সময় আপনাকে কোনওটি কীওয়ার্ড নয়, বস্তুটি ফিরিয়ে দিতে হবে। এটাই আপনি মিস করেছেন।


2

আমি গৃহীত উত্তরটি এবং অন্য সমস্তগুলিকে অদ্ভুত বলে মনে করি যেহেতু তারা selfএকটি বিমূর্ত শ্রেণিতে পাস করে। একটি বিমূর্ত শ্রেণি তাত্ক্ষণিক হয় না তাই এটি থাকতে পারে না self

সুতরাং এটি চেষ্টা করুন, এটি কাজ করে।

from abc import ABCMeta, abstractmethod


class Abstract(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def foo():
        """An abstract method. No need to write pass"""


class Derived(Abstract):
    def foo(self):
        print('Hooray!')


FOO = Derived()
FOO.foo()

1
এমনকি এবিসি ডকুমেন্টেশনগুলি তাদের উদাহরণগুলির লিঙ্কে স্ব ব্যবহার করে
আইগোর স্মারনভ

2
 from abc import ABCMeta, abstractmethod

 #Abstract class and abstract method declaration
 class Jungle(metaclass=ABCMeta):
     #constructor with default values
     def __init__(self, name="Unknown"):
     self.visitorName = name

     def welcomeMessage(self):
         print("Hello %s , Welcome to the Jungle" % self.visitorName)

     # abstract method is compulsory to defined in child-class
     @abstractmethod
     def scarySound(self):
         pass

সুন্দর @ শিবাম ভরদ্বাজ, আমি এটি একইভাবে করেছি
মুহাম্মদ ইরফান

-3

আপনার কোড স্নিপেটে, আপনি __new__একইভাবে সাবক্লাসে পদ্ধতিটির জন্য একটি বাস্তবায়ন সরবরাহ করেও এটি সমাধান করতে পারেন :

def G(F):
    def __new__(cls):
        # do something here

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

এছাড়াও যখন আপনি একটি নতুন (বেস) বর্গ তৈরি করুন, এটা উপশ্রেণী করা object, এরকম: class MyBaseClass(object):। আমি জানি না এটি এত বেশি তাৎপর্যপূর্ণ কিনা কিনা তবে এটি আপনার কোডে শৈলীর ধারাবাহিকতা বজায় রাখতে সহায়তা করে


-4

শুধু @ TimGilbert পুরাতন-স্কুল উত্তর ... আপনি আপনার বিমূর্ত বেস ক্লাস করতে পারেন একটি দ্রুত উপরন্তু Init () মেথড একটি ব্যতিক্রম নিক্ষেপ এবং যে, কোন instantiated হওয়া আটকাতে হবে?

>>> class Abstract(object):
...     def __init__(self):
...         raise NotImplementedError("You can't instantiate this class!")
...
>>> a = Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
NotImplementedError: You can't instantiate this class! 

6
এটি সাবক্লাসগুলিকে যে কোনও সাধারণ আরম্ভ কোডটি যুক্তিযুক্তভাবে তাদের সাধারণ বেস শ্রেণিতে উপস্থিত হওয়া উচিত সেগুলি উপকার করতে বাধা দেবে ।
অর্পিত সিং

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