简体   繁体   中英

Python abstract setters and getters

I want to write abstract class that will force inheriting classes to implement all methods AND properties in my abstract class.

Additionally I want to use of setters and getters for my abstract property to make my code uncluttered and looking nicely

However, current implementation:

import abc


class Component(metaclass=abc.ABCMeta):

    @property
    @abc.abstractmethod
    def status(self):
        pass

    @property
    @status.setter
    @abc.abstractmethod
    def status(self, value):
        pass

does enforce inheriting class to implement getter for my abstract property getter, but does not enforce creating a property setter (what is exactly what I want)

How can I achieve this behavior without loosing all benefits from application of further mentioned method (aka writing new methods and executing them in my abstract class setter)?

from abc import ABCMeta, abstractmethod

class Base(object): metaclass = ABCMeta

def __init__(self, val):
    self._foo = val

@abstractmethod
def _doStuff(self, signals):
    print ('Base does stuff')

@abstractmethod
def _get_foo(self):
    return self._foo

@abstractmethod
def _set_foo(self, val):
    self._foo = val + 'r'

foo = property(_get_foo, _set_foo)

class floor_1(Base): metaclass = ABCMeta

def __init__(self, val):
    self._foo = val
    super(floor_1, self).__init__(val)

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

def _get_foo(self):
    return self._foo

def _set_foo(self, val):
    #self._foo = val + 'r'
    super()._set_foo(val + 'r')

foo = property(_get_foo, _set_foo)

class floor_2(floor_1):

@property
def foo(self):
    return self._foo

@foo.setter
def foo(self, val):
    self._foo = val + 'r'
    #super()._set_foo(val + 'r')

b1 = floor_1('bar')

b1 = floor_2('bar')

print(b1.foo) b1.foo = 'bar' print(b1.foo)

The problem is that neither the getter nor the setter is a method of your abstract class; they are attributes of the property , which is a (non-callable) class attribute. Consider this equivalent definition:

def status_getter(self):
    pass

def status_setter(self, value):
    pass

class Component(metaclass=abc.ABCMeta):
    # status = property(...)
    # status.__isabstractmethod__ = True
    status = abstractmethod(property(status_getter, status_setter))

Inheriting a property is quite different from inheriting a method. You are basically replacing the property, because your class itself does not have a reference to either the getter or the setter. Despite the name, abstractmethod does not actually make the property a method; it really does nothing more than add an attribute to whatever it is applied to and return the original value.

So, to ensure that a subclass provides a read/write property, what are you to do? Skip the decorator syntax, define the getter and setter as explicit abstract methods, then define the property explicitly in terms of those private methods.

class Component(metaclass=abc.ABCMeta):
    @abstractmethod
    def _get_status(self):
        pass

    @abstractmethod
    def _set_status(self, v):
        pass

    status = property(lambda self: self._get_status(), lambda self, v: self._set_status(self, v))

Or, you can make use of __init_subclass__ (which postdates abc ; its purpose is to allow class initialization that is otherwise only possible via a metaclass).

class Component:
    def __init_subclass(cls, **kwargs):
        super().__init_subclass__(**kwargs)

        try:
            p = cls.status
        except AttributeError:
            raise ValueError("Class does not define 'status' attribute")

        if not isinstance(p, property):
            raise ValueError("'status' is not a property")

        if p.fget is None:
            raise ValueError("'status' has no getter")

        if p.fset is None:
            raise ValueError("'status' has no setter")

This is actually an improvement over abc , in my opinion. If a subclass fails to define a read/write status property, an exception will be raised when the class is defined , not just when you attempt to instantiate the class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM