I am trying to define an abstract class with an abstract property that can then be inherited in a concrete class. But it is giving me maximum recursion depth exceeded.
Vechile is an abstract class and Car implements that class. The base class has a property called parking_ticket which I am keeping out of the __init__ of the abstract class and defining a property called parking_ticket to act as a getter and setter. Please help me to point out where is the mistake.
Abstract Class
"""
module string
"""
from abc import ABC, abstractmethod
from vehicle_type import VehicleType
from parking_ticket import ParkingTicket
class Vechile(ABC):
"""
class docstring
"""
#abstractmethod
def __init__(self, license_plate: str,
kind_of_vehicle: VehicleType) -> None:
self.plate_number = license_plate
self.vehicle_type = kind_of_vehicle
self.parking_ticket = None
#property
#abstractmethod
def parking_ticket(self):
pass
#parking_ticket.setter
#abstractmethod
def parking_ticket(self, parking_ticket: ParkingTicket):
pass
Concrete Class
from vehicle import Vechile
from vehicle_type import VehicleType
from parking_ticket import ParkingTicket
from datetime import datetime, timedelta
class Car(Vechile):
def __init__(self,
license_plate: str) -> None:
super().__init__(license_plate, VehicleType.CAR)
#property
def parking_ticket(self):
return self.parking_ticket
#parking_ticket.setter
def parking_ticket(self,
ticket: ParkingTicket):
self.parking_ticket = ticket
p_ticket = ParkingTicket(ticket_number="123",
plate_number="12345",
allocated_spot_id=10,
issued_at=datetime.utcnow(),
vaccated_at=datetime.utcnow() + timedelta(hours=5),
charges=50
)
c = Car('MH53TS7618')
c.parking_ticket = p_ticket
print(c)
found the answer for it. I was using the same variable name as the method name and instance variable. hence the run time exception was occuring.
Related
I recently came across Python's metaclass concept and I am trying to enforce Singleton for a few existing classes. I want to reuse the previously created instance when the class is instantiated again.
Here is my attempt at it. But the singleton condition doesn't seem to get enforced.
The existing class that should enforce Singleton. I have multiple such classes like Bike, Train which inherits Vehicle and all these should be made singleton.
from singleton import Singleton
from vehicle import Vehicle
class Car(Vehicle, object):
__metaclass__ = Singleton
def __init__(self):
print("Constructor: Car is starting")
pass
def print_car(self):
print("Car is running")
Car class inherits a Vehicle
from abc import ABC
class Vehicle(ABC):
def __init__(self):
pass
Here is my attempt at Singleton. I used a few reference SO posts.
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
However, when I create multiple objects for Car, I don't see singleton being enforced.
car = Car()
car.print_car()
car2 = Car()
car2.print_car()
print(car is car2) # Returns False
As I am still learning Python's metaclass and how it works, wondering what I am missing here?
All these classes (base class and derived class) are in my control and I can make changes to them. How can I achieve enforcing singleton to my sub-classes like Car, Bike etc?
[Update 1]
Using Python 3 version of metaclass gives me the below error
from singleton import Singleton
from vehicle import Vehicle
class Car(Vehicle, object, metaclass=Singleton):
# class Car(Vehicle, object):
# __metaclass__ = Singleton
def __init__(self):
print("Constructor: Car is starting")
def print_car(self):
print("Car is running")
Error:
File "/Users/user/HelloVsCode/hello.py", line 5, in <module>
from car import Car
File "/Users/user/HelloVsCode/car.py", line 7, in <module>
class Car(Vehicle, object, metaclass=Singleton):
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
First:
Metaclasses are overkill for a singleton.
If you want a singleton, you can just instantiate the class right after it is declared, and throw away the "class" and keep only its instance.
Like in:
class _Sing1:
...
Sing1 = _Sing1()
# done.
If you want the singleton to behave like a class, that is called, and them will yield always the same instance, a decorator that injects a __call__method and instantiate the class at once is also simple enough, and can be used across several classes:
def singleton(cls):
cls.__call__ = lambda self: self
return cls()
#singleton
class Sing2:
...
This should be enough for you, unless your singleton instances need to be callables themselves.
Other than that, the recipe you found around to create singletons using a metaclass works - what is wrong is how to specify that a class should use that as a metaclass.
The syntax you used in your post, declaring a __metaclass__ attribute inside the class body, was Python 2 way of determining the use of a custom metaclass. In Python 3, we simply specify the metaclass in the metaclas kwarg when declaring the class.
That means, this will work, with the metaclass above, to make your "Car" class work as a singleton factory:
class Car(Vehicle, object, metaclass=Singleton):
def __init__(self):
print("Constructor: Car is starting")
def print_car(self):
print("Car is running")
update
If you want to use ABCMeta along the metaclass for singletons antipattern, your metaclass have to inherit from ABCMeta - otherwise you will have a conflict of metaclasses:
import abc
class Singleton(abc.ABCMeta):
def __call__(...):
...
Thanks to #jsbueno for the answer. Here are two solutions that solved my use case.
Method 1: A decorator
With this approach, I can use a decorator for every derived class.
singleton.py
import functools
# https://github.com/siddheshsathe/handy-decorators/blob/master/src/decorators.py
def singleton(cls):
previous_instances = {}
#functools.wraps(cls)
def wrapper(*args, **kwargs):
if cls in previous_instances and previous_instances.get(cls, None).get('args') == (args, kwargs):
return previous_instances[cls].get('instance')
else:
previous_instances[cls] = {
'args': (args, kwargs),
'instance': cls(*args, **kwargs)
}
return previous_instances[cls].get('instance')
return wrapper
car.py
from singleton import singleton
from vehicle import Vehicle
#singleton
class Car(Vehicle):
def __init__(self):
print("Constructor: Car is starting")
def print_car(self):
print("Car is running")
vehicle.py
from abc import ABC
class Vehicle(ABC):
def __init__(self, vehicle_type, can_fly: bool = False, enable_timeout: bool = False):
self._vehicle_type = vehicle_type
self._can_fly = can_fly
self._enable_timeout = enable_timeout
Method 2: Metaclass
Ref: https://stackoverflow.com/a/33364149/73137
With this approach, I can make my base class as singleton. Thus enforcing it on all derived classes.
singleton.py
class Singleton(ABCMeta):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
car.py
from vehicle import Vehicle
class Car(Vehicle):
def __init__(self):
print("Constructor: Car is starting")
def print_car(self):
print("Car is running")
vehicle.py
from abc import ABC
from singleton import Singleton
class Vehicle(ABC, metaclass=Singleton):
def __init__(self, vehicle_type, can_fly: bool = False, enable_timeout: bool = False):
self._vehicle_type = vehicle_type
self._can_fly = can_fly
self._enable_timeout = enable_timeout
Common Code:
test.py
from car import Car
car = Car()
car.print_car()
car2 = Car()
car2.print_car()
print(car is car2)
output:
Constructor: Car is starting
Car is running
Car is running
True
As I am still new to the world of Python's metaclass and singleton, let me know if there are any specific pros and cons between the two approaches.
class MyClass(models.Model):
CONSTANT = "value"
...
class Meta:
# I want to Access CONSTANT here
I know a simple solution of hardcoding the "value" inside class Meta, but is there a way to access the outer class CONSTANT. I tried CONSTANT and MyClass.CONSTANT inside class Meta, but these two ways did not work.
class MyClass:
CONST = 'val from MyClass'
def __init__(self):
self.meta = self.Meta()
class Meta:
def __init__(self):
self.value = MyClass.CONST
if __name__ == '__main__':
my_class = MyClass()
print(my_class.meta.value)
# something like this
My goal:
class BaseClass1:
def f(self)
class BaseClass2:
def f(self)
class DerivedClass:
def g(self):
self.f() # this is either BaseClass1::f() or BaseClass2::f(), depending on instantiation
Where DerivedClass is can be instanced either:
class DerivedClass(BaseClass1)
or
class DerivedClass(BaseClass2)
I understand that this can solve the problem itself:
class UtilityClass1:
def f(self)
class UtilityClass2:
def f(self)
class DerivedClass:
def __init__(self, utilityInstance):
self.utility = utilityInstance
def g(self):
self.utility.f()
d = DerivedClass(UtilityClass1())
d = DerivedClass(UtilityClass2())
However, I specifically want to know, if there is another way, through inheritance (probably using some decorators, or whatever).
Just to wrap things up:
INPUT:
3 class definitions:
class BaseClass1
class BaseClass2
class DerivedClass
OUTPUT:
2 "merged" class instances:
d1 = DerivedClass(BaseClass1)
d2 = DerivedClass(BaseClass2)
I have an abstract class that i will use as template to implement different kind of subclasses.
I want to define some attributes/methods that are mandatory to all subclasses. For example
class BlockModel(ABC):
def __init__(self, position):
self.__position = position
self.__lives = None
self.__hitbox = None
#property
def lives(self):
return self.__lives
#lives.setter
def lives(self, lives):
self.__lives = lives
#property
def hitbox(self):
return self.__hitbox
#hitbox.setter
def hitbox(self, hitbox):
self.__hitbox = hitbox
#abstractmethod
def method1(self)
#some abstract methods
#abstractmethod
def method2(self)
#some abstract methods
When i create a subclass, for example
class Block1(BlockModel):
def __init__(self,position_):
super().__init__(position_)
self.__lives=1
self.__hitbox = pygame.Rect(self.__position['x'],
self.__position['y'],
5,
5)
#Implement abstract methods
The second class doesn't inherit the attributes __position, __lives, __hitbox, but the public ones without the underscores (i know that there are no real private attributes/methods in python). There s a way too keep them private(with underscores) in the subclass too?
What's the best practice to define an abstract instance attribute, but not as a property?
I would like to write something like:
class AbstractFoo(metaclass=ABCMeta):
#property
#abstractmethod
def bar(self):
pass
class Foo(AbstractFoo):
def __init__(self):
self.bar = 3
Instead of:
class Foo(AbstractFoo):
def __init__(self):
self._bar = 3
#property
def bar(self):
return self._bar
#bar.setter
def setbar(self, bar):
self._bar = bar
#bar.deleter
def delbar(self):
del self._bar
Properties are handy, but for simple attribute requiring no computation they are an overkill. This is especially important for abstract classes which will be subclassed and implemented by the user (I don't want to force someone to use #property when he just could have written self.foo = foo in the __init__).
Abstract attributes in Python question proposes as only answer to use #property and #abstractmethod: it doesn't answer my question.
The ActiveState recipe for an abstract class attribute via AbstractAttribute may be the right way, but I am not sure. It also only works with class attributes and not instance attributes.
A possibly a bit better solution compared to the accepted answer:
from better_abc import ABCMeta, abstract_attribute # see below
class AbstractFoo(metaclass=ABCMeta):
#abstract_attribute
def bar(self):
pass
class Foo(AbstractFoo):
def __init__(self):
self.bar = 3
class BadFoo(AbstractFoo):
def __init__(self):
pass
It will behave like this:
Foo() # ok
BadFoo() # will raise: NotImplementedError: Can't instantiate abstract class BadFoo
# with abstract attributes: bar
This answer uses same approach as the accepted answer, but integrates well with built-in ABC and does not require boilerplate of check_bar() helpers.
Here is the better_abc.py content:
from abc import ABCMeta as NativeABCMeta
class DummyAttribute:
pass
def abstract_attribute(obj=None):
if obj is None:
obj = DummyAttribute()
obj.__is_abstract_attribute__ = True
return obj
class ABCMeta(NativeABCMeta):
def __call__(cls, *args, **kwargs):
instance = NativeABCMeta.__call__(cls, *args, **kwargs)
abstract_attributes = {
name
for name in dir(instance)
if getattr(getattr(instance, name), '__is_abstract_attribute__', False)
}
if abstract_attributes:
raise NotImplementedError(
"Can't instantiate abstract class {} with"
" abstract attributes: {}".format(
cls.__name__,
', '.join(abstract_attributes)
)
)
return instance
The nice thing is that you can do:
class AbstractFoo(metaclass=ABCMeta):
bar = abstract_attribute()
and it will work same as above.
Also one can use:
class ABC(ABCMeta):
pass
to define custom ABC helper. PS. I consider this code to be CC0.
This could be improved by using AST parser to raise earlier (on class declaration) by scanning the __init__ code, but it seems to be an overkill for now (unless someone is willing to implement).
2021: typing support
You can use:
from typing import cast, Any, Callable, TypeVar
R = TypeVar('R')
def abstract_attribute(obj: Callable[[Any], R] = None) -> R:
_obj = cast(Any, obj)
if obj is None:
_obj = DummyAttribute()
_obj.__is_abstract_attribute__ = True
return cast(R, _obj)
which will let mypy highlight some typing issues
class AbstractFooTyped(metaclass=ABCMeta):
#abstract_attribute
def bar(self) -> int:
pass
class FooTyped(AbstractFooTyped):
def __init__(self):
# skipping assignment (which is required!) to demonstrate
# that it works independent of when the assignment is made
pass
f_typed = FooTyped()
_ = f_typed.bar + 'test' # Mypy: Unsupported operand types for + ("int" and "str")
FooTyped.bar = 'test' # Mypy: Incompatible types in assignment (expression has type "str", variable has type "int")
FooTyped.bar + 'test' # Mypy: Unsupported operand types for + ("int" and "str")
and for the shorthand notation, as suggested by #SMiller in the comments:
class AbstractFooTypedShorthand(metaclass=ABCMeta):
bar: int = abstract_attribute()
AbstractFooTypedShorthand.bar += 'test' # Mypy: Unsupported operand types for + ("int" and "str")
Just because you define it as an abstractproperty on the abstract base class doesn't mean you have to make a property on the subclass.
e.g. you can:
In [1]: from abc import ABCMeta, abstractproperty
In [2]: class X(metaclass=ABCMeta):
...: #abstractproperty
...: def required(self):
...: raise NotImplementedError
...:
In [3]: class Y(X):
...: required = True
...:
In [4]: Y()
Out[4]: <__main__.Y at 0x10ae0d390>
If you want to initialise the value in __init__ you can do this:
In [5]: class Z(X):
...: required = None
...: def __init__(self, value):
...: self.required = value
...:
In [6]: Z(value=3)
Out[6]: <__main__.Z at 0x10ae15a20>
Since Python 3.3 abstractproperty is deprecated. So Python 3 users should use the following instead:
from abc import ABCMeta, abstractmethod
class X(metaclass=ABCMeta):
#property
#abstractmethod
def required(self):
raise NotImplementedError
If you really want to enforce that a subclass define a given attribute, you can use metaclasses:
class AbstractFooMeta(type):
def __call__(cls, *args, **kwargs):
"""Called when you call Foo(*args, **kwargs) """
obj = type.__call__(cls, *args, **kwargs)
obj.check_bar()
return obj
class AbstractFoo(object):
__metaclass__ = AbstractFooMeta
bar = None
def check_bar(self):
if self.bar is None:
raise NotImplementedError('Subclasses must define bar')
class GoodFoo(AbstractFoo):
def __init__(self):
self.bar = 3
class BadFoo(AbstractFoo):
def __init__(self):
pass
Basically the meta class redefine __call__ to make sure check_bar is called after the init on an instance.
GoodFoo() # ok
BadFoo () # yield NotImplementedError
As Anentropic said, you don't have to implement an abstractproperty as another property.
However, one thing all answers seem to neglect is Python's member slots (the __slots__ class attribute). Users of your ABCs required to implement abstract properties could simply define them within __slots__ if all that's needed is a data attribute.
So with something like,
class AbstractFoo(abc.ABC):
__slots__ = ()
bar = abc.abstractproperty()
Users can define sub-classes simply like,
class Foo(AbstractFoo):
__slots__ = 'bar', # the only requirement
# define Foo as desired
def __init__(self):
self.bar = ...
Here, Foo.bar behaves like a regular instance attribute, which it is, just implemented differently. This is simple, efficient, and avoids the #property boilerplate that you described.
This works whether or not ABCs define __slots__ at their class' bodies. However, going with __slots__ all the way not only saves memory and provides faster attribute accesses but also gives a meaningful descriptor instead of having intermediates (e.g. bar = None or similar) in sub-classes.1
A few answers suggest doing the "abstract" attribute check after instantiation (i.e. at the meta-class __call__() method) but I find that not only wasteful but also potentially inefficient as the initialization step could be a time-consuming one.
In short, what's required for sub-classes of ABCs is to override the relevant descriptor (be it a property or a method), it doesn't matter how, and documenting to your users that it's possible to use __slots__ as implementation for abstract properties seems to me as the more adequate approach.
1 In any case, at the very least, ABCs should always define an empty __slots__ class attribute because otherwise sub-classes are forced to have __dict__ (dynamic attribute access) and __weakref__ (weak reference support) when instantiated. See the abc or collections.abc modules for examples of this being the case within the standard library.
The problem isn't what, but when:
from abc import ABCMeta, abstractmethod
class AbstractFoo(metaclass=ABCMeta):
#abstractmethod
def bar():
pass
class Foo(AbstractFoo):
bar = object()
isinstance(Foo(), AbstractFoo)
#>>> True
It doesn't matter that bar isn't a method! The problem is that __subclasshook__, the method of doing the check, is a classmethod, so only cares whether the class, not the instance, has the attribute.
I suggest you just don't force this, as it's a hard problem. The alternative is forcing them to predefine the attribute, but that just leaves around dummy attributes that just silence errors.
I've searched around for this for awhile but didn't see anything I like. As you probably know if you do:
class AbstractFoo(object):
#property
def bar(self):
raise NotImplementedError(
"Subclasses of AbstractFoo must set an instance attribute "
"self._bar in it's __init__ method")
class Foo(AbstractFoo):
def __init__(self):
self.bar = "bar"
f = Foo()
You get an AttributeError: can't set attribute which is annoying.
To get around this you can do:
class AbstractFoo(object):
#property
def bar(self):
try:
return self._bar
except AttributeError:
raise NotImplementedError(
"Subclasses of AbstractFoo must set an instance attribute "
"self._bar in it's __init__ method")
class OkFoo(AbstractFoo):
def __init__(self):
self._bar = 3
class BadFoo(AbstractFoo):
pass
a = OkFoo()
b = BadFoo()
print a.bar
print b.bar # raises a NotImplementedError
This avoids the AttributeError: can't set attribute but if you just leave off the abstract property all together:
class AbstractFoo(object):
pass
class Foo(AbstractFoo):
pass
f = Foo()
f.bar
You get an AttributeError: 'Foo' object has no attribute 'bar' which is arguably almost as good as the NotImplementedError. So really my solution is just trading one error message from another .. and you have to use self._bar rather than self.bar in the init.
Following https://docs.python.org/2/library/abc.html you could do something like this in Python 2.7:
from abc import ABCMeta, abstractproperty
class Test(object):
__metaclass__ = ABCMeta
#abstractproperty
def test(self): yield None
def get_test(self):
return self.test
class TestChild(Test):
test = None
def __init__(self, var):
self.test = var
a = TestChild('test')
print(a.get_test())