How can I make a class or method abstract in Python?
I tried redefining __new__() like so:
class F:
def __new__(cls):
raise Exception("Unable to create an instance of abstract class %s" %cls)
But now, if I create a class G that inherits from F like so:
class G(F):
pass
Then, I can't instantiate G either, since it calls its super class's __new__ method.
Is there a better way to define an abstract class?
Use the abc module to create abstract classes. Use the abstractmethod decorator to declare a method abstract, and declare a class abstract using one of three ways, depending upon your Python version.
In Python 3.4 and above, you can inherit from ABC. In earlier versions of Python, you need to specify your class's metaclass as ABCMeta. Specifying the metaclass has different syntax in Python 3 and Python 2. The three possibilities are shown below:
# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
#abstractmethod
def foo(self):
pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
#abstractmethod
def foo(self):
pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
__metaclass__ = ABCMeta
#abstractmethod
def foo(self):
pass
Whichever way you use, you won't be able to instantiate an abstract class that has abstract methods, but will be able to instantiate a subclass that provides concrete definitions of those methods:
>>> Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
... pass
...
>>> StillAbstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
... def foo(self):
... print('Hello, World')
...
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>
The old-school (pre-PEP 3119) way to do this is just to raise NotImplementedError in the abstract class when an abstract method is called.
class Abstract(object):
def foo(self):
raise NotImplementedError('subclasses must override foo()!')
class Derived(Abstract):
def foo(self):
print 'Hooray!'
>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]
This doesn't have the same nice properties as using the abc module does. You can still instantiate the abstract base class itself, and you won't find your mistake until you call the abstract method at runtime.
But if you're dealing with a small set of simple classes, maybe with just a few abstract methods, this approach is a little easier than trying to wade through the abc documentation.
Here's a very easy way without having to deal with the ABC module.
In the __init__ method of the class that you want to be an abstract class, you can check the "type" of self. If the type of self is the base class, then the caller is trying to instantiate the base class, so raise an exception. Here's a simple example:
class Base():
def __init__(self):
if type(self) is Base:
raise Exception('Base is an abstract class and cannot be instantiated directly')
# Any initialization code
print('In the __init__ method of the Base class')
class Sub(Base):
def __init__(self):
print('In the __init__ method of the Sub class before calling __init__ of the Base class')
super().__init__()
print('In the __init__ method of the Sub class after calling __init__ of the Base class')
subObj = Sub()
baseObj = Base()
When run, it produces:
In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__ method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
baseObj = Base()
File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly
This shows that you can instantiate a subclass that inherits from a base class, but you cannot instantiate the base class directly.
Most Previous answers were correct but here is the answer and example for Python 3.7. Yes, you can create an abstract class and method. Just as a reminder sometimes a class should define a method which logically belongs to a class, but that class cannot specify how to implement the method. For example, in the below Parents and Babies classes they both eat but the implementation will be different for each because babies and parents eat a different kind of food and the number of times they eat is different. So, eat method subclasses overrides AbstractClass.eat.
from abc import ABC, abstractmethod
class AbstractClass(ABC):
def __init__(self, value):
self.value = value
super().__init__()
#abstractmethod
def eat(self):
pass
class Parents(AbstractClass):
def eat(self):
return "eat solid food "+ str(self.value) + " times each day"
class Babies(AbstractClass):
def eat(self):
return "Milk only "+ str(self.value) + " times or more each day"
food = 3
mom = Parents(food)
print("moms ----------")
print(mom.eat())
infant = Babies(food)
print("infants ----------")
print(infant.eat())
OUTPUT:
moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day
As explained in the other answers, yes you can use abstract classes in Python using the abc module. Below I give an actual example using abstract #classmethod, #property and #abstractmethod (using Python 3.6+). For me it is usually easier to start off with examples I can easily copy&paste; I hope this answer is also useful for others.
Let's first create a base class called Base:
from abc import ABC, abstractmethod
class Base(ABC):
#classmethod
#abstractmethod
def from_dict(cls, d):
pass
#property
#abstractmethod
def prop1(self):
pass
#property
#abstractmethod
def prop2(self):
pass
#prop2.setter
#abstractmethod
def prop2(self, val):
pass
#abstractmethod
def do_stuff(self):
pass
Our Base class will always have a from_dict classmethod, a property prop1 (which is read-only) and a property prop2 (which can also be set) as well as a function called do_stuff. Whatever class is now built based on Base will have to implement all of these four methods/properties. Please note that for a method to be abstract, two decorators are required - classmethod and abstract property.
Now we could create a class A like this:
class A(Base):
def __init__(self, name, val1, val2):
self.name = name
self.__val1 = val1
self._val2 = val2
#classmethod
def from_dict(cls, d):
name = d['name']
val1 = d['val1']
val2 = d['val2']
return cls(name, val1, val2)
#property
def prop1(self):
return self.__val1
#property
def prop2(self):
return self._val2
#prop2.setter
def prop2(self, value):
self._val2 = value
def do_stuff(self):
print('juhu!')
def i_am_not_abstract(self):
print('I can be customized')
All required methods/properties are implemented and we can - of course - also add additional functions that are not part of Base (here: i_am_not_abstract).
Now we can do:
a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})
a1.prop1
# prints 10
a1.prop2
# prints 'stuff'
As desired, we cannot set prop1:
a.prop1 = 100
will return
AttributeError: can't set attribute
Also our from_dict method works fine:
a2.prop1
# prints 20
If we now defined a second class B like this:
class B(Base):
def __init__(self, name):
self.name = name
#property
def prop1(self):
return self.name
and tried to instantiate an object like this:
b = B('iwillfail')
we will get an error
TypeError: Can't instantiate abstract class B with abstract methods
do_stuff, from_dict, prop2
listing all the things defined in Base which we did not implement in B.
This one will be working in python 3
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
#abstractmethod
def foo(self):
pass
Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo
also this works and is simple:
class A_abstract(object):
def __init__(self):
# quite simple, old-school way.
if self.__class__.__name__ == "A_abstract":
raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")
class B(A_abstract):
pass
b = B()
# here an exception is raised:
a = A_abstract()
You can also harness the __new__ method to your advantage. You just forgot something.
The __new__ method always returns the new object so you must return its superclass' new method. Do as follows.
class F:
def __new__(cls):
if cls is F:
raise TypeError("Cannot create an instance of abstract class '{}'".format(cls.__name__))
return super().__new__(cls)
When using the new method, you have to return the object, not the None keyword. That's all you missed.
I find the accepted answer, and all the others strange, since they pass self to an abstract class. An abstract class is not instantiated so can't have a self.
So try this, it works.
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
#staticmethod
#abstractmethod
def foo():
"""An abstract method. No need to write pass"""
class Derived(Abstract):
def foo(self):
print('Hooray!')
FOO = Derived()
FOO.foo()
from abc import ABCMeta, abstractmethod
#Abstract class and abstract method declaration
class Jungle(metaclass=ABCMeta):
#constructor with default values
def __init__(self, name="Unknown"):
self.visitorName = name
def welcomeMessage(self):
print("Hello %s , Welcome to the Jungle" % self.visitorName)
# abstract method is compulsory to defined in child-class
#abstractmethod
def scarySound(self):
pass
Late to answer here, but to answer the other question "How to make abstract methods" which points here, I offer the following.
# decorators.py
def abstract(f):
def _decorator(*_):
raise NotImplementedError(f"Method '{f.__name__}' is abstract")
return _decorator
# yourclass.py
class Vehicle:
def add_energy():
print("Energy added!")
#abstract
def get_make(): ...
#abstract
def get_model(): ...
The class base Vehicle class can still be instantiated for unit testing (unlike with ABC), and the Pythonic raising of an exception is present. Oh yes, you also get the method name that is abstract in the exception with this method for convenience.
You can create an abstract class by extending ABC which stands for "Abstract Base Classes" and can create the abstract method with #abstractmethod in the abstract class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
And, to use an abstract class, it should be extended by a child class and the child class should override the abstract method of the abstract class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
class Cat(Animal): # Extends "Animal" abstract class
def sound(self): # Overrides "sound()" abstract method
print("Meow!!")
obj = Cat()
obj.sound()
Output:
Meow!!
And, an abstract method can have code rather than pass and can be called by a child class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
print("Wow!!") # Here
class Cat(Animal):
def sound(self):
super().sound() # Here
obj = Cat()
obj.sound()
Output:
Wow!!
And, an abstract class can have the variables and non-abstract methods which can be called by a child class and non-abstract methods don't need to be overridden by a child class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
def __init__(self): # Here
self.name = "John" # Here
x = "Hello" # Here
def test1(self): # Here
print("Test1")
#classmethod # Here
def test2(cls):
print("Test2")
#staticmethod # Here
def test3():
print("Test3")
class Cat(Animal):
def sound(self):
print(self.name) # Here
print(super().x) # Here
super().test1() # Here
super().test2() # Here
super().test3() # Here
obj = Cat()
obj.sound()
Output:
John
Hello
Test1
Test2
Test3
And, you can define an abstract class and static methods and an abstract getter, setter and deleter in an abstract class as shown below. *#abstractmethod must be the innermost decorator otherwise error occurs and you can see my answer which explains more about an abstract getter, setter and deleter:
from abc import ABC, abstractmethod
class Person(ABC):
#classmethod
#abstractmethod # The innermost decorator
def test1(cls):
pass
#staticmethod
#abstractmethod # The innermost decorator
def test2():
pass
#property
#abstractmethod # The innermost decorator
def name(self):
pass
#name.setter
#abstractmethod # The innermost decorator
def name(self, name):
pass
#name.deleter
#abstractmethod # The innermost decorator
def name(self):
pass
Then, you need to override them in a child class as shown below:
class Student(Person):
def __init__(self, name):
self._name = name
#classmethod
def test1(cls): # Overrides abstract class method
print("Test1")
#staticmethod
def test2(): # Overrides abstract static method
print("Test2")
#property
def name(self): # Overrides abstract getter
return self._name
#name.setter
def name(self, name): # Overrides abstract setter
self._name = name
#name.deleter
def name(self): # Overrides abstract deleter
del self._name
Then, you can instantiate the child class and call them as shown below:
obj = Student("John") # Instantiates "Student" class
obj.test1() # Class method
obj.test2() # Static method
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))
Output:
Test1
Test2
John
Tom
False
And, if you try to instantiate an abstract class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
obj = Animal()
The error below occurs:
TypeError: Can't instantiate abstract class Animal with abstract methods sound
And, if you don't override the abstract method of an abstract class in a child class and you instantiate the child class as shown below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
class Cat(Animal):
pass # Doesn't override "sound()" abstract method
obj = Cat() # Here
The error below occurs:
TypeError: Can't instantiate abstract class Cat with abstract methods sound
And, if you define an abstract method in the non-abstract class which doesn't extend ABC, the abstract method is a normal instance method so there are no errors even if the non-abstract class is instantiated and even if a child class doesn't override the abstract method of the non-abstract class as shown below:
from abc import ABC, abstractmethod
class Animal: # Doesn't extend "ABC"
#abstractmethod # Here
def sound(self):
print("Wow!!")
class Cat(Animal):
pass # Doesn't override "sound()" abstract method
obj1 = Animal() # Here
obj1.sound()
obj2 = Cat() # Here
obj2.sound()
Output:
Wow!!
Wow!!
In addition, you can replace Cat class extending Animal class below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
# ↓↓↓ Here ↓↓↓
class Cat(Animal):
def sound(self):
print("Meow!!")
# ↑↑↑ Here ↑↑↑
print(issubclass(Cat, Animal))
With this code having register() below:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def sound(self):
pass
# ↓↓↓ Here ↓↓↓
class Cat:
def sound(self):
print("Meow!!")
Animal.register(Cat)
# ↑↑↑ Here ↑↑↑
print(issubclass(Cat, Animal))
Then, both of the code above outputs the same result below showing Cat class is the subclass of Animal class:
True
In your code snippet, you could also resolve this by providing an implementation for the __new__ method in the subclass, likewise:
def G(F):
def __new__(cls):
# do something here
But this is a hack and I advise you against it, unless you know what you are doing. For nearly all cases I advise you to use the abc module, that others before me have suggested.
Also when you create a new (base) class, make it subclass object, like this: class MyBaseClass(object):. I don't know if it is that much significant anymore, but it helps retain style consistency on your code
Just a quick addition to #TimGilbert's old-school answer...you can make your abstract base class's init() method throw an exception and that would prevent it from being instantiated, no?
>>> class Abstract(object):
... def __init__(self):
... raise NotImplementedError("You can't instantiate this class!")
...
>>> a = Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
NotImplementedError: You can't instantiate this class!
Related
In the following code, I create a base abstract class Base. I want all the classes that inherit from Base to provide the name property, so I made this property an #abstractmethod.
Then I created a subclass of Base, called Base_1, which is meant to supply some functionality, but still remain abstract. There is no name property in Base_1, but nevertheless python instatinates an object of that class without an error. How does one create abstract properties?
from abc import ABCMeta, abstractmethod
class Base(object):
# class Base(metaclass = ABCMeta): <- Python 3
__metaclass__ = ABCMeta
def __init__(self, str_dir_config):
self.str_dir_config = str_dir_config
#abstractmethod
def _do_stuff(self, signals):
pass
#property
#abstractmethod
def name(self):
"""This property will be supplied by the inheriting classes
individually.
"""
pass
class Base1(Base):
__metaclass__ = ABCMeta
"""This class does not provide the name property and should
raise an error.
"""
def __init__(self, str_dir_config):
super(Base1, self).__init__(str_dir_config)
# super().__init__(str_dir_config) <- Python 3
def _do_stuff(self, signals):
print "Base_1 does stuff"
# print("Base_1 does stuff") <- Python 3
class C(Base1):
#property
def name(self):
return "class C"
if __name__ == "__main__":
b1 = Base1("abc")
Since Python 3.3 a bug was fixed meaning the property() decorator is now correctly identified as abstract when applied to an abstract method.
Note: Order matters, you have to use #property above #abstractmethod
Python 3.3+: (python docs):
from abc import ABC, abstractmethod
class C(ABC):
#property
#abstractmethod
def my_abstract_property(self):
...
Python 2: (python docs)
from abc import ABC, abstractproperty
class C(ABC):
#abstractproperty
def my_abstract_property(self):
...
Until Python 3.3, you cannot nest #abstractmethod and #property.
Use #abstractproperty to create abstract properties (docs).
from abc import ABCMeta, abstractmethod, abstractproperty
class Base(object):
# ...
#abstractproperty
def name(self):
pass
The code now raises the correct exception:
Traceback (most recent call last):
File "foo.py", line 36, in
b1 = Base_1('abc')
TypeError: Can't instantiate abstract class Base_1 with abstract methods name
Based on James answer above
def compatibleabstractproperty(func):
if sys.version_info > (3, 3):
return property(abstractmethod(func))
else:
return abstractproperty(func)
and use it as a decorator
#compatibleabstractproperty
def env(self):
raise NotImplementedError()
In python 3.6+, you can also anotate a variable without providing a default. I find this to be a more concise way to make it abstract.
class Base():
name: str
def print_name(self):
print(self.name) # will raise an Attribute error at runtime if `name` isn't defined in subclass
class Base_1(Base):
name = "base one"
it may also be used to force you to initialize the variable in the __new__ or __init__ methods
As another example, the following code will fail when you try to initialize the Base_1 class
class Base():
name: str
def __init__(self):
self.print_name()
class Base_1(Base):
_nemo = "base one"
b = Base_1()
AttributeError: 'Base_1' object has no attribute 'name'
Using the #property decorator in the abstract class (as recommended in the answer by James) works if you want the required instance level attributes to use the property decorator as well.
If you don't want to use the property decorator, you can use super(). I ended up using something like the __post_init__() from dataclasses and it gets the desired functionality for instance level attributes:
import abc
from typing import List
class Abstract(abc.ABC):
"""An ABC with required attributes.
Attributes:
attr0
attr1
"""
#abc.abstractmethod
def __init__(self):
"""Forces you to implement __init__ in 'Concrete'.
Make sure to call __post_init__() from inside 'Concrete'."""
def __post_init__(self):
self._has_required_attributes()
# You can also type check here if you want.
def _has_required_attributes(self):
req_attrs: List[str] = ['attr0', 'attr1']
for attr in req_attrs:
if not hasattr(self, attr):
raise AttributeError(f"Missing attribute: '{attr}'")
class Concrete(Abstract):
def __init__(self, attr0, attr1):
self.attr0 = attr0
self.attr1 = attr1
self.attr2 = "some value" # not required
super().__post_init__() # Enforces the attribute requirement.
For example, you can define the abstract getter, setter and deleter with #abstractmethod and #property, #name.setter or #name.deleter in Person abstract class as shown below. *#abstractmethod must be the innermost decorator otherwise error occurs:
from abc import ABC, abstractmethod
class Person(ABC):
#property
#abstractmethod # The innermost decorator
def name(self): # Abstract getter
pass
#name.setter
#abstractmethod # The innermost decorator
def name(self, name): # Abstract setter
pass
#name.deleter
#abstractmethod # The innermost decorator
def name(self): # Abstract deleter
pass
Then, you can extend Person abstract class with Student class, override the abstract getter, setter and deleter in Student class, instantiate Student class and call the getter, setter and deleter as shown below:
class Student(Person):
def __init__(self, name):
self._name = name
#property
def name(self): # Overrides abstract getter
return self._name
#name.setter
def name(self, name): # Overrides abstract setter
self._name = name
#name.deleter
def name(self): # Overrides abstract deleter
del self._name
obj = Student("John") # Instantiates "Student" class
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))
Output:
John
Tom
False
Actually, even if you don't override the abstract setter and deleter in Student class and instantiate Student class as shown below:
class Student(Person): # Extends "Person" class
def __init__(self, name):
self._name = name
#property
def name(self): # Overrides only abstract getter
return self._name
# #name.setter
# def name(self, name): # Overrides abstract setter
# self._name = name
# #name.deleter
# def name(self): # Overrides abstract deleter
# del self._name
obj = Student("John") # Instantiates "Student" class
# ...
No error occurs as shown below:
John
Tom
False
But, if you don't override the abstract getter, setter and deleter in Student class and instantiate Student class as shown below:
class Student(Person): # Extends "Person" class
def __init__(self, name):
self._name = name
# #property
# def name(self): # Overrides only abstract getter
# return self._name
# #name.setter
# def name(self, name): # Overrides abstract setter
# self._name = name
# #name.deleter
# def name(self): # Overrides abstract deleter
# del self._name
obj = Student("John") # Instantiates "Student" class
# ...
The error below occurs:
TypeError: Can't instantiate abstract class Student with abstract methods name
And, if you don't override the abstract getter in Student class and instantiate Student class as shown below:
class Student(Person): # Extends "Person" class
def __init__(self, name):
self._name = name
# #property
# def name(self): # Overrides only abstract getter
# return self._name
#name.setter
def name(self, name): # Overrides abstract setter
self._name = name
#name.deleter
def name(self): # Overrides abstract deleter
del self._name
obj = Student("John") # Instantiates "Student" class
# ...
The error below occurs:
NameError: name 'name' is not defined
And, if #abstractmethod is not the innermost decorator as shown below:
from abc import ABC, abstractmethod
class Person(ABC):
#abstractmethod # Not the innermost decorator
#property
def name(self): # Abstract getter
pass
#name.setter
#abstractmethod # The innermost decorator
def name(self, name): # Abstract setter
pass
#name.deleter
#abstractmethod # The innermost decorator
def name(self): # Abstract deleter
pass
The error below occurs:
AttributeError: attribute 'isabstractmethod' of 'property' objects is not writable
Another possible solution is to use metaclasses.
A minimal example can look like this:
class BaseMetaClass(type):
def __new__(mcls, class_name, bases, attrs):
required_attrs = ('foo', 'bar')
for attr in required_attrs:
if not attr in attrs:
raise RunTimeError(f"You need to set {attr} in {class_name}")
return super().__new__(mcls, class_name, bases, attrs)
class Base(metaclass=BaseMeta):
foo: str
bar: int
One advantage of this approach is that the check will happen at definition time (not instantiation).
Also, setting class attributes in child classes is a bit easier than declaring properties (as long as they are simple values known in advance) and your final classes will look more concise
I have a free-standing function, that uses super(), and using it like this:
import typing
from abc import ABC
class Foo(typing.Protocol):
def foo(): raise NotImplementedError
def free_foo(child):
super(child.__class__, child).foo()
class Base(ABC):
pass
class Parent1(Base):
def foo(self): pass
class Child1(Parent1):
foo = free_foo
class Parent2(Base):
def foo(self): pass
class Child2(Parent2):
foo = free_foo
How do I annotate free_foo function? I need to indicate that child is both instance of Base and comply with Foo protocol?
I figured out that right order of parent classes would do the trick:
from abc import ABC
class FooBase(Foo, Base, ABC):
pass
def foo(child: FooBase): pass
mypy seems happy on this one.
In Python 3, I have class Animal as an abstract base class, with abstract method give_name(self). Derived classes include Dog and Cat.
The start of the contents of give_name() method can can differ across subclasses. For example, in the Dog subclass, there might be print("bark") while in the Cat subclass, there may be print("meow").
However, I want to make sure that in all derived classes, give_name() ends with return self.__class__.__name__. How can I enforce this constraint (as elegantly as possible)?
Current Code:
from abc import ABC, abstractmethod
class Animal(ABC):
#abstractmethod
def give_name(self):
pass
class Dog(Animal):
def give_name(self):
print("bark")
return self.__class__.__name__
class Cat(Animal):
def give_name(self):
print("meow")
return self.__class__.__name__
You could implement give_name() in the superclass, but delegate the print part to subclasses, e.g.
from abc import ABC, abstractmethod
class Animal(ABC):
def give_name(self):
self.print()
return self.__class__.__name__
#abstractmethod
def print(self):
pass
class Dog(Animal):
def print(self):
print("bark")
class Cat(Animal):
def print(self):
print("meow")
parent.py:
class A(object):
def methodA(self):
print("in methodA")
child.py:
from parent import A
class B(A):
def methodb(self):
print("am in methodb")
Is there anyway to call methodb() in parent.py?
Doing this would only make sense if A is an abstract base class, meaning that A is only meant to be used as a base for other classes, not instantiated directly. If that were the case, you would define methodB on class A, but leave it unimplemented:
class A(object):
def methodA(self):
print("in methodA")
def methodB(self):
raise NotImplementedError("Must override methodB")
from parent import A
class B(A):
def methodB(self):
print("am in methodB")
This isn't strictly necessary. If you don't declare methodB anywhere in A, and instantiate B, you'd still be able to call methodB from the body of methodA, but it's a bad practice; it's not clear where methodA is supposed to come from, or that child classes need to override it.
If you want to be more formal, you can use the Python abc module to declare A as an abstract base class.
from abc import ABC, abstractmethod
class A(ABC):
def methodA(self):
print("in methodA")
#abstractmethod
def methodB(self):
raise NotImplementedError("Must override methodB")
Or if using Python 2.x:
from abc import ABCMeta, abstractmethod
class A(object):
__metaclass__ = ABCMeta
def methodA(self):
print("in methodA")
#abstractmethod
def methodB(self):
raise NotImplementedError("Must override methodB")
Using this will actually prevent you from instantiating A or any class that inherits from A without overriding methodB. For example, if B looked like this:
class B(A):
pass
You'd get an error trying to instantiate it:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class B with abstract methods methodB
The same would happen if you tried instantiating A.
You can do something like this:
class A():
def foo(self):
self.testb()
class B(A):
def testb(self):
print('lol, it works')
b = B()
b.foo()
Which would return this of course:
lol, it works
Note, that in fact there is no call from parent, there is just call of function foo from instance of child class, this instance has inherited foo from parent, i.e. this is impossible:
a=A()
a.foo()
will produce:
AttributeError: A instance has no attribute 'testb'
because
>>> dir(A)
['__doc__', '__module__', 'foo']
>>> dir(B)
['__doc__', '__module__', 'foo', 'testb']
What I've wanted to show that you can create instance of child class, and it will have all methods and parameters from both parent and it's own classes.
There are three approaches/ways to do this ! but I highly recommend to use the approach #3 because composition/decoupling has certain benefits in terms of design pattern. (GOF)
## approach 1 inheritance
class A():
def methodA(self):
print("in methodA")
def call_mehtodB(self):
self.methodb()
class B(A):
def methodb(self):
print("am in methodb")
b=B()
b.call_mehtodB()
## approach 2 using abstract method still class highly coupled
from abc import ABC, abstractmethod
class A(ABC):
def methodA(self):
print("in methodA")
#abstractmethod
def methodb(self):
pass
class B(A):
def methodb(self):
print("am in methodb")
b=B()
b.methodb()
#approach 3 the recommended way ! Composition
class A():
def __init__(self, message):
self.message=message
def methodA(self):
print(self.message)
class B():
def __init__(self,messageB, messageA):
self.message=messageB
self.a=A(messageA)
def methodb(self):
print(self.message)
def methodA(self):
print(self.a.message)
b=B("am in methodb", "am in methodA")
b.methodb()
b.methodA()
You could use the function anywhere so long as it was attached to an object, which it appears to be from your sample. If you have a B object, then you can use its methodb() function from absolutely anywhere.
parent.py:
class A(object):
def methoda(self):
print("in methoda")
def aFoo(obj):
obj.methodb()
child.py
from parent import A
class B(A):
def methodb(self):
print("am in methodb")
You can see how this works after you import:
>>> from parent import aFoo
>>> from child import B
>>> obj = B()
>>> aFoo(obj)
am in methodb
Granted, you will not be able to create a new B object from inside parent.py, but you will still be able to use its methods if it's passed in to a function in parent.py somehow.
If the both class in same .py file then you can directly call child class method from parents class.
It gave me warning but it run well.
class A(object):
def methodA(self):
print("in methodA")
Self.methodb()
class B(A):
def methodb(self):
print("am in methodb")
You can certainly do this -
parent.py
class A(object):
def __init__(self,obj):
self.obj_B = obj
def test(self):
self.obj_B.methodb()
child.py
from parent import A
class B(A):
def __init__(self,id):
self.id = id
super().__init__(self)
def methodb(self):
print("in method b with id:",self.id)
Now if you want to call it from class B object
b1 = B(1)
b1.test()
>>> in method b with id: 1
Or if you want to call it from class A object
b2 = B(2)
a = A(b2)
a.test()
>>> in method b with id: 2
You can even make new objects in super class by invoking class dict objects of the object passed to super class from child class.
I have a base class that define a couple of empty methods. I would like to enforce/make mandatory the definition of these methods in the subclass AND make it crash at init in case they are not overriden.
Ex:
class Shape:
def __init__(self, name):
self.name = name
def number_of_edges(self):
pass # method HAS TO be overloaded in subclass
class Triangle(Shape):
def __init__(self, name):
super(Triangle, self).__init__()
def number_of_edges(self):
return 3
It seems like the Python way is to raise a NotImplementedError:
def number_of_edges(self):
raise(NotImplementedError)
But it could be beneficial to crash early and detect the lack of implementation during the class instantiation. Why is this not the default, and can it be done?
Use the abc module to create an Abstract Base Class.
import abc
class Shape(object):
__metaclass__ = abc.ABCMeta
def __init__(self, name):
self.name = name
#abc.abstractmethod
def number_of_edges(self):
pass
Any method decorated with the #abc.abstractmethod decorator will trigger a TypeError exception:
>>> import abc
>>> class Shape(object):
... __metaclass__ = abc.ABCMeta
... def __init__(self, name):
... self.name = name
... #abc.abstractmethod
... def number_of_edges(self):
... pass
...
>>>
>>> class Triangle(Shape):
... pass
...
>>> Triangle('t3')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Triangle with abstract methods number_of_edges
>>> class Triangle(Shape):
... def number_of_edges(self):
... return 3
...
>>> Triangle('t3')
<__main__.Triangle object at 0x105f3b8d0>
You can be use Abstact Base Class;
import abc
class Shape:
__metaclass__ = abc.ABCMeta
def __init__(self, name):
self.name = name
#abc.abstractmethod
def number_of_edges(self):
pass # method HAS TO be overloaded in subclass
Now, if Triangle class doesn't override the number_of_edges method, it will raise exception.