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 color
and 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 *args
and **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.)