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.
Related
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.
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))
In the case of multiple inheritance in python, is there a way to identify which super class a class-level variable is obtained from?
All attempts I tried to google are overwhelmingly about How to get the attribute not find out where it came from:
https://www.google.com/search?q=pythin+which+super+class+defines+attr
https://www.google.com/search?q=python+which+super+class+has+attribute&oq=python+which+super+class+has+attr
https://www.google.com/search?q=python+which+super+class+attribute+obtained+from
I suppose I can manually step through the MRO using inspect.getmro(cls). But I couldn't find any more elegant solutions. Just wondering if anyone knows of one.
EDIT
For a concrete example:
class Super1(object):
__class_attribute__ = "Foo"
class Super2(object):
pass
class Derived(Super1, Super2):
pass
d = Derived()
parent_cls = some_function_to_get_defining_class(d.__class_attribute__) # <-- should return `Super1`
The __qualname__ attribute gives an indication from which class a method was inherited. However, this only returns a string, not the superclass itself. If you need to the superclass for metaprogramming, I think you are going to have to dig into the MRO.
class A:
def a(self):
return 1
def b(self):
return 2
class B:
def b(self):
return 2.5
def c(self):
return 3
class C(A,B):
pass
Using:
C.b.__qualname__
# returns:
'A.b'
However, this does not apply when using abstract methods to define an interface, since the method has to be overwritten.
from abc import abstractmethod
class A:
def a(self):
return 1
#abstractmethod
def b(self):
pass
class C(A):
def b(self):
return 100
C.b.__qualname__
# returns:
'C.b'
How to make one instance of a derived class share attributes and state with another instance of its base class in Python?
class Foo(object):
def __init__(self, a, b):
self.value = a
def method1(self):
self.value += 1
return self.value
class Foo_child(Foo):
def __init__(self, Foo_instance, c, d):
super().__init__()
A = Foo(30,40)
B = Foo_child(A,50,60)
What i need is some way where changing B should affect A and vice versa.
For e.g. If i call B.method1, then i need A to have a A.value of 31 and vice versa. Is there any obvious pythonic way to do this?
Your problem is a containment (has-a) relationship, not a is-a relationship. It may also be necessary to make Foo_child inherit from Foo (they provide the same functionality), but in essence you want to delegate handling of value and method1 to Foo_instance.
Re-implement method1 on Foo_child to delegate to Foo_instance, value on Foo_child should be a property object that also delegates:
class Foo_child(Foo):
def __init__(self, Foo_instance, c, d):
super().__init__(c, d)
self.Foo_instance = Foo_instance
#property
def value(self):
return self.Foo_instance.value
#value.setter
def value(self, value):
self.Foo_instance.value = value
def method1(self):
return self.Foo_instance.method1()