Derived class with differing interface - python

The following code:
class Cache:
def __init__(self):
self._cache = []
def store(self, data):
self._cache.append(data)
def stats(self):
print('We are caching {} elements'.format(len(self._cache)))
class LegoCache(Cache):
def store(self, weight, color):
Cache.store(self, (weight, color))
has a problem in that the store method does not implement the interface of the base class.
How can this code be improved? I have the following ideas:
do not derive from Cache, just make use of it.
rename the store method in the base class to store_base
Are there other alternatives?
EDIT
The base class must also support this other use case:
class ZombieCache(Cache):
def store(self, dead_since, humans_eaten, can_be_saved=False):
Cache.store(self, dict(
dead_since=dead_since,
humans_eaten=humans_eaten,
can_be_saved=can_be_saved))

You may use a variable argument list in the base class:
class Cache:
def __init__(self):
self._cache = []
def store(self, *args):
self._cache.append(args)
def stats(self):
print('We are caching {} elements'.format(len(self._cache)))
class LegoCache(Cache):
pass
# "overloading" store isn't needed
So it isn't needed to overload this method or add methods with different names for specials cases:
cache = Cache()
legoCache = LegoCache()
cache.store(x)
legoCache.store(x, y)
Another solution may be delegation:
class LegoCache(object):
def __init__(self):
self.cache = Cache()
def store(self, weight, color):
self.cache.store((weight, color))
# or just self.cache.store(weight, color) if you use the *args implementation

I would implement it like this,
class Cache:
def __init__(self):
self._cache = []
def store(self, data):
self._cache.append(data)
def stats(self):
print('We are caching {} elements'.format(len(self._cache)))
class LegoData(object):
def __init__(self, weight, color):
self.weight = weight
self.color = color
class LegoCache(Cache):
pass
Client will access it like this,
lego_cache = LegoCache()
lego_cache.store(LegoData(weight=10, color='Green'))

Related

Inheritance of OptaPy planning_entitiy classes?

I'm working on a schedule optimization solution with Optapy.
I'm trying like to make a chained planned variable, which should include multiple types of actual entities in the chain. Something like this:
#planning_entity
class Slot:
""" Schedule slot """
def __init__(self):
self.prev_slot = None
#planning_variable(Slot, value_range_provider_refs=['slot_range'],
graph_type=PlanningVariableGraphType.CHAINED)
def get_previous_slot(self):
return self.prev_slot
def set_previous_slot(self, previous_slot: 'Slot'):
self.prev_slot = previous_slot
#planning_entity
class Task(Slot):
""" Allocation to task """
...
#planning_entity
class LunchBreak(Slot):
""" Lunch break """
...
This doesn't work by various reasons. If I don't add any planning variable to derived classes, it fails saying I have to, but if I add them - I'm getting java exceptions like 'assignment error' or similar.
Is it actually possible to inherit one #planning_entity class from another in Optapy?
The issue is in this code:
#planning_entity
class Slot:
""" Schedule slot """
def __init__(self):
self.prev_slot = None
#planning_variable(Slot, value_range_provider_refs=['slot_range'],
graph_type=PlanningVariableGraphType.CHAINED)
def get_previous_slot(self):
return self.prev_slot
def set_previous_slot(self, previous_slot: 'Slot'):
self.prev_slot = previous_slot
Due to Python semantics, the class Slot is not available during it own definition. However, chained models / subclasses are possible in optapy. There is a bug in the current version of optapy (https://github.com/optapy/optapy/issues/101) that throws a ClassCastException if the #value_range_provider type does not EXACTLY match the #planning_variable type. This can be work around by using the most derived super class (which in this case, is Slot) in the #planning_solution. Additionally, there seem to be a problem with multiple #planning_entity classes in chained models that I'll investigate. To accomplish having multiple subclasses of planning entities (without new #planning_variables), the following code will work:
import optapy
from optapy.score import HardSoftScore
from optapy.types import PlanningVariableGraphType
#optapy.problem_fact
class Base:
pass
#optapy.planning_entity
class Slot(Base):
def __init__(self, value=None):
self.value = value
#optapy.planning_variable(Base, value_range_provider_refs=['employee_range', 'task_range', 'lunch_break_range'],
graph_type=PlanningVariableGraphType.CHAINED)
def get_value(self):
return self.value
def set_value(self, value):
self.value = value
#optapy.problem_fact
class Employee(Base): # chained models need an anchor
def __init__(self, code):
self.code = code
#optapy.planning_entity
class Task(Slot):
def __init__(self, code, value=None):
self.code = code
self.value = value
#optapy.planning_entity
class LunchBreak(Slot):
def __init__(self, code, value=None):
self.code = code
self.value = value
#optapy.planning_solution
class Solution:
def __init__(self, employees, tasks, lunch_breaks, score=None):
self.employees = employees
self.tasks = tasks
self.lunch_breaks = lunch_breaks
self.score = score
#optapy.problem_fact_collection_property(Slot)
#optapy.value_range_provider('employee_range')
def get_employees(self):
return self.employees
#optapy.planning_entity_collection_property(Slot)
#optapy.value_range_provider('task_range')
def get_tasks(self):
return self.tasks
#optapy.planning_entity_collection_property(Slot)
#optapy.value_range_provider('lunch_break_range')
def get_lunch_breaks(self):
return self.lunch_breaks
#optapy.planning_score(HardSoftScore)
def get_score(self):
return self.score
def set_score(self, score):
self.score = score
def build_problem():
employees = [
Employee('Amy'),
Employee('Beth')
]
tasks = [
Task('T1'),
Task('T2'),
Task('T3')
]
lunch_breaks = [
LunchBreak('L1'),
LunchBreak('L2')
]
return Solution(employees, tasks, lunch_breaks)
And to create the solver:
import optapy
import optapy.config
from optapy.types import Duration
from domain import Solution, Slot, build_problem
from constraints import define_constraints
solver_config = (optapy.config.solver.SolverConfig()
.withSolutionClass(Solution)
.withEntityClasses(Slot)
.withConstraintProviderClass(define_constraints)
.withTerminationSpentLimit(Duration.ofSeconds(10))
)
solver_factory = optapy.solver_factory_create(solver_config)
solver = solver_factory.buildSolver()
solver.solve(build_problem())

Observer pattern updating after unsubscribing

I am currently trying to implement the observer design pattern on a List class. The list class is the subject, and a calculate class is the observer. The calculate class needs to observe the list class, and calculate the sum of the values in the list in the list class. However, when the observer unsubscribes from the List, it is still getting the updates. And if the observer subscribes to a new list, the list before unsubscribing is still showing. Does anyone have any tips to why this happens?
from abc import ABCMeta, abstractmethod
class Observer(metaclass = ABCMeta):
#abstractmethod
def update(self, data):
pass
class Subject(metaclass = ABCMeta):
#abstractmethod
def subscribe(self, observer):
pass
#abstractmethod
def unsubscribe(self, observer):
pass
#abstractmethod
def notify(self):
pass
class List(Subject):
def __init__(self):
self.__list = []
self._observers = set()
def add(self, data):
self.__list.append(data)
self.notify()
def subscribe(self, observer):
self._observers.add(observer)
def unsubscribe(self, observer):
self._observers.discard(observer)
def notify(self):
for obs in self._observers:
obs.update(self.__list)
class Calculate(Observer):
def __init__(self, lst):
self.__data = []
self._list = lst
self._list.subscribe(self)
def update(self, data):
self.__data = data
def calculate(self):
total = 0
for item in self.__data:
total += item
return total
def remove(self):
self._list.unsubscribe(self)
def attach(self, lst):
self._list.subscribe(self)
So, this is my test and the output i get:
first_list = List()
first_list.add(1)
list_observer = Calculate(first_list)
first_list.add(5)
list_observer.calculate()
This returns 6, which is correct. However, when i do this:
list_observer.remove()
first_list.add(5)
list_observer.calculate()
I get 11. Which in sense, is the correct calculation, but since the observer unsubscribed, why is it still getting the updates? I also tried subscribing to a new list, but the old data is still in there.
This has to do with the type of data you pass. The list of data is mutable. I can't explain it that well, but this answer is what you are looking for.
How do I pass a variable by reference?
You can fix it by doing this, you take a copy of the original object:
def update(self, data):
self.__data = data.copy()

Python: Is it possible to merge two __init__ after multiple inheritance?

I want my class Parents(Mom, Dad) to inheritance init attributes from two previous classes so then my input will require me to write mom_name and dad_name. Can someone suggest me how to do this?
class Mom(object):
def __init__(self, mom_name):
self.mom_name = mom_name
class Dad(object):
def __init__(self, dad_name):
self.dad_name = dad_name
class Parents(Mom, Dad):
pass
par = Parents('Mom', 'Dad')
print(par.mom_name)
print(par.dad_name)
you can implement it this way
class Mom(object):
def __init__(self, mom_name):
self.mom_name = mom_name
class Dad(object):
def __init__(self, dad_name):
self.dad_name = dad_name
class Parents(Mom, Dad):
def __init__(self, mom_name, dad_name):
Mom.__init__(self, mom_name)
Dad.__init__(self,dad_name)
par = Parents('Mom', 'Dad')
print(par.mom_name)
print(par.dad_name)
output
Mom
Dad
Like this.
class Parents(Mom, Dad):
def __init__(self, mom_name, dad_name):
super(Mom, self).__init__(mom_name)
super(Dad, self).__init__(dad_name)
Edit 1 :
The code above doesn't work, a suitable way will be to subclass Dad from Mum and then paste it to parents like this.
class Mom(object):
def __init__(self, mom_name):
self.mom_name = mom_name
class Dad(Mom):
def __init__(self, dad_name, **kw):
self.dad_name = dad_name
super(Dad, self).__init__(**kw)
class Parents(Dad):
def __init__(self, mom_name, dad_name):
super(Parents, self).__init__(mom_name=mom_name, dad_name=dad_name)
p = Parents("mumy", "dady")
print(p.mom_name)
print(p.dad_name)

Combining dict in super class's init and subclass's init automatically?

I'm creating an event system which uses the following class for events:
class Event(set):
def __init__(self, name, iterable=()):
super().__init__(iterable)
self.name = name
def __iadd__(self, listener):
self.add(listener)
return self
def __isub__(self, listener):
self.remove(listener)
return self
def fire(self, **eargs):
for listener in self:
listener(**eargs)
Now I'm trying to create some kind of a dict that would automatically create the events in its __init__ like so:
class EventDict(dict):
def __init__(self, prefix, *event_names):
super().__init__({
name: Event('%s.%s' % (prefix, name))
for name in event_names
})
And here's an example of usage:
class Player:
def __init__(self, name):
self._name = name
self.events = EventDict('Player', 'change_name')
#property
def name(self):
returns self._name
#name.setter
def name(self, value):
old_name = self.name
self.name = value
self.events['change_name'].fire(player=self, old_name=old_name)
Now the problem I'm facing is subclassing.
If I were to subclass my Player class to include also health attribute, I can't use the same way of creating an event dict, cause it would override the existing one and I couldn't access change_name anymore.
So I'm trying to find a way where I can just do something like this (ideal solution):
class Player:
events = EventDict('Player', 'change_name')
class Player2(Player):
events = EventDict('Player2', 'attack', 'kill')
p2 = Player2()
p2.events['change_name'] += my_event_listener # Still access Player class's events
Would something like this be possible?
I know I can do:
class Player2(Player):
def __init__(self, name):
super().__init__()
self.events.update(...)
But it's not the same :P
I think what you want is:
class Player:
EVENTS = ('change_name',)
def __init__(self, name):
self._name = name
self.events = EventDict(
self.__class__.__name__,
*self.EVENTS,
)
...
Then all you need in Player2 is:
class Player2(Player):
EVENTS = Player.EVENTS + ('attack', 'kill')
and the inherited __init__ will work fine.
Stop using EventDict.
The class itself has its own dict which supports inheritance like that.
class Player:
def __init__(self, name):
self._name = name
self.change_name_event = Event('Player.change_name')
class Player2(Player):
def __init__(self, name):
super().__init__(name)
self.attack_event = Event('Player2.attack')
self.kill_event = Event('Player2.kill')
All the events from the subclasses will be added no matter what.
I noticed that maybe you wanted to make it obvious that they're events, so I added 'event' to the names of the fields, but you don't need to if you don't want to.
If you wanted it so that the prefix is the same throughout, then you'd change the strings from something like 'Player.change_name' to self.__class__.__name__ + '.change_name'. That way, it always gets whatever the actual class for the object is. This is part of what #jonrsharpe's solution is trying to get at.
If you wanted to make it so others could add more events dynamically, they can simply do a line like playerObj.my_new_event = Event('Player.my_new_event') or you could provide a nice method in the Player class to make their lives easier:
def add_event(self, event_name):
setattr(self, event_name, Event(self.__class__.__name__ + '.' + event_name)

Is it ok to have a property decorated method to return a class in Python?

Let's say I have the following code:
class MyClass(object):
def __init__(self, param):
self.param = param
#property
def super_param(self):
return Param(self, self.param)
class Param(object):
def __init__(self, parent, param):
self.param = param
self.parent = parent
#property
def get_parent(self):
return self.parent
My question is, is it considered bad practice to use the #property decorator in this way? Are there any pros or cons?
Why not just
class MyClass(object):
def __init__(self, param):
self.param = Param(self, param)
I don't think there's anything particularly wrong with returning a class from a property; a better question is, what are you trying to accomplish by doing so?
Edit: if you don't want it changed, make it a private property:
class MyClass(object):
def __init__(self, param):
self._param = param # naming convention, 'don't muck with it'
# OR
self.__param = param # name mangled
#property
def param(self):
return self._param

Categories

Resources