I am a hobbyist programmer, working on a much more complex Python project than I've attempted before, which is in the form of a Python library.
I find that I'm often passing
**kwargs around (as described in this question), but I'm also realising it's bad for readability and maintainability. The answer to the previous question suggests that the code may need to be refactored, but I am unsure how to do so while still achieving what I was trying to achieve in the first place.
Here's an example of what I find myself doing. My application is built around
Shape objects that can be displayed on screen. Displaying them is quite a complex task, so I have a
DisplayManager class that looks like this:
# displayManager.py class DisplayManager: def __init__(self): # set up display context, open a window, etc. def display_shape(self, shape, color, opacity=1): # query the Shape object for the data needed # to display it, then process that data and # show it on screen
It makes sense to put the
display method here rather than in the
Shape class, because the code and libraries used for displaying are quite distinct from the rest of the application's logic, so I wanted to keep it separate.
But most of the time the user shouldn't have to worry about the displayManager class. So I end up having a global
displayManager object and putting a convenience
display method in
Shape, like this:
# shape.py import display class Shape: # ... def display(self, color, opacity=1): display.globalDisplayManager.display_shape(self, opacity)
The problem is that now I have the arguments
opacity in two different places in the code, in separate files, which violates DRY. It's quite likely that these arguments or their default values will change in the future, and I would have to remember to update them in both places. So instead, I end up doing this:
# shape.py import display class Shape: # ... def display(self, *args, **kwargs): display.globalDisplayManager.display_shape(self, *args, **kwargs)
Now the arguments are specified in only one place, and if I decide to change them or add new ones I only have to do it in one place, in
display.py where the display routines are implemented.
The problem is that it's not very good from the user's point of view. Looking at
Shape.display doesn't actually tell you its arguments, and to find that out you have to delve into the
DisplayManager class, which I didn't want the user to worry about.
Of course this can be resolved by describing the arguments in the docstring for
Shape.display, but then I'd have an even worse violation of DRY, since the documentation would be in a different place from the implementation and it would be easy to forget to keep in them in sync.
So after that long description, my question is how (or whether) I can achieve my goals without this big disadvantage? That is, how can I keep the display implementation separate from the
Shape implementation, in such a way that (i) I don't have to remember to update multiple files if I change the API, and (ii) the user doesn't have to dig into the implementation to find out how to use it? More broadly, if I find myself passing
**kwargs around like this, what should I be considering instead?
(Note: a possible response to this question is that I shouldn't be changing the API all the time anyway. My reply is that at this stage the only user is me. The API will all be locked down before I release, if I ever do release, but for now it's important to keep it flexible so I can figure out what works.)