I want to create a class hierarchy in which I have a class Block which can be instantiated by itself. Then I have a class List which inherits from Block and contains methods common to all lists, and finally I have classes OrderedList, LableledList etc that inherit from List. I want people to be able to instantiate OrderedList etc, but not List.
In other words, you can instantiate a plain Block and you can instantiate an OrderedList that inherits from List that inherits from Block, but you can't instantiate List.
All attempts to Google this lead to Abstract Base Classes, but none provides and example that fits this case and I am having trouble extrapolating.
The following conversation with the interpreter should show how this is possible. After inheriting from the Abstract Base Class with Block, you only need to mark the initializer on List as being an abstractmethod. This will prevent instantiation of the class without causing problems for child classes.
>>> import abc
>>> class Block(abc.ABC):
def __init__(self, data):
self.data = data
>>> class List(Block):
#abc.abstractmethod
def __init__(self, data, extra):
super().__init__(data)
self.extra = extra
>>> class OrderedList(List):
def __init__(self, data, extra, final):
super().__init__(data, extra)
self.final = final
>>> instance = Block(None)
>>> instance = List(None, None)
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
instance = List(None, None)
TypeError: Can't instantiate abstract class List with abstract methods __init__
>>> instance = OrderedList(None, None, None)
>>>
Your List class should have ABCMeta as a metaclass and make the init methods abstract.
from abc import ABCMeta
class List(metaclass=ABCMeta):
#abstractmethod
__init__():
pass
https://docs.python.org/3/library/abc.html
Inherit from ABC located in the abc module and make methods that are implemented in base classes (that inherit from List) #abstractmethods (a decorator located in abc):
from abc import ABC, abstractmethod
class List(ABC, Block):
#abstractmethod
def size(self):
return 0
Having an ABC with #abstractmethods defined forbids from instantiation.
The "one and obvious" way to do this is to use ABCMeta and mark some methods as abstract as documented on other answers.
But if in your case you don't have a set of methods that one has to override in a mandatory way (let's suppose your __init__ is reusable in some cases, and other of the list methods as well):
In that case you can create a __new__ method that checks if the clas being istantiated is the own class, and raises. To do that, you have to use teh magic __class__ variable that is documentend only in corners of Python docs - if you as much as use the __class__ variable in any method body, it will automatically take the value of the class where it was declared, at run time. It is part of the parameterless super mechanism of Python 3.
Thus:
class List(Block):
def __new__(cls, *args, **kw):
if cls is __class__:
raise TypeError(cls.__name__ + " can't be directly instantiated")
return super().__new__(cls, *args, **kw)
Btw, you should give preference for the ABCMeta abstractmethods if your pattern allows it. Note that if your classes use a custom metaclass it will conflict with the ABCMeta as well - so you may need to resort to this as well
(If you don't further customize __new__, then you'd better not pass args and kw upstream on the __new__ method: Python's object.__new__ ignore extra args if __init__ is defined but __new__ is not in the subclasses - but if both are defined it raises an error)
Related
class Borg:
"""Borg pattern making the class attributes global"""
_shared_data = {} # Attribute dictionary
def __init__(self):
self.__dict__ = self._shared_data # Make it an attribute dictionary
class Singleton(Borg): #Inherits from the Borg class
"""This class now shares all its attributes among its various instances"""
# This essentially makes the singleton objects an object-oriented global variable
def __init__(self, **kwargs):
Borg.__init__(self) # WHAT DOES THIS DO? Why is borg initialized with the self of this class?
self._shared_data.update(kwargs) # Update the attribute dictionary by inserting a new key-value pair
def __str__(self):
return str(self._shared_data) # Returns the attribute dictionary for printing
Borg.__init__(self) What does this do? What is borg initialized with the self of this class?
When you create an object of a derived class, Python only calls the __init__ special method for the most derived class. If ancestor classes require initialization, it is up to the programmer of the derived class to explicitely call __init__ on those classes.
What you show is the old Python 2 compatible idiom: Borg.__init__ is the unbound initialization method of the Borg class. If you call it you have to pass it the object as the first parameter, because in Python if a is an object from a A class having a m method with no parameters, a.m() is the same as A.m(a) - if there are parameters, they will just come after the object.
In this case and assuming Python 3, the most idiomatic way would be:
def __init__(self, **kwargs):
super().__init__() # exactly the same as Borg.__init__(self)
...
Only the "child init" will be executet.
So in your case if you call Singleton, only the Singleton init will be executet.
creating an instance of the Singleton class like this mySingleton = Singleton(someArgs) will call the constructor def __init__(self, **kwargs):initializing the new instance of your class. the first thing that is being initialized by the initializeation code block is to call the initialization of the underlying class Borg by calling Borg.__init__(self), this happens in pretty much all languages granted sometimes in a slightly different layout, but it has to happen in order to pass in any required arguments to the underlying base class
I have two classes that are supposed to implement the same test cases for two independent libraries (let's call them LibA and LibB). So far I define the test methods to be implemented in an abstract base class which ensures that both test classes implement all desired tests:
from abc import ABC, abstractmethod
class MyTests(ABC):
#abstractmethod
def test_foo(self):
pass
class TestsA(MyTests):
def test_foo(self):
pass
class TestsB(MyTests):
def test_foo(self):
pass
This works as expected, but what may still happen is that someone working on LibB accidentally adds a test_bar() method to TestB instead of the base class. The missing test_bar() in the TestA class would go unnoticed in that case.
Is there a way to prohibit the addition of new methods to an (abstract) base class? The objective is to force the addition of new methods to happen in the base class and thus force the implementation of new methods in all derived classes.
Yes. It can be done through a metaclass, or from Python 3.6 onwards, with a check in __init_subclass__ of the baseclass.
__init_sublass__ is a special method called by the language each time a subclass is instantiated. So it can check if the new class have any method that is not present in any of the superclasses and raise a TypeError when the subclass is declared. (__init_subclass__ is converted to a classmethod automatically)
class Base(ABC):
...
def __init_subclass__(cls, *args, **kw):
super().__init_subclass__(*args, **kw)
# By inspecting `cls.__dict__` we pick all methods declared directly on the class
for name, attr in cls.__dict__.items():
attr = getattr(cls, name)
if not callable(attr):
continue
for superclass in cls.__mro__[1:]:
if name in dir(superclass):
break
else:
# method not found in superclasses:
raise TypeError(f"Method {name} defined in {cls.__name__} does not exist in superclasses")
Note that unlike the TypeError raised by non-implemented abstractmethods, this error is raised at class declaration time, not class instantiation time. If the later is desired, you have to use a metaclass and move the check to its __call__ method - however that complicates things, as if one method is created in an intermediate class, that was never instantiated, it won't raise when the method is available in the leaf subclass. I guess what you need is more along the code above.
This question already has answers here:
Create an abstract Enum class
(3 answers)
Closed 3 years ago.
Simple Example
The goal is to create an abstract enum class through a metaclass deriving from both abc.ABCMeta and enum.EnumMeta. For example:
import abc
import enum
class ABCEnumMeta(abc.ABCMeta, enum.EnumMeta):
pass
class A(abc.ABC):
#abc.abstractmethod
def foo(self):
pass
class B(A, enum.IntEnum, metaclass=ABCEnumMeta):
X = 1
class C(A):
pass
Now, on Python3.7, this code will be interpreted without error (on 3.6.x and presumably below, it will not). In fact, everything looks great, our MRO shows B derived from both A and IntEnum.
>>> B.__mro__
(<enum 'B'>, __main__.A, abc.ABC, <enum 'IntEnum'>, int, <enum 'Enum'>, object)
Abstract Enum is not Abstract
However, even though B.foo has not been defined, we can still instantiate B without any issue, and call foo().
>>> B.X
<B.X: 1>
>>> B(1)
<B.X: 1>
>>> B(1).foo()
This seems rather weird, since any other class that derives from ABCMeta cannot be instantiated, even if I use a custom metaclass.
>>> class NewMeta(type):
... pass
...
... class AbcNewMeta(abc.ABCMeta, NewMeta):
... pass
...
... class D(metaclass=NewMeta):
... pass
...
... class E(A, D, metaclass=AbcNewMeta):
... pass
...
>>> E()
TypeError: Can't instantiate abstract class E with abstract methods foo
Question
Why does a class using a metaclass derived from EnumMeta and ABCMeta effectively ignore ABCMeta, while any other class using a metaclass derived from ABCMeta use it? This is true even if I custom define the __new__ operator.
class ABCEnumMeta(abc.ABCMeta, enum.EnumMeta):
def __new__(cls, name, bases, dct):
# Commented out lines reflect other variants that don't work
#return abc.ABCMeta.__new__(cls, name, bases, dct)
#return enum.EnumMeta.__new__(cls, name, bases, dct)
return super().__new__(cls, name, bases, dct)
I'm rather confused, since this seems to fly in the face of what a metaclass is: the metaclass should define how the class is defined and behaves, and in this case, defining a class using a metaclass that is both abstract and an enumeration creates a class that silently ignores the abstract component. Is this a bug, is this intended, or is there something greater I am not understanding?
As stated on #chepner's answer, what is going on is that Enum metaclass overrides the normal metaclass' __call__ method, so that an Enum class is never instantiated through the normal methods, and thus, ABCMeta checking does not trigger its abstractmethod check.
However, on class creation, the Metaclass's __new__ is run normally, and the attributes used by the abstract-class mechanisms to mark the class as abstract do create the attribute ___abstractmethods__ on the created class.
So, all you have to do for what you intend to work, is to further customize your metaclass to perform the abstract check in the code to __call__:
import abc
import enum
class ABCEnumMeta(abc.ABCMeta, enum.EnumMeta):
def __call__(cls, *args, **kw):
if getattr(cls, "__abstractmethods__", None):
raise TypeError(f"Can't instantiate abstract class {cls.__name__} "
f"with frozen methods {set(cls.__abstractmethods__)}")
return super().__call__(*args, **kw)
This will make the B(1) expression to fail with the same error as abstractclass instantiation.
Note, however, that an Enum class can't be further inherited anyway, and it simply creating it without the missing abstractmethods may already violate what you want to check. That is: in your example above, class B can be declared and B.x will work, even with the missing foo method. If you want to prevent that, just put the same check in the metaclass' __new__:
import abc
import enum
class ABCEnumMeta(abc.ABCMeta, enum.EnumMeta):
def __new__(mcls, *args, **kw):
cls = super().__new__(mcls, *args, **kw)
if issubclass(cls, enum.Enum) and getattr(cls, "__abstractmethods__", None):
raise TypeError("...")
return cls
def __call__(cls, *args, **kw):
if getattr(cls, "__abstractmethods__", None):
raise TypeError(f"Can't instantiate abstract class {cls.__name__} "
f"with frozen methods {set(cls.__abstractmethods__)}")
return super().__call__(*args, **kw)
(Unfortunatelly, the ABC abstract method check in CPython seems to be performed in native code, outside the ABCMeta.__call__ method - otherwise, instead of mimicking the error, we could just call ABCMeta.__call__ explicitly overriding super's behavior instead of hardcoding the TypeError there.)
Calling an enumerated type doesn't create a new instance. Members of the enumerated type are created immediately at class-creation time by the meta class. The __new__ method simply performs lookup, which means ABCMeta is never invoked to prevent instantiation.
B(1).foo() works because, once you have an instance, it doesn't matter if the method was marked as abstract. It's still a real method, and can be called as such.
I have a base class extending unittest.TestCase, and I want to patch that base class, such that classes extending this base class will have the patches applied as well.
Code Example:
#patch("some.core.function", mocked_method)
class BaseTest(unittest.TestCase):
#methods
pass
class TestFunctions(BaseTest):
#methods
pass
Patching the TestFunctions class directly works, but patching the BaseTest class does not change the functionality of some.core.function in TestFunctions.
You probably want a metaclass here: a metaclass simply defines how a class is created.
By default, all classes are created using Python's built-in class type:
>>> class Foo:
... pass
...
>>> type(Foo)
<class 'type'>
>>> isinstance(Foo, type)
True
So classes are actually instances of type.
Now, we can subclass type to create a custom metaclass (a class that creates classes):
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
We need to control the creation of our classes, so we wanna override the type.__new__ here, and use the patch decorator on all new instances:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __new__(meta, name, bases, attrs):
cls = type.__new__(meta, name, bases, attrs)
cls = patch("some.core.function", mocked_method)(cls)
return cls
And now you simply set the metaclass using __metaclass__ = PatchMeta:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
# methods
The issue is this line:
cls = patch("some.core.function", mocked_method)(cls)
So currently we always decorate with arguments "some.core.function" and mocked_method.
Instead you could make it so that it uses the class's attributes, like so:
cls = patch(*cls.patch_args)(cls)
And then add patch_args to your classes:
class BaseTest(unittest.TestCase):
__metaclass__ = PatchMeta
patch_args = ("some.core.function", mocked_method)
Edit: As #mgilson mentioned in the comments, patch() modifies the class's methods in place, instead of returning a new class. Because of this, we can replace the __new__ with this __init__:
class PatchMeta(type):
"""A metaclass to patch all inherited classes."""
def __init__(cls, *args, **kwargs):
super(PatchMeta, self).__init__(*args, **kwargs)
patch(*cls.patch_args)(cls)
Which is quite unarguably cleaner.
Generally, I prefer to do this sort of thing in setUp. You can make sure that the patch gets cleaned up after the test is completed by making use of the tearDown method (or alternatively, registering a the patch's stop method with addCleanup):
class BaseTest(unittest.TestCase):
def setUp(self):
super(BaseTest, self).setUp()
my_patch = patch("some.core.function", mocked_method)
my_patch.start()
self.addCleanup(my_patch.stop)
class TestFunctions(BaseTest):
#methods
pass
Provided that you're disciplined enough to always call super in your overridden setUp methods, it should work just fine.
I would like to declare a hierarchy of user-defined exceptions in Python. However, I would like my top-level user-defined class (TransactionException) to be abstract. That is, I intend TransactionException to specify methods that its subclasses are required to define. However, TransactionException should never be instantiated or raised.
I have the following code:
from abc import ABCMeta, abstractmethod
class TransactionException(Exception):
__metaclass__ = ABCMeta
#abstractmethod
def displayErrorMessage(self):
pass
However, the above code allows me to instantiate TransactionException...
a = TransactionException()
In this case a is meaningless, and should instead draw an exception. The following code removes the fact that TransactionException is a subclass of Exception...
from abc import ABCMeta, abstractmethod
class TransactionException():
__metaclass__ = ABCMeta
#abstractmethod
def displayErrorMessage(self):
pass
This code properly prohibits instantiation but now I cannot raise a subclass of TransactionException because it's not an Exception any longer.
Can one define an abstract exception in Python? If so, how? If not, why not?
NOTE: I'm using Python 2.7, but will happily accept an answer for Python 2.x or Python 3.x.
There's a great answer on this topic by Alex Martelli here. In essence, it comes down how the object initializers (__init__) of the various base classes (object, list, and, I presume, Exception) behave when abstract methods are present.
When an abstract class inherits from object (which is the default, if no other base class is given), its __init__ method is set to that of object's, which performs the heavy-lifting in checking if all abstract methods have been implemented.
If the abstract class inherits from a different base class, it will get that class' __init__ method. Other classes, such as list and Exception, it seems, do not check for abstract method implementation, which is why instantiating them is allowed.
The other answer provides a suggested workaround for this. Of course, another option that you have is simply to accept that the abstract class will be instantiable, and try to discourage it.
class TransactionException(Exception):
def __init__(self, *args, **kwargs):
raise NotImplementedError('you should not be raising this')
class EverythingLostException(TransactionException):
def __init__(self, msg):
super(TransactionException, self).__init__(msg)
try:
raise EverythingLostException('we are doomed!')
except TransactionException:
print 'check'
try:
raise TransactionException('we are doomed!')
except TransactionException:
print 'oops'
My implementation for an abstract exception class, in which the children of the class work out of the box.
class TransactionException(Exception):
def __init__(self):
self._check_abstract_initialization(self)
#staticmethod
def _check_abstract_initialization(self):
if type(self) == TransactionException:
raise NotImplementedError("TransactionException should not be instantiated directly")
class AnotherException(TransactionException):
pass
TransactionException() # NotImplementedError: TransactionException should not be instantiated directly
AnotherException # passes
Here's a helper function that can be used in such scenario:
def validate_abstract_methods(obj):
abstract_methods = []
for name in dir(obj):
value = getattr(obj, name, None)
if value is not None and getattr(value, '__isabstractmethod__', False):
abstract_methods.append(name)
if abstract_methods:
abstract_methods.sort()
raise TypeError(f"Can't instantiate abstract class {obj.__class__.__name__} with abstract methods {', '.join(abstract_methods)}")
This function roughly does the same thing as abc.ABC class - you just need to call it from your class' __init__ method.