Understanding Abstract Base Classes in Python - python

I was reading about abstract base class and came across https://www.python-course.eu/python3_abstract_classes.php website. I got general idea about them but I found two statement contradictory of each other.
Subclasses of an abstract class in Python are not required to implement abstract methods of the parent class.
and
A class that is derived from an abstract class cannot be instantiated unless all of its abstract methods are overridden.
My understanding of first statement is, derived class are not required to implement abstract method of the parent class which is wrong. I made a sample program to check that.
from abc import ABC, abstractmethod
class AbstractClassExample(ABC):
#abstractmethod
def do_something(self):
print("Some implementation!")
class AnotherSubclass(AbstractClassExample):
def just_another_method(self):
super().do_something()
print("The enrichment from AnotherSubclass")
x = AnotherSubclass() # TypeError: Can't instantiate abstract class AnotherSubclass with abstract methods do_something
x.do_something()
I would like an explanation of what the first statement means(preferably with examples).

Your code demonstrates that the second statement is true. It doesn't show that the first statement is false.
In your code, you are trying to instantiate AnotherSubclass, which is not allowed because AnotherSubclass does not implement all the abstract methods. The second statement says this.
However, if you delete the last two lines, i.e. not instantiating AnotherSubclass, then your code will produce no errors when you try to run it. This shows that the first statement is true - subclasses of abstract classes that doesn't implement all its abstract methods are allowed to exist.
You can write another subclass of AnotherSubclass called YetAnotherClass, this time implementing the abstract method, and you will be able to instantiate YetAnotherClass. Note that your program now does something, and AnotherSubclass is still allowed to exist.

Related

Implementing an abstract class with a class method doesn't raise an exception

I am attempting to implement a method as both an abstract method and as a class method but it doesn't feel like any of the benefits of an abstract class are gained when doing so.
For example:
from abc import ABC, abstractmethod
class BasePipeline(ABC):
#classmethod
#abstractmethod
def consume_frame(cls):
pass
#abstractmethod
def consume_frame_two(self):
pass
class AnotherSubclass(BasePipeline):
#classmethod
def does_nothing(cls):
a = 1 + 1
# Call it.
AnotherSubclass.consume_frame()
This doesn't raise any exception and does not error out. I'd expect for it to say something along the lines of: consume_frame_two is not implemented and consume_frame is not implemented.
Not sure what the intended behavior is or if I'm just doing something wrong. I'd like for AnotherSubclass.consume_frame() to raise an exception if it isn't properly implemented as a class method.
Your code doesn't try to create an instance of the AnotherSubclass class. All it does is access the implementation of a classmethod that is marked as abstract. Python's ABC abstract classes are not intended to prevent that kind of access.
The abc module is intended to help you define a protocol or interface, a base class that sets expectations as to what attributes must be present on concrete objects that should be considered the same.
To that end, all that you can do with an ABC subclass is prevent instances to be created of any class in the class hierarchy that has at least one abstractmethod or abstractproperty attribute. From the #abc.abstractmethod() documentation:
A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden.
Any abstractmethod-decorated method can still be called; there is no mechanism to prevent this and it is actually a specific goal of the module that concrete implementations can use super().name() to access the implementation of an abstractmethod object. From the same source:
The abstract methods can be called using any of the normal ‘super’ call mechanisms
and
Note: Unlike Java abstract methods, these abstract methods may have an implementation. This implementation can be called via the super() mechanism from the class that overrides it. This could be useful as an end-point for a super-call in a framework that uses cooperative multiple-inheritance.
Any other attributes of the class can be used just the same as on other classes, including classmethod objects.
Under the covers, each ABCMeta metaclass gives each class you create with it a __abstractmethods__ attribute, which is a frozenset object with the names of any attribute on the class that has the __isabstractmethod__ attribute set to True, subclasses only have to use the same name as a parent abstract method object, setting it to an attribute that doesn't have __isabstractmethod__ set to true to remove that name from the set for that class. Python will then raise an exception when you try to create an instance of a class whose __abstractmethods__ is not empty.
If you need to lock down your class definitions further, then you'll have to come up with our own metaclass or other mechanism to implement those rules. For example, you could wrap classobject attributes in your own descriptor object that prevents calling a classmethod bound to a class with a non-empty __abstractmethods__ attribute.

Using ABC, PolymorphicModel, django-models gives metaclass conflict

So far every other answer on SO answers in the exact same way: construct your metaclasses and then inherit the 'joined' version of those metaclasses, i.e.
class M_A(type): pass
class M_B(type): pass
class A(metaclass=M_A): pass
class B(metaclass=M_B): pass
class M_C(M_A, M_B): pass
class C:(A, B, metaclass=M_C): pass
But I don't know what world these people are living in, where they're constructing your own metaclasses! Obviously, one would be using classes from other libraries and unless you have a perfect handle on meta programming, how are you supposed to know whether you can just override a class's metaclass? (Clearly I do not have a handle on them yet).
My problem is:
class InterfaceToTransactions(ABC):
def account(self):
return None
...
class Category(PolymorphicModel, InterfaceToTransactions):
def account(self):
return self.source_account
...
class Income(TimeStampedModel, InterfaceToTransactions):
def account(self):
return self.destination_account
...
Which of course gives me the error: "metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases"
I've tried many variations of the solution given above, the following does not work, gives the same error.
class InterfaceToTransactionsIntermediaryMeta(type(PolymorphicModel), type(InterfaceToTransactions)):
pass
class Category(PolymorphicModel, InterfaceToTransactions):
__metaclass__ = InterfaceToTransactionsIntermediaryMeta
...
Nor does putting anything inside the class Meta function. I've read every single other SO question on this topic, please don't simply mark it as duplicate.
-------------------Edited 1/8/18 after accepting the solution-------
Oddly enough, if I try to makemigrations with this new configuration (the one I accepted), it starts giving the metaclass error again, but it still works during runtime. If I comment out the metaclass parts then makemigrations and migrate, it will do it successfully, but then I have to put it back in there after migrating every time.
If you are using Python 3, you are trying to use your derived metaclass incorrectly.
And since you get "the same error", and not other possible, more subtle, error, I'd say this is what is happening.
Try just changing to:
class IntermediaryMeta(type(InterfaceToTransactions), type(PolymorphicModel)):
pass
class Category(PolymorphicModel, InterfaceToTransactions, metaclass=IntermediaryMeta):
...
(At least the ABCMeta class is guaranteed to work collaboratively using super, that is enough motive to place the classe it first on the bases )
tuple)
If that yields you new and improved errors, this means that one or both of those classes can't really collaborate properly due to one of several motives. Then, the way to go is to force your inheritance tree that depends on ABCMeta not to do so, since its role is almost aesthetical in a language where everything else is for "consenting adults" like Python.
Unfortunatelly, the way to that is to use varying methods of brute-force, from safe "rewritting everything" to monkey patching ABCMeta and abstractmethod on the place were "InterfaceToTransactions" is defined to simply do nothing.
If you need to get there, and need some help, please post another question.
Sorry - this is actually the major drawbacks of using metaclasses.
Unless django-polymorphic decides to inherit from abc.ABC this is going to be very difficult to achieve. A good solution would be to "manually" create your interface. For instance:
class InterfaceToTransactions:
def account(self):
raise NotImplementedError("Account method must be implemented.")
...
class Category(PolymorphicModel, InterfaceToTransactions):
def account(self):
return self.source_account
...
class Income(TimeStampedModel, InterfaceToTransactions):
def account(self):
return self.destination_account
...

How to tell if a class is abstract in Python 3?

I wrote a metaclass that automatically registers its classes in a dict at runtime. In order for it to work properly, it must be able to ignore abstract classes.
The code works really well in Python 2, but I've run into a wall trying to make it compatible with Python 3.
Here's what the code looks like currently:
def AutoRegister(registry, base_type=ABCMeta):
class _metaclass(base_type):
def __init__(self, what, bases=None, attrs=None):
super(_metaclass, self).__init__(what, bases, attrs)
# Do not register abstract classes.
# Note that we do not use `inspect.isabstract` here, as
# that only detects classes with unimplemented abstract
# methods - which is a valid approach, but not what we
# want here.
# :see: http://stackoverflow.com/a/14410942/
metaclass = attrs.get('__metaclass__')
if not (metaclass and issubclass(metaclass, ABCMeta)):
registry.register(self)
return _metaclass
Usage in Python 2 looks like this:
# Abstract classes; these are not registered.
class BaseWidget(object): __metaclass__ = AutoRegister(widget_registry)
class BaseGizmo(BaseWidget): __metaclass__ = ABCMeta
# Concrete classes; these get registered.
class AlphaWidget(BaseWidget): pass
class BravoGizmo(BaseGizmo): pass
What I can't figure out, though, is how to make this work in Python 3.
How can a metaclass determine if it is initializing an abstract class in Python 3?
PEP3119 describes how the ABCMeta metaclass "marks" abstract methods and creates an __abstractmethods__ frozenset that contains all methods of a class that are still abstract. So, to check if a class cls is abstract, check if cls.__abstractmethods__ is empty or not.
I also found this relevant post on abstract classes useful.
I couldn't shake the feeling as I was posting this question that I was dealing with an XY Problem. As it turns out, that's exactly what was going on.
The real issue here is that the AutoRegister metaclass, as implemented, relies on a flawed understanding of what an abstract class is. Python or not, one of the most important criteria of an abstract class is that it is not instanciable.
In the example posted in the question, BaseWidget and BaseGizmo are instanciable, so they are not abstract.
Aren't we just bifurcating rabbits here?
Well, why was I having so much trouble getting AutoRegister to work in Python 3? Because I was trying to build something whose behavior contradicts the way classes work in Python.
The fact that inspect.isabstract wasn't returning the result I wanted should have been a major red flag: AutoRegister is a warranty-voider.
So what's the real solution then?
First, we have to recognize that BaseWidget and BaseGizmo have no reason to exist. They do not provide enough functionality to be instantiable, nor do they declare abstract methods that describe the functionality that they are missing.
One could argue that they could be used to "categorize" their sub-classes, but a) that's clearly not what's going on in this case, and b) quack.
Instead, we could embrace Python's definition of "abstract":
Modify BaseWidget and BaseGizmo so that they define one or more abstract methods.
If we can't come up with any abstract methods, then can we remove them entirely?
If we can't remove them but also can't make them properly abstract, it might be worthwhile to take a step back and see if there are other ways we might solve this problem.
Modify the definition of AutoRegister so that it uses inspect.isabstract to decide if a class is abstract: see final implementation.
That's cool and all, but what if I can't change the base classes?
Or, if you have to maintain backwards compatibility with existing code (as was the case for me), a decorator is probably easier:
#widget_registry.register
class AlphaWidget(object):
pass
#widget_registry.register
class BravoGizmo(object):
pass

A django model that subclasses an abc, gives a metaclass conflict

I have a following model and abstract base class
import abc
from django.db import models
class AbstractBase():
__metaclass__ = abc.ABCMeta
#abc.abstractmethod
def my_method(self):
return
class MyModel(models.Model, AbstractBase):
#abc.abstractmethod
def my_method(self):
return 1
But I am getting the following error.
metaclass conflict: the metaclass of a derived class must be a
(non-strict) subclass of the metaclasses of all its bases
I think the problem here is (As it is described here http://code.activestate.com/recipes/204197-solving-the-metaclass-conflict/) that two base class has two different metaclasses so python cannot decide which metaclass to use for child object.
In order to solve this I removed multiple inheritence and use following register method to register child class
abc.register(Child)
But I did not really like this approach since it looks like monkey patching.
Is there another way to solve this problem?
I try to assign Model metaclass to Child explicitly but it did not work.
I am not looking for a way to solve it by writing code. I think this must be solved by changing my class structure.
Apart from creating a new metaclass that inherits from both ABCMeta and ModelBase, or making ABCMeta inherit from ModelBase, there isn't much you can do.
However, possibly a different registration pattern might be appropriate? Maybe something like contrib.admin.autodiscover? Or a class decorator? Or a loop at the bottom of the .py file which calls register on the appropriate classes (ex, for var in globals().values(): if isinstance(var, type) and issubclass(var, AbastractBase): register(var))?
Edit: D'oh. I'd assumed that ABCMeta was an example, not ABCMeta. That's what I get for browsing StackOverflow on too little sleep.

What does abstract mean in this context?

I need some help in understanding a python concept.
class TilePuzzleProblem(search.Problem):
""" This class is the class for the NxN - blanks tile puzzle problem """
def __init__(self, N, blanks, initial, goal):
""" Initialize """
search.Problem.__init__(self, initial, goal)
self.N = N
self.blanks = blanks
def successor(self, state):
""" Generate the successors of the given state. Returns a list of (move, successor) pairs"""
abstract
def h(self, node):
abstract
Currently the code hangs at the abstract part of the function h(...), but I have no idea what abstract means, hence can not understand what the problem is.
This is a trick described here. There's not keyword abstract in Python, so, if you won't override this method in some subclass, it'll cause NotImplementedError.
An abstract method is one which a class doesn't implement, making it an abstract class; subclasses must override all abstract methods (i.e., provide concrete implementations) to be concrete classes, i.e., ones for which you can make instances. The normal way in Python to express "this method is abstract" is to have the method's body be raise NotImplementedError.
For more about the general concept, apart from its specifics in Python, see wikipedia.
So, formally, you need to subclass this and implement those two methods marked as "abstract". (Depending on your TA's hints, he or she may actually mean that you should replace the word "abstract" with a working body of code, but that would be quite a stretch with respect the normal meaning of "abstract" in OOP!-).
An abstract function is a function with no implementation. It's a placeholder, just there to fill out the class contract so that you know what methods subclass should provide. What you need to do here is create a descendant of TilePuzzleProblem and fill in your own implementation of h. If you want to run it as is, create a descendant and make your h do nothing.
Abstract means the class must be inherited. Abstract or "base" classes are there to provide a base type which you can extend through inheritance. You cannot instantiate an abstract class, only the classes that inherit it.
See this Wikipedia article for more information.
One reason you'd want to use an abstract base class is if you want to categorize or group your sub-types. For example, Car, Truck and Plane would all inherit from the Vehicle abstract base class. You can't just instantiate a "vehicle", you have to instantiate a car, truck or plane. Being abstract protects it from being instantiated.

Categories

Resources