Cara membuat properti abstrak di kelas abstrak python

118

Dalam kode berikut, saya membuat kelas abstrak dasar Base. Saya ingin semua kelas yang diwarisi Basemenyediakan nameproperti, jadi saya menjadikan properti ini sebagai @abstractmethod.

Kemudian saya membuat subclass dari Base, disebut Base_1, yang dimaksudkan untuk menyediakan beberapa fungsionalitas, tetapi tetap abstrak. Tidak ada nameproperti di Base_1, namun python membuat objek dari kelas itu tanpa kesalahan. Bagaimana cara membuat properti abstrak?

from abc import ABCMeta, abstractmethod
class Base(object):
    __metaclass__ = ABCMeta
    def __init__(self, strDirConfig):
        self.strDirConfig = strDirConfig

    @abstractmethod
    def _doStuff(self, signals):
        pass

    @property    
    @abstractmethod
    def name(self):
        #this property will be supplied by the inheriting classes
        #individually
        pass


class Base_1(Base):
    __metaclass__ = ABCMeta
    # this class does not provide the name property, should raise an error
    def __init__(self, strDirConfig):
        super(Base_1, self).__init__(strDirConfig)

    def _doStuff(self, signals):
        print 'Base_1 does stuff'


class C(Base_1):
    @property
    def name(self):
        return 'class C'


if __name__ == '__main__':
    b1 = Base_1('abc')  
Boris Gorelik
sumber
Gotcha: Jika Anda lupa untuk menggunakan dekorator @propertydi class C, nameakan kembali ke metode.
kevinarpe

Jawaban:

117

Sejak Python 3.3 bug telah diperbaiki yang berarti property()dekorator sekarang diidentifikasi dengan benar sebagai abstrak ketika diterapkan ke metode abstrak.

Catatan: Urutan penting, Anda harus menggunakan @propertysebelumnya@abstractmethod

Python 3.3+: ( python docs ):

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Python 2: ( dokumen python )

class C(ABC):
    @abstractproperty
    def my_abstract_property(self):
        ...
James
sumber
1
@James Bagaimana membuatnya kompatibel untuk python 2 dan juga?
himanshu219
@James sebenarnya saya bermaksud untuk keduanya tetapi tidak masalah saya memposting jawaban berdasarkan solusi Anda
himanshu219
45

Hingga Python 3.3 , Anda tidak dapat menumpuk @abstractmethoddan @property.

Gunakan @abstractpropertyuntuk membuat properti abstrak ( dokumen ).

from abc import ABCMeta, abstractmethod, abstractproperty

class Base(object):
    # ...
    @abstractproperty
    def name(self):
        pass

Kode sekarang memunculkan pengecualian yang benar:

Traceback (panggilan terakhir terakhir):
  File "foo.py", baris 36, in 
    b1 = Base_1 ('abc')  
TypeError: Tidak dapat membuat instance kelas abstrak Base_1 dengan nama metode abstrak
codeape
sumber
42
sebenarnya jawaban ini salah untuk ular sanca yang lebih muda: karena 3.3, @abstractpropertysudah tidak digunakan lagi untuk kombinasi seperti OP.
domba terbang
jadi sampai 3,3, hanyaraise NotImplementedError
Sławomir Lenart
dapatkah kita mewarisi dari objek dan masih menggunakan anotasi ABC? Apakah akan berfungsi seperti yang ditunjukkan pada contoh?
santhosh kumar
3

Berdasarkan jawaban James di atas

def compatibleabstractproperty(func):

    if sys.version_info > (3, 3):             
        return property(abstractmethod(func))
    else:
        return abstractproperty(func)

dan menggunakannya sebagai dekorator

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()
himanshu219
sumber