mercredi 25 juillet 2018

Confusing design pattern when creating a new parameter for the original function while using decorator

I'm using a decorator to shorten the functions that uses selected rows and the current row in PyQt-5. Here's the simplified decorator:

def call_with_selected_record(function):
    @functools.wraps(function)
    def wrapper(self):
        selected_rows = self.getSelectedRows()
        if selected_rows:
            function(self, self.currentRow())

    return wrapper

And the function looks like this:

@call_with_selected_record
def toggle_selected_records(self, row):
    doSomethingWithRow(row)
    return

Here's the confusing part for this design: When I try to use any function that uses this decorator, my IDE shows the row parameter as needed. But row parameter is satisfied by the decorator itself. So, whenever I try to use the toggle_selected_records function, I need to remember how its decorator works. Do I really need to check the wrapper of each decorator? Isn't this a bit confusing? Or is this the intended design for decorators? Nevertheless I came up with these solutions:

1-)Add this line to all functions that uses the same decorator:

row=self.currentRow()

So the function looks like this after the change:

@call_with_selected_record
def toggle_selected_records(self):
    row=self.currentRow()
    doSomethingWithRow(row)
    return

But this solution adds one line to all functions unnecessarily. It's redundant and beats the purpose of having a decorator.

2-)Replace all the row keywords in functions with args[1]. So the function looks like this after the change:

@call_with_selected_record
def toggle_selected_records(self, *args):
    doSomethingWithRow(args[1])
    return

This doesn't add any new lines to the function and eliminates the row parameter. But it creates a new confusion: *args doesn't do anything and it gives an error when a parameter is passed because of the wrapper. This solution also decreases the readability.

So, in short, I don't want to have a row parameter for the original function but want to use the row information that's provided by the decorator without adding something redundant to each function. What's the ultimate solution to this issue?

Aucun commentaire:

Enregistrer un commentaire