Problems with ABC/Interfaces using pydantic + Mixins pattern - python

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

Related

What is the correct OOP way to add functionality to a set of classes deriving from an abstract base class?

Setting
My friend told me that in OOP, you generally don't want to modify any abstract base classes in an existing codebase, because that means you have to implement the new changes to each and every derived class. I'm interested in which modification to the codebase one would prefer in a preferably pythonic way. The emphasis is on changing an existing codebase.
Example scenario
I have an abstract base class called Animal, because the code that uses this library has to interact with Animal objects.
I have multiple child class implementations: Dog and Cat that each have a different set of fields that they each need for their own inner functionality. So at this point the code base looks like:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
Modification
AFTER this is implemented, the developers realize that they want to log the relevant fields (or states) from an Animal objects and what the logged data is varies from child class to child class.
Option A
Originally, my idea would be to implement another abstractmethod and add functionality that way. This forces every Animal to implement the new get_fields() in whatever way they need to.
class Animal(ABC):
#abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
#abstractmethod
def get_fields(self) -> list:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
def get_fields(self) -> list:
return [self.woof]
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
def get_fields(self) -> list:
return [self.meow, self.purr]
Option B
My friend is saying we shouldn't modify the abstract class however, the only other option we came up with is to do the following:
def get_field(animal: Animal) -> list:
if isinstance(animal, Dog):
return [animal.woof]
elif isinstance(animal, Cat):
return [animal.meow, animal.purr]
else:
raise TypeError
Which one would you go with? Is there another, better way to do this? Which one is more pythonic?
Implement a generic mechanism on the ABC, as a concrete method, but devolve the configuration to the subclasses and DONT use hardcoded names.
I've used Meta here because that is the type of stuff you see in Django models, name-spacing what is a given class's configuration in an embedded Meta class. Django specifically uses a very similar system to track which fields get display where in the auto-generated admin panels used for data entry.
from abc import ABC, abstractmethod
from typing import Optional, List
class Animal(ABC):
class Meta:
#could also set to [] as default...
fields_of_interest : Optional[List[str]] = None
#abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
def get_fields(self) -> List:
if self.Meta.fields_of_interest is None:
# could also raise NotImplementedError("need to set `fields_of_interest` on class Meta in class {self.__class__.__name__}")
return []
res = [getattr(self, field) for field in self.Meta.fields_of_interest]
return res
class Dog(Animal):
class Meta:
fields_of_interest = ["woof"]
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
class Cat(Animal):
class Meta:
fields_of_interest = ["purr", "meow"]
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
class Mouse(Animal):
def feed(self, foo: str) -> None:
print(f"{self} feed")
for cls in [Cat, Dog, Mouse]:
animal = cls()
print(f"{animal} {animal.get_fields()}")
output:
<__main__.Cat object at 0x1079f67d0> ['purr', 'meow']
<__main__.Dog object at 0x1079f6320> ['woof']
<__main__.Mouse object at 0x1079f67d0> []
Also, in terms of abstract vs concrete, it helps to think creatively to keep method behavior uniform (and therefore generic), rather than being overly picky. For example, either the original Design Patterns book or one taking it up was talking the Composite Pattern, which deals with "trees". Well, what they said was that instead of throwing an exception when you are on a Leaf (no children) and trying to iterate its, non-existent, children, they could just return an empty list.

Explicitly require attribute in descendent of python base class

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.

Constant python class

I want to create python class with read only properties.
Please see this example:
class ClassProperty(object):
def __init__(self, getter):
self.getter = getter
def __get__(self, instance, owner):
return self.getter(owner)
class Constants(object):
#ClassProperty
def version(cls):
return '1.0.11'
So under this (cls) word i have this message:
Usually first parameter of method is named self
So i wonder is i need to declare it this way:
class Constants(object):
#ClassProperty
def version(self):
return '1.0.11'
And in this way the message disappear

What is the pythonic way to overload class variables(properties)?

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))

Implementing Abstract Properties in Python

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

Categories