What's the best, most Pythonic, way to deal with abstract properties in Python? If I want to implement a base class which has a mixture of abstract properties, and concrete methods, I can do so similar to the following.
class BaseClass(object):
__metaclass__ = ABCMeta
#abstractmethod
def property1(self):
pass
#abstractmethod
def property2(self):
pass
#abstractmethod
def property3(self):
pass
#abstractmethod
def abstract_method(self):
pass
def concrete_method(self):
return self.property1 + self.property2
However, when I then go to implement the inheriting class I need to implement each of those properties as getter method for a private property.
class Klass(BaseClass):
def __init__(property1, property2, property3):
self.__property1 = property1
self.__property2 = property2
self.__property3 = property3
#property
def property1(self):
return self.__property1
#property
def property2(self):
return self.__property2
#property
def property3(self):
return self.__property3
Which seems both unnecessarily verbose, and makes the code more obscure than it needs to be.
I don't love the idea of implementing things concretely and raising a NotImplementedErrorif the inheriting class doesn't implement it's own version.
Is there a better way to do this?
You are not required to implement properties as properties. All you need is for the name to exist on the class. So the following, using regular attributes, would work:
class Klass(BaseClass):
property1 = None
property2 = None
property3 = None
def __init__(property1, property2, property3):
self.property1 = property1
self.property2 = property2
self.property3 = property3
def abstract_method(self):
# implementation for the abstract method
Note that there is a abstractproperty decorator that'd better document that you want to use those names as simple values, not methods to call:
class BaseClass(object):
__metaclass__ = ABCMeta
#abstractproperty
def property1(self):
pass
#abstractproperty
def property2(self):
pass
#abstractproperty
def property3(self):
pass
#abstractmethod
def abstract_method(self):
pass
def concrete_method(self):
return self.property1 + self.property2
Related
from abc import ABC, abstractmethod
class BasketballPlayer(ABC):
move1 = 'pass'
move2 = 'dribble'
move3 = 'shoot'
#property
#abstractmethod
def name(self):
pass
class Player2(BasketballPlayer):
def name(self):
pass
y = Player2()
I expect an error on the line y = Player2() because the name is suddenly declared as a method instead of a property. Is this a bug? or something I did wrong?
Equally Python won't care if you implement #abstractmethod as class variable. I am not sure what is logic behind this, perhaps good question to ask on: https://github.com/python/cpython/issues
from abc import ABC, abstractmethod, abstractproperty
class MyAbstractClass(ABC):
#property
#abstractmethod
def name(self):
pass
#abstractmethod
def run(self):
pass
class Child(MyAbstractClass):
name = "foo"
run = "foo"
c = Child()
print(c.name)
I found similar question with answer here: enforcement for abstract properties in python3
I have an abstract base class of which I'm only showing a small portion here, to illustrate my issue.
The abstract base class A below has a property onemore that uses the instance attribute val. Below it are subclasses B and C, which provide this attribute in distinct (but both valid) ways.
So far, so good:
from abc import ABC, abstractmethod
class A(ABC):
#property
def onemore(self) -> int:
return self.val + 1
class B(A):
def __init__(self, value):
self._val = value
val = property(lambda self: self._val)
class C(A):
def __init__(self, value):
self.val = value
b = B(32)
b.onemore # 33
c = C(54)
c.onemore # 55
Now here is my question: is there a way to define A in such a way, that it's more clear that the subclasses need to implement val? As it's defined above, this is hard to miss, esp. if A has many more methods.
I tried this:
class A(ABC):
#property
#abstractmethod
def val(self) -> int:
...
#property
def onemore(self) -> int:
return self.val + 1
This definition is too strict, though: I don't want to demand that the subclasses implement val as a property, I just want to require them to have it as a (readable) attribute. In other words, I want C to be a valid subclass, which is not the case here: this definition does not work with how C provides self.val. (TypeError: Can't instantiate abstract class C with abstract method val).
Any ideas?
PS I have seen this question, which is similar, but doesn't solve the TypeError I'm getting.
Im trying to implement Mixin patter while Im using Pydantics BaseClass to facilitate the instantiation and validation of data from my class. The problem is that my Mixins cannot inhirit from my base classes (actually, the dependency is the opposite ). Also, im using mypy so my implementation needs to be rightly typed.
Lets see an simplified example:
class BaseCart(BaseModel):
id: int
items: List[Item]
adress: str
class CartInterface(ABC):
#abstractproperty
def id(self):
...
#abstractproperty
def items(self):
...
#abstractproperty
def adress(self):
...
#abstractmethod
def get_shipping_value(self):
...
#abstractmethod
def get_items_availability(self):
...
class ShippingMixin(ABC, CartInterface):
def get_shipping_value(self) -> int:
# some business logic using self.address to calculate
class NormalItemsMixin(ABC, CartInterface):
def get_items_availability(self) -> bool:
# iterate over self.items and check stock availability
class AwesomeItemsMixin(ABC, CartInterface):
def get_items_availability(self) -> bool:
# iterate over self.items and check stock availability
# this implementation is different from the normal one
class NormalCart(BaseCart, ShippingMixin, NormalItemsMixin):
...
class AwesomeCart(BaseCart, ShippingMixin, AwesomeItemsMixin):
...
The problem is that after implementing this, I can't instantiate AwesomeCart, I get the following error:
TypeError: Can't instantiate abstract class ResellerCart with abstract methods business_unit, cart_type, channel, id, items, reseller, status, updated_at
What am I missing ?
TLDR: Why this
class Data(BaseModel):
a: int
class IData(ABC):
#abstractproperty
def a(self):
...
class XData(Data, IData):
...
raises TypeError: Can't instantiate abstract class XData with abstract method a when I instanciates XData likes x = XData(a=1)?
In IData a is only "masquarading" as a property when in fact it is a method of the class. You have to do something like
class Data(BaseModel):
_a: int
#property
def a(self):
return self._a
class IData(ABC):
#abstractproperty
def a(self):
pass
class XData(Data, IData):
pass
Now a in Data is also a method and the code works as expected.
#abstractproperty is deprecated since python 3.3, use 'property' with 'abstractmethod' instead.
class IData(ABC):
#property
#abstractmethod
def a(self):
pass
Hello!
I need each child class to has own set of constants. I've found a "proper" way with properties and overloading setter methods, but:
I need to define constructor in child classes (which I don't need) and assign values in constructor;
Every instance of class will have copy of this constants in memory (senseless resource consumption);
It looks weird when you define setter, getter and property at all just to use it as constant.
I've done something like this:
class BaseClass:
def get_a(self):
raise NotImplementedError("Oooops")
def get_b(self):
raise NotImplementedError("Oooops")
class FirstClass(BaseClass):
def get_a(self):
return "a"
def get_b(self):
return "b"
class SecondClass(BaseClass)
def get_a(self):
return "A"
def get_b(self):
return "B"
class SomeClass:
def some_method(self, class_param):
return "{}-{}".format(class_param.get_a, class_param.get_b)
This method also doesn't solve problems of method with properties (except last), just more compact. There's other way, which I find not good:
class BaseClass:
pass
class FirstClass(BaseClass):
A_CONST = "a"
B_CONST = "b"
class SecondClass(BaseClass)
A_CONST = "A"
B_CONST = "B"
class SomeClass:
def some_method(self, class_param):
return "{}-{}".format(class_param.A_CONST, class_param.B_CONST)
In fact, it solve all problems and pretty compact, BUT it violates rule of inheritance (isn't it?).
Question:
What is the proper way to do this?
P.S. Provided code is simplified example, base class contains methods which I use in child class, please don't write me that base class is useless here.
If you want your base class to indicate that it needs to be subclassed with certain attributes, you can make it an abstract base class.
from abc import ABC, abstractmethod
class Base(ABC):
#property
#abstractmethod
def a(self):
raise NotImplementedError
#property
#abstractmethod
def b(self):
raise NotImplementedError
You will then not be allowed to instantiate Base or its subclasses unless they override the abstract methods. You can do either
class First(Base):
a = 1
b = 2
to assign class attributes with those names, or
class Second(Base):
#Base.a.getter
def a(self):
return 3
#Base.b.getter
def b(self):
return 4
The benefit of the second approach is that it will raise an error if you try to assign to the property
Second().a = 5 # AttributeError
your second version looks fine to me… each language has their own conventions around what a "class" or "object" means, and this looks reasonably "Pythonic"
one minor comment about the first version, is that Python doesn't care about "overloading", you don't need to include:
class BaseClass:
def get_a(self):
raise NotImplementedError("Oooops")
at all, i.e. it's fine to have:
class BaseClass:
pass
as well in your first version.
another potentially useful tool here is the property decorator, e.g:
class FirstClass(BaseClass):
#property
def a(self):
return "a"
print(FirstClass().a)
would output "a"
If the key_name : [A_CONST, B_CONST] remains same for child classes, super() will take care of all your concerns (1., 2., 3.).
A 'pythonic' solution would include, to remove duplication's, of any, setter and getter in child classes and let BaseClass() handle these common-tasks.
class BaseClass(object):
def __init__(self, a, b):
self._a_const = a
self._b_const = b
#property
def A_CONST(self):
return self._a_const
#property
def B_CONST(self):
return self._b_const
class FirstClass(BaseClass):
def __init__(self, _aconst, _bconst):
# Let Base class object hold my constants but FirstClass Constructor
# is setting the value. Look SecondClass
super(FirstClass, self).__init__(_aconst, _bconst)
class SecondClass(BaseClass):
def __init__(self, _aconst, _bconst):
# Magic happens here
super(SecondClass, self).__init__(_aconst, _bconst)
class SomeClass():
def some_method(self, class_param):
return "{}-{}".format(class_param.A_CONST, class_param.B_CONST)
firstobj = FirstClass("a", "b")
secondobj = SecondClass("A", "B")
print(SomeClass().some_method(firstobj))
print(SomeClass().some_method(secondobj))
I have python class trees, each made up of an abstract base class and many deriving concrete classes. I want all concrete classes to be accessible through a base-class method, and I do not want to specify anything during child-class creation.
This is what my imagined solution looks like:
class BaseClassA(object):
# <some magic code around here>
#classmethod
def getConcreteClasses(cls):
# <some magic related code here>
class ConcreteClassA1(BaseClassA):
# no magic-related code here
class ConcreteClassA2(BaseClassA):
# no magic-related code here
As much as possible, I'd prefer to write the "magic" once as a sort of design pattern. I want to be able to apply it to different class trees in different scenarios (i.e. add a similar tree with "BaseClassB" and its concrete classes).
Thanks Internet!
you can use meta classes for that:
class AutoRegister(type):
def __new__(mcs, name, bases, classdict):
new_cls = type.__new__(mcs, name, bases, classdict)
#print mcs, name, bases, classdict
for b in bases:
if hasattr(b, 'register_subclass'):
b.register_subclass(new_cls)
return new_cls
class AbstractClassA(object):
__metaclass__ = AutoRegister
_subclasses = []
#classmethod
def register_subclass(klass, cls):
klass._subclasses.append(cls)
#classmethod
def get_concrete_classes(klass):
return klass._subclasses
class ConcreteClassA1(AbstractClassA):
pass
class ConcreteClassA2(AbstractClassA):
pass
class ConcreteClassA3(ConcreteClassA2):
pass
print AbstractClassA.get_concrete_classes()
I'm personnaly very wary of this kind of magic. Don't put too much of this in your code.
Here is a simple solution using modern python's (3.6+) __init__subclass__ defined in PEP 487. It allows you to avoid using a meta-class.
class BaseClassA(object):
_subclasses = []
#classmethod
def get_concrete_classes(cls):
return list(cls._subclasses)
def __init_subclass__(cls):
BaseClassA._subclasses.append(cls)
class ConcreteClassA1(BaseClassA):
pass # no magic-related code here
class ConcreteClassA2(BaseClassA):
pass # no magic-related code here
print(BaseClassA.get_concrete_classes())
You should know that part of the answer you're looking for is built-in. New-style classes automatically keep a weak reference to all of their child classes which can be accessed with the __subclasses__ method:
#classmethod
def getConcreteClasses(cls):
return cls.__subclasses__()
This won't return sub-sub-classes. If you need those, you can create a recursive generator to get them all:
#classmethod
def getConcreteClasses(cls):
for c in cls.__subclasses__():
yield c
for c2 in c.getConcreteClasses():
yield c2
Another way to do this, with a decorator, if your subclasses are either not defining __init__ or are calling their parent's __init__:
def lister(cls):
cls.classes = list()
cls._init = cls.__init__
def init(self, *args, **kwargs):
cls = self.__class__
if cls not in cls.classes:
cls.classes.append(cls)
cls._init(self, *args, **kwargs)
cls.__init__ = init
#classmethod
def getclasses(cls):
return cls.classes
cls.getclasses = getclasses
return cls
#lister
class A(object): pass
class B(A): pass
class C(A):
def __init__(self):
super(C, self).__init__()
b = B()
c = C()
c2 = C()
print 'Classes:', c.getclasses()
It will work whether or not the base class defines __init__.