Best practices for python abstract class - python

I came across this code in a codebase:
class Worker(object):
state_names = {}
def __init__(self, status_value):
self.status_value = status_value
#property
def state(self):
return self.status_value.value
#state.setter
def state(self, status):
self.ui.debug('state: {} -> {}'
''.format(self.state_name(), self.state_name(status)))
self.status_value.value = status
def state_name(self, s=None):
return self.state_names[s or self.state]
There are some obvious problems with it:
self.state_names is read-only and empty
self.ui does not exist
And after looking around for awhile, I realized that there was a single class that derived from this and was known to have self.ui and provided self.state_names.
I think it is kind of odd to implement an abstract class in this way, where the virtual member functions have implementations that assume data members in the derived class.
Is there a better, cleaner way to do this development pattern in python? I am going to assume that AbstractBaseClass is a starting point.

Related

Refactor the python design patterns

I have a class called resources and I have defined one method called get_connect. I want to use the data of which get_connect returns to the other classes. I need at least three classes and I use the data of get_connect and I have to parse that data. To implement this I have written the code below
class resources:
#staticmethod
def get_connect():
return 1 + 2
class Source1(resources):
def __init__(self):
self.response = resources.get_connect()
def get__details1(self):
print(self.response)
class Source2(resources):
def __init__(self):
self.response = resources.get_connect()
def get_details2(self):
print(self.response)
class Source3(resources):
def __init__(self):
self.response = resources.get_connect()
def get__detail3(self):
print(self.response)
source1 = Source1()
source2 = Source2()
source3 = Source3()
source1.get__details1()
source2.get_details2()
source3.get__detail3()
But the problem with the code is for every class in init method I am calling the get_connect method. I don't want to repeat the code. I need help for avoiding redundancy which I have asked below
Is there any way I can call get_connect in one place and use it for other classes maybe a decorator or anything? if yes how can I?
While creating objects also I am calling each class and calling each method every time. is there a way to use any design pattern here?
If anyone helps me with these oops concepts it will be useful.
First of all, is there any reason why you are using get_connect method as static?
Because what you can do here is declare it in the parent class:
class resources:
def __init__(self):
self.response = self.get_connect()
def get_connect(self):
return 1 + 2
This way you do not need to define the __init__ method on every class, as it will be automatically inherited from the parent.
Regarding the second question, it really depends on the context, but you can use a strategy pattern in order to retrieve the class that you require to call. For this rename the method of get details into the same for each of the classes, as basically they're used for the same purpose, but changed on the context of the class implementation:
class Source1(resources):
def get_details(self):
print(self.response)
class Source2(resources):
def get_details(self):
print(self.response)
class Source3(resources):
def get_details(self):
print(self.response)
classes = {
"source_1": Source1,
"source_2": Source2,
"source_3": Source3
}
source_class = classes["source_1"]
source = source_class()
source.get_details()
Hope this helped!

I am getting error in this simple piece of code while using super function

I have tried changing the code a little but it didn't work.
Here's the code anyway.
class Car:
def __init__(self,model,name,year):
self.name=name
self.model=model
self.year=year
self.Class='normal'
self.odometer=0
def descriptive_name(self):
return f'{self.year} {self.name} {self.model}'
def type(self):
return self.Class
def odometer_reading(self):
return self.odometer
class Battery:
def __init__(self):
self.capacity='75 kwh'
def get_capacity(self):
return self.capacity
class Electric_car:
def __init__(self,model,name,year):
super(Car()).__init__(model,name,year)
self.battery=Battery()
self.Class='Electric_car'
def my_battrey(self):
return self.battery.get_capacity()
The code above is in the same directory as the code below.
Name the code above as main.py and in the same directory write the code below in new file and try running it.
import main
my_scorpio=main.Car('s','scorpio',2019)
print(my_scorpio.descriptive_name())
print(my_scorpio.odometer_reading())
print(my_scorpio.type())
my_tesla=main.Electric_car('s','Tesla',2019)
print(my_tesla.descriptive_name())
print(my_tesla.odometer_reading)
print(my_tesla.type())
print(my_tesla.my_battrey())
class Electric_car:
def __init__(self,model,name,year):
super(Car()).__init__(model,name,year)
I would guess from your title that this is the issue at hand.
In python you have to say explicitly that ElectricCar inherits from Car using this syntax
class Electric_car(Car):
def __init__(self,model,name,year):
# this will give you the bound super object
# from the inheritance Car > ElectricCar
super(Car, self).__init__(model,name,year)
Then using super(Car, self) would know where to look for in the inheritance tree.
As pointed out in the comments you could also go for super(ElectricCar, self) as it is true that isinstance(self, ElectricCar).
The error here was not setting up the inheritance tree by specifying that
the ElectricCar is a subclass of a Car and therefore you can't get that
far!
Without the inheritance bit then isinstance(self, Car) would be false as
self is of type ElectricCar which does not inherit from Car!
I really suggest reading the documentation for super though because it explains what is happening when you use it!
The right way (old style) to call super is:
super(Electric_car, self).__init__(model,name,year)
Or simply (in Python 3.x):
super().__init__(model,name,year)

Remove a property (getter/setter) from an attribute in subclass

This may have been answered somewhere else, but I was wondering if there was any way to remove an attribute/method decorated with #property in a subclass.
Example:
from datetime import datetime
class A():
def __init__(self, num):
self._num = num
#property
def id(self):
return self._num * datetime.now().timestamp()
class B(A):
def __init__(self, id, num):
super().__init__(num)
self.id = id
The above code does not run if you attempt to create an instance of class B. AttributeError: can't set attribute
The base class uses a property because it needs to evaluate its ID on the fly, while my sub class is able to know its ID when it is created. The id attribute is accessed OFTEN, and I am seeing a significant performance hit because I have to use a property to serve this attribute, instead of just accessing it directly. (From what I have read, properties increase time-to-access by 5x). My application is currently spending around 10% of runtime getting this property.
Is there any way I can short-circuit the property in a sub class?
I'm going to go through several possibilities here. Some of them do what you literally asked. Some of them don't, but they may be better options anyway.
First, your example base class changes the value of obj.id on every access due to the passage of time. That's really bizarre and doesn't seem like a useful concept of "ID". If your real use case has a stable obj.id return value, then you can cache it to avoid the expense of recomputation:
def __init__(self):
...
self._id = None
#property
def id(self):
if self._id is not None:
return self._id
retval = self._id = expensive_computation()
return retval
This may mitigate the expense of the property. If you need more mitigation, look for places where you access id repeatedly, and instead, access it once and save it in a variable. Local variable lookup outperforms attribute access no matter how the attribute is implemented. (Of course, if you actually do have weird time-variant IDs, then this sort of refactoring may not be valid.)
Second, you can't override a property with a "regular" attribute, but you can create your own version of property that can be overridden this way. Your property blocks attribute setting, and takes priority over "regular" attributes even if you force an entry into the instance __dict__, because property has a __set__ method (even if you don't write a setter). Writing your own descriptor without a __set__ would allow overriding. You could do it with a generic LowPriorityProperty:
class LowPriorityProperty(object):
"""
Like #property, but no __set__ or __delete__, and does not take priority
over the instance __dict__.
"""
def __init__(self, fget):
self.fget = fget
def __get__(self, instance, owner=None):
if instance is None:
return self
return self.fget(instance)
class Foo(object):
...
#LowPriorityProperty
def id(self):
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
Or with a role-specific descriptor class:
class IDDescriptor(object):
def __get__(self, instance, owner=None):
if instance is None:
return self
# Remember, self is the descriptor. instance is the object you're
# trying to compute the id attribute of.
return whatever(instance)
class Foo(object):
id = IDDescriptor()
...
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
The role-specific descriptor performs better than the generic LowPriorityProperty, but both perform worse than property due to implementing more logic in Python instead of C.
Finally, you can't override a property with a "regular" attribute, but you can override it with another descriptor, such as another property, or such as the descriptors created for __slots__. If you're really, really pressed for performance, __slots__ is probably more performant than any descriptor you could implement manually, but the interaction between __slots__ and the property is weird and obscure and you'll probably want to leave a comment explaining what you're doing.
class Foo(object):
#property
def id(self):
...
class Bar(Foo):
__slots__ = ('id',)
def __init__(self):
super(Bar, self).__init__()
self.id = whatever
...
add a class C as common ancestor, without id. inherit A and B from it and implement id there as needed. Python wont care that id doesn’t exist on C.
refactor non-id code/attributes from A to C.
Suitability depends on whether OP controls class hierarchy and instantiation mechanisms.
I also found a workaround to get it working as is:
from datetime import datetime
class A():
def __init__(self, num):
self._num = num
#property
def id(self):
return self._num * datetime.now().timestamp()
class B(A):
#this fixes the problem
id = None
def __init__(self, id, num):
super().__init__(num)
self.id = id
b = B("id", 3)
print(vars(b))
This will output:
{'_num': 3, 'id': 'id'}
The trick is id = None on class B. Basically, Python's attribute/method lookup mechanism will stop at the first class with id as an attribute in the MRO. With id = None on class B, the lookup stops there and it never gets as far as that pesky #property on A.
If I comment it back out, as per the OP:
self.id = id
AttributeError: can't set attribute

Python - Abstract class subclassing

I'm trying to find the best for users of my python library to implement an abstract class I wrote.
Namely, my abstract class define an API to access specific values stored in a database, but I would like to let the user choose how to store it (simple text file, json, sqlite, etc.)
My problem is, how should I retrieve the class the user create and use it in my library ?
This is the solution I came up with, but I don't find it very graceful and wonder if there is a more pythonic way.
In my library:
from abc import ABC, abstractmethod
class Database(ABC):
#abstractmethod
def get(self, index):
pass
#abstractmethod
def insert(self, data):
pass
def get_database():
"""call this anywhere I need a concrete database class"""
return Database.__subclasses__()[-1]
In the user code
class SqliteDatabase(Database):
def get(self, index):
# sqlite SELECT and such
return data
def insert(self, data):
# sqlite INSERT INTO
# return data with index provided
return data
Of course, I will return a better error than IndexError if there is no subclass defined, but you get the idea.
Thank you in advance !
I finally settled for something else, as Blorgbeard suggested
_databases = {}
def register(dbname="default"):
def wrapper(klass):
_databases[dbname] = klass
return klass
return wrapper
def get_db(name="default"):
return _databases[name]
And the user only needs to declare
#register()
class SqliteDatabase:
def __get__(self, index):
# retrieve data
if data is None:
raise KeyError(index)
return data
This way, anybody can declare as many as databases as they want.
If you have improvements over this version, I'll gladly take them.

defining or automatically setting required fields for a type of class

I have a range of derived classes "ChildA", "ChildB", etc... that all inherit from a Base class "Parent".
The purpose of Parent is to implement a common method for executing code in Child classes. Inside this method is a condition based on the child instance's 'status'.
class Parent:
def process(self):
try:
self.run()
except:
assert self.status == 'ok'
class ChildA(Parent):
def run(self):
...
I want save the future developers Child classes from unpleasant surprises.
I feel like the solution would come in the form of an AbstractClass, but I haven't (fully) grasped the concept yet. Or rather, I get the concept, but not how to apply it.
Otherwise I can:
specify the requirement for a 'status' field in doc.
I'd much prefer a strict and organic enforcement mean.
add __init__(self): self.status = None to the Parent, and require (via documentation once again), all Child classes to use
super() to call the Parent's init function.
looks overcomplicated for a Base init method that only initialises one field, but willing to use that if that's the Pythonic way to do it.
check for a .status field at the beginning of Parent.process()
Does the job in one line but too hacky.
What do you recommend?
Running Python 3.5
I think beeing explicit and call super is probably the best way to go.
But you could also consider using a property in the Parent class:
class Parent(object):
def process(self):
try:
self.run()
except Exception, e:
assert self.status == 'ok'
def get_status(self):
try:
return self._status
except AttributeError:
print "oops: child class did not initialize status"
return "ok"
def set_status(self, status):
self._status = status
status = property(get_status, set_status)
class BadChild(Parent):
def run(self):
print "run bad"
self.status
class GoodChild(Parent):
def __init__(self):
self.status = "ok"
def run(self):
print "run good"
self.status
And the test:
bad = BadChild()
good = GoodChild()
good.process()
bad.process()

Categories

Resources