How can objects communicate without violating the Dependency Inversion Principle? - python

I am building a Path Planner that will help people plan a path through an RPG console game.
I want to create a table that shows each step through the stage. I have actually implemented a working version of this, however, it is seemingly awful OOP design; it breaks all sorts of principles, and I believe it is not even legitimate OOP. The problem is, clearly, that Table is a God Class.
Due to this, I have decided to rewrite it while trying to keep in mind proper OOP principles. I want to break up Table into multiple classes.
My problem is I need various objects to talk to each other. However, my solution is to always use composition. This breaks the dependency principle as well as the single responsibility principle.
Here is the main Table that will store the player's steps:
class PathTable(object):
''' A path table. '''
def __init__(self):
# table is a list of dicts, representing rows
self._table = []
#property
def table(self):
return self._table
def addStep(self, step):
''' Adds a step to the table. '''
self._table.append(step)
def rmStep(self):
''' Removes the last step from the table. '''
try:
del self._table[-1]
except:
raise IndexError('Tried to remove item from an empty table.')
Now, I have created an InputManager that is responsible for accepting and validating user input:
class InputManager(object):
''' Responsible for managing user input. '''
def __init__(self):
pass
def addFight(self, position):
''' Add a 'fight' at table[position]. '''
# table._table[position]['input'] = 'fight'
# Need to somehow edit a particular row in the Table.
However, now I do not know how I can access PathTable._table[position]. Without breaking all kinds of OO design principles.
It is frustrating, because the entire job of InputManager is to access PathTable. But I cannot use composition to place InputManager inside PathTable, because it is bad design.
What is a clean way to accomplish this?
I am a beginner, and I am trying to learn.

First add support for editing a step's row in your PathTable class:
class PathTable(object):
def __init__(self):
self._table = []
## NB : This defeats the whole point of making `_table` private
##property
#def table(self):
# return self._table
def add_step(self, step):
''' Adds a step to the table. '''
self._table.append(step)
def rm_step(self):
''' Removes the last step from the table. '''
return self._table.pop()
def update_step(self, position, key, value):
self._table[position][key] = value
Then pass a PathTable instance to your InputManager:
class InputManager(object):
''' Responsible for managing user input. '''
def __init__(self, path_table):
self._path_table = path_table
def add_fight(self, position):
''' Add a 'fight' at table[position]. '''
self._path_table.update_step(position, 'input', 'fight')

Related

How to reassign a class method after calling another method?

I am currently working on a Python project with a couple class methods that are each called tens of thousands of times. One of the issues with these methods is that they rely on data being populated via another method first, so I want to be able to raise an error if the functions are called prior to populating the data.
And before anyone asks, I opted to separate the data population stage from the class constructor. This is because the data population (and processing) is intensive and I want to manage it separately from the constructor.
Simple (inefficient) implementation
A simple implementation of this might look like:
class DataNotPopulatedError(Exception):
...
class Unblocker1:
def __init__(self):
self.data = None
self._is_populated = False
def populate_data(self, data):
self.data = data
self._is_populated = True
# It will make sense later why this is its own method
def _do_something(self):
print("Data is:", self.data)
def do_something(self):
if not self._is_populated:
raise DataNotPopulatedError
return self._do_something()
unblocker1 = Unblocker1()
# Raise an error (We haven't populated the data yet)
unblocker1.do_something()
# Don't raise an error (We populated the data first)
unblocker1.populate_data([1,2,3])
unblocker1.do_something()
My goal
Because the hypothetical do_something() method is called tens (or hundreds) of thousands of times, I would think those extra checks to make sure that the data has been populated would start to add up.
While I may be barking up the wrong tree, my first thoughts to improve the efficiency of thefunction were to dynamically re-assign the method after the data is populated. I.e., when first creating the class, the do_something() method would point to another function that only raises a DataNotPopulatedError. The populate_data() method would then both populate the data and also "unblock" do_something() by dynamically reassigning do_something() back to the function as written.
I figure the cleanest way to implement something like this would be using a decorator.
Hypothetical usage
I have no idea how to implement the technique described above, however, I did create a hypothetical usage with the inefficient method from before. Given the goal implementation, there might need to be two decorators--one for the blocked functions, and one to unblock them.
import functools
def blocked2(attr, raises):
def _blocked2(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
# Assumes `args[0]` is `self`
# If `self.key` is False, raise `raises`, otherwise call `func()`
if not getattr(args[0], attr):
raise raises
return func(*args, **kwargs)
return wrapper
return _blocked2
class Unblocker2:
def __init__(self):
self.data = None
self._is_populated = False
def populate_data(self, data):
self.data = data
self._is_populated = True
#blocked2("_is_populated", DataNotPopulatedError)
def do_something(self):
print("Data is:", self.data)
I've been having a hard time explaining what I am attempting to do, so I am open to other suggestions to accomplish a similar goal (and potentially better titles for the post). There is a decent chance I am taking the complete wrong approach here; that's just part of learning. If there is a better way of doing what I am trying to do, I am all ears!
What you are trying to do does not seem especially difficult. I suspect you are overcomplicating the task a bit. Assuming you are willing to respect your own private methods, you can do something like
class Unblocker2:
def __init__(self):
self.data = None
def populate_data(self, data):
self.data = data
self.do_something = self._do_something_populated
def do_something(self):
raise DataNotPopulatedError('Data not populated yet')
def _do_something_populated(self):
print("Data is:", self.data)
Since methods are non-data descriptors, assigning a bound method to the instance attribute do_something will shadow the class attribute. That way, instances that have data populated can avoid making a check with the minimum of redundancy.
That being said, profile your code before going off and optimizing it. You'd be amazed at which parts take the longest.

How to instantiate an object in a new class?

A bit of an odd question, but I'm wondering how to import an object from one class to another. I imagine adding more class methods and attributes as I expand my program, and I still want to be able to use old data. I am thinking something as follows:
class old_obj:
def __init__(self, text):
self.name = text
def set_amount(self, num):
self.amount = num
def introduce_yourself(self):
print("I am {} and I am {} many".format(self.name, self.amount))
oldest = old_obj("myself")
oldest.set_amount(15)
also_old = old_obj("Bach")
class new_obj:
def __init__(self):
#some code
#more code
I want to be able to write something like:
renewed = new_obj(oldest)
also_new = new_obj(also_old)
Here, I want to retain the 15 from oldest.amount, but not complain that also_old.amount is None. In particular, I want to retain any attributes that oldest has, while not requiring that it have all possible attributes. Is there a way for me to copy over instances of a class to a new class?
Edit: edited for clarity
You could copy the object instance dict to the new class.
from copy import deepcopy
class old_obj:
def __init__(self, text):
self.name = text
def set_amount(self, num):
self.amount = num
def introduce_yourself(self):
print("I am {} and I am {} many".format(self.name, self.amount))
oldest = old_obj("myself")
class new_obj:
def __init__(self, my_old_obj):
for var, val in my_old_obj.__dict__.items():
setattr(self, var, deepcopy(val))
#some code
#more code
newest = new_obj(oldest)
I did a deepcopy of the value assuming you want unique values in the new object. But that can also be problematic because not everything can be copied (file objects for instance). There can be other oddities when duplicating attributes such as what you want to do with a generator. And if this is something like a GUI widget, it could get stranger still.
But for a lot of object types, this would work.
Slightly different take:
Your new class has a set of concerns that are probably similar to your old class. This should guide the way you update it and build out the behavior in question. With this in mind...
Provide a class method in your new class to allow construction of the new object from the old object. Don’t make this behavior a part of __init__. Your __init__ should have a more limited responsibility. For the class method, updating the new object’s __dict__ using the old object’s __dict__ would do the job.
Don’t use inheritance to make new versions of classes. Use inheritance to move from general to specific or abstract to concrete. Otherwise, you end up with code that is hard to understand and update. (Imagine several generations down of just sub-classing in order to add some new methods.)
If the number of methods and attributes is growing, you might want to consider whether or not you’re encapsulating data/behaviors that should be split into multiple classes. The guiding principle is that you should encapsulate the data/behaviors that are likely to change together. That is, when you change the way you’re implementing your program, things that don’t need to change should probably be encapsulated separate from things that need changing. If you find that a lot of your static data is bound up with an object class that you’re frequently updating (but wanting to just import the old data unchanged), then you’ve probably got two different sets of concerns, at least.
You can simply initialize the new object by passing it the old one.
class old_obj:
def __init__(self, text):
self.text = text
oldest = old_obj("myself")
class new_obj:
def __init__(self, old_inst):
self.text = old_inst.text
renewed = new_obj(oldest)
print(renewed.text)
First, make your new_obj class inherit from old_obj, so that new_obj has all the methods old_obj had:
class new_obj(olb_obj):
Then, in __init__ method of the new class you can check what is passed as the argument - a string or an object:
def __init__(self, arg):
if isinstance(arg, str):
self.text = arg
elif isinstance(arg, old_obj):
self.text = arg.text
else:
raise TypeError

How would I change my functions into classes?

I'm working on a coursework for my uni (GUI program) and I run into a problem as my code works but the specification is that we use OOP instead of just functions, and I'm lost.
I tried making new classes for each button but I don't know how to make them work like they do in the functions.
def add():
#get input
task=txt_input.get()
if task !="":
tasks.append(task)
#updating the list box
update_listbox()
else:
display["text"]=("Input a task.")
with open("ToDoList.txt", "a") as f:
f.write(task)
f.close()
txt_input=tk.Entry(root, width=25)
txt_input.pack(pady=15)
add=tk.Button(root, text="Add", fg="DarkOrchid3", bg="blanched almond", command=add)
add.pack(pady=5, ipadx=15)
This allows the user to add a task to his to-do list in the GUI, but like I said it should be using OOP not functions.
If I get to understand this one, I should be able to do the rest of the buttons.
UPDATE:
Ok so I tried the solution given below and I don't really know what is wrong with my code, the GUI appears but the adding functions won't work.
class ToDoList():
def __init__(self):
self.tasks = []
def update_listbox(self):
#calling clear function to clear the list to make sure tasks don't keep on adding up
clear()
for task in self.tasks:
box_tasks.insert("end", task)
def clear(self):
box_tasks.insert("end", task)
class adding():
def add(self):
task=txt_input.get()
if task!="":
self.tasks.append(task)
update_listbox()
else:
display["text"]=("Input a task")
It's not clear what your teacher meant about using classes. I'm going to guess that they want you to create a "todo list" object that has methods for adding and removing tasks. I don't know whether they want the GUI to be part of that class or not. It could be that your entire application is made of classes, or you could only use the class for the business logic.
I think you should start by creating a class just for the business logic. It would look something like this:
class ToDoList():
def __init__(self):
self.the_list = []
def add(self, value):
<code to add the value to self.the_list>
def remove(self, item):
<code to remove a value from self.the_list>
With that, you can write a simple little program without a GUI, which makes it easy to test the logic:
# create an instance of the to-do list
todo_list = ToDoList()
# add two items:
todo_list.add("mow the lawn")
todo_list.add("buy groceries")
# delete the first item:
todo_list.remove(0)
To build a GUI on top of that, you could either add the GUI component to the existing class, or create a new class specifically for the GUI. Each has pros and cons.
In the following example, the GUI is a separate class which uses the ToDoList class to maintain the data. This design lets you re-use the underlying todo list logic in tests, in a GUI, and even in a webapp or possibly even a mobile app.
class ToDoGUI():
def __init__(self):
# initalize the list
self.todo_list = ToDoList()
# initialize the GUI
<code to create the entry, button, and widget to show the list>
def add(self):
# this should be called by your button to the list
data = self.entry.get()
self.todo_list.add(data)

Having mulitple classes inside a class python3?

I'd like to do something like this:
Robot.GyroController.getLatestMeasurement()
Is there a way to do this?
More specifically I wanted to do this:
robot = Robot.__init__(arguments)
latesMeasurement = robot.GyroController.getLatestMeasurement()
Is this valid python? And most importantly, is it possible to do so?
I need to do a LEGO competition. I can use whatever programming language that I want to and so I figured I'd write a library to get slightly better abstraction over the existent one (also to practice python as I want to get into tensorflow)
I have a class called robot. This class is initialized with references to all the motors/sensors the robot has.
From there, I want some subclasses (or maybe something else?) that can control motors, sensors, and do some other fancy stuff.
Instead of passing robot (that contains references to motors/sensors) every time I use motors/sensors, I figured that I could do something like this.
PS. I am coming from OOP, and still learning python, so please, it is my intention to improve the question as best as I can. Please give me a chance.
From what i read, you want to have a Robot Class that has multiple Motor's class or something like that, maybe this could work as a hint on how that could be done:
class Motor:
def __init__(self, motor):
self.motor = motor
def go_slow(self):
self.motor.setval = 100
def go_fast(self):
self.motor.setval = 255
class Robot:
def ___init___(self, reference_motor1, reference_motor2):
self.motor1 = Motor(reference_motor1)
self.motor2 = Motor(reference_motor1)
def go_straight_slow():
self.motor1.go_slow()
self.motor2.go_slow()
def go_straight_fast():
self.motor1.go_fast()
self.motor2.go_fast()
here's a dummy example on how your code maybe look like if you wanna do it object oriented.
Edit:
assuming that you already got the class that "MotorController"
class MotorController:
def __init__(self):
pass
def goStraight():
pass
class Robot:
def ___init___(self):
self.motor_controllers = [] #List for storing all motors
def add_motor_reference(self, reference):
self.motor_controllers.append(MotorController(reference))
#Appends new motors to the list
def go_straight(self):
for motor_controller in self.motor_controllers:
motor_controller.goStraight()
#Executes for the "goStraight" function on every motor in the list
Edit:
If you want to add the motors on the constructor of the class you could do something like:
class Robot:
def ___init___(self, *args):
self.motor_controllers = [] #List for storing all motors
for motor in args:
self.motor_controllers.append(MotorController(motor))
#Here every motor reference you pass will be automatically added in the list of motor controllers.

Creating and colorizing new constructs on a existing Scintilla lexer

All,
I'm using QScintilla to syntax-highlight my domain specific language (DSL).
Since my DSL is based on python, I use the existing Python Lexer for QScintilla.
I manage to create new keywords as following:
self.text = Qscintilla(self)
pythonLexer = QsciLexerPython(self.text)
self.text.setLexer(pythonLexer)
self.text.SendScintilla(QsciScintilla.SCI_SETKEYWORDS,1,bytes('WARNING', 'utf-8'))
Now, how do I choose a color to highlight my newly created keywords?
Thanks a lot!
The QsciLexerPython is quite limited when it comes to highlighting sets of keywords, as it only gives you two to play with. This limitation is imposed by the Python Lexer class from the underlying Scintilla library, so there's not much that can be done about it (unless you want to create a patch).
However, if you only need to highlight one extra set of keywords, then you can subclass QsciLexerPython and reimplement its keywords method:
class CustomLexer(QsciLexerPython):
def keywords(self, keyset):
if keyset == QsciLexerPython.HighlightedIdentifier:
return b'WARNING'
return QsciLexerPython.keywords(self, keyset)
With that in place, you can then set the color, font, etc for the style:
pythonLexer = CustomLexer(self.text)
pythonLexer.setColor(
QColor('purple'), QsciLexerPython.HighlightedIdentifier)
...
(PS: note that keywords can only contain single-byte characters in the range 0-255)
To gain even more flexibility, you can consider to build your own custom lexer, not derived from the existing QsciLexerPython one. Watch out - it will be more work.
QScintilla provides the QsciLexerCustom class for this purpose. You have to subclass it like this:
class MyLexer(QsciLexerCustom):
def __init__(self, parent):
super(MyLexer, self).__init__(parent)
[...]
''''''
def language(self):
[...]
''''''
def description(self, style):
[...]
''''''
def styleText(self, start, end):
# Called everytime the editors text has changed
[...]
''''''
'''--- end class ---'''
Please notice the following parts:
__init__(self, parent) : The constructor is typically where you create style-objects.
language(self) : This method must return the name of the language. You have to implement it, but what it actually gets used for is unclear to me.
description(self, style_nr) : Returns the descriptive name for a given style.
styleText(self, start, end) : Gets called everytime editors text has changed. Here you implement syntax highlighting!
For more details, you can visit the following website: https://qscintilla.com/subclass-qscilexercustom/

Categories