I would like construct a class composition that includes a function set_props for setting the instance variables of components.
The application for this is in defining new objects for drawing in matplotlib. One example is that I would like to have a function drawMyArrow that draws an arrow with possibly different colors (and other specifications) for its head, tail, and arc. I would like to be able to pass various specifications for the head, tail, and arc via keyword arguments in drawMyArrow. I haven't worked with classes before, but reading up on this online, I believe that the best way to solve my problem is to define a class MyArrow that is a composition of some classes ArrowHead and ArrowArc.
To illustrate my problem, I am using a toy example (that I also used for a previous question here). Let's define a class Room that is a composition of the classes wall, window, and door.
class Door:
def __init__(self, color='white', height=2.3, width=1.0, locked=True):
self.color = color
self.height = height
self.width = width
self.locked=locked
class Window:
def __init__(self, color='white', height=1.0, width=0.8):
self.color = color
self.height = height
self.width = width
class Wall:
def __init__(self, color='white', height=2.5, width=4.0):
self.color = color
self.height = height
self.width = width
class Room:
def __init__(self):
self.door = Door()
self.window = Window()
self.wall = Wall()
If I want to have a function for Room that can set properties of its components, I can do something like this:
def set_windowprops(r, color=None, width=None, height=None):
if not color==None: r.window.color=color
if not width==None: r.window.widht=width
if not height==None: r.window.height=height
return r
But if I decide to add more instance variables to Window, I would have to go back to this function and add the new instance variables. Can I write set_windowprops so that it automatically accepts all instance variables of Window as keywords?
Ideally, I would like to write a function like this:
def set_props(r, windowcolor=None, windowwidth=None, windowheight=None,
doorcolor=None, doorwidth=None, doorheight=None, doorlocked=None,
wallcolor=None, wallwidth=None, wallheight=None):
if not windowcolor==None: r.window.color=windowcolor
if not windowwidth==None: r.window.widht=windowwidth
if not windowheight==None: r.window.height=windowheight
if not doorcolor==None: r.door.color=doorcolor
if not doorwidth==None: r.door.widht=doorwidth
if not doorheight==None: r.door.height=dooorheight
if not doorlocked==None: r.door.locked=doorlocked
if not wallcolor==None: r.wall.color=wallcolor
if not wallwidth==None: r.wall.widht=wallwidth
if not wallheight==None: r.wall.height=wallheight
return r
but without the need of hardcoding all instance variables of components into the function.
I was looking into using keyword dictionaries like so:
window_vars = getNamesOfInstanceVariables(Window) #TODO
window_kwargs = {}
for v in window_vars:
window_kwargs[v] = None
def set_windowprops(r, **kwargs):
for kw in kwargs:
if not kwargs[kw]==None:
r["window"][kw] = kwargs[kw] #TODO
return r
Two issues keep me from getting this to work:
(1) In the last line, I am pretending that r is a dictionary (of dictionaries) and using dictionary syntax to assign a value to r.window.kw. But that doesn't work because r is an instance of Room and not a dictionary. What would be the syntax for setting instance variables if the name of the component class and the name of the instance variable are given as strings?
(2) I have tried using inspect to write getNamesOfInstanceVariables, but I am unable to get it to work robustly. Many classes in matplotlib inherit from base classes. I would like getNamesOfInstanceVariables to return all instance variables that a user can set for an object of this class. For example, the class FancyArrow in matplotlib has Patch as base class and instance variables head_length and head_width. So I would getNamesOfInstanceVariables(FancyArrow) to return ['head_length','head_width', *listOfInstanceVarsForPatch].
EDIT
Let me add a bit of background on why I am asking for a dynamical way to write these functions. Let's say I have finished my script and it includes the classes Window, Door, Wall and many class compositions of these three. One of these many class compositions is Room. This class Room has ten hardcoded set_ functions that look like this:
def set_windowcolor(r, color):
r.window.color = color
return r
I now decide that I want to add another instance variable to the class Window. For example,
class Window:
def __init__(self, color='white', height=1.0, width=0.8, open=False):
self.color = color
self.height = height
self.width = width
self.open = open # new attribute of Window
Similar to all the other instance variables of window, this new attribute of Window should be customizable in all classe compositions that contain a Window. So I would go through my code, find the class Room and add a function
def set_windowopen(r, open):
r.window.open = open
return r
I would also have to look for all other class compositions that contain a Window and update them manually as well. I don't want to do this because it is a lot of work and I am likely going to overlook some class dependencies in the process and introduce bugs into my code. I am looking for a solution that either
generates
setfunctions for single properties (e.g.set_windowcolor) automatically inRoomfor all instance variables ofWindoworautomatically adjusts the list of keyword arguments in
set_windowpropsorset_props.