2

I have two sorting functions that accept same (many) arguments:

def sort_A(a, b, c, d, e):
    print('sorting A', a, c, b, d, e)
    hook()
    return 'sort A result'

def sort_B(a, b, c, d, e):
    print('sorting B', e, b, a, c, d)
    return 'sort B result'

def hook():
    print('HOOK')

a = 'a'
if a == 'a':
    sorter = sort_A
else:
    sorter = sort_B
sorter(a, 'b', 'c', 'd', 'e')

I don't like the repetition in those sorting functions signatures, so I came up with two solutions:

  1. Make a Sorter class that would accept those many arguments in its constructor and save them for usage in methods sort_A and sort_B. But the code will be quite bloted with all the needed self references, and I think it is not worth it for my simple use case.
  2. Create some kind of factory function like the one below that accepts the many arguments and by doing so provides context for sorter functions defined in it.

But is solution 2 a good practice? Specifically, is it OK for a factory function like this to have some other function definitions in its scope? It seems like breaking single responsibility principle - the factory function not only returns the object, but also defines it. It is also probably not best performance-wise, since the definitions are interpreted at each call of the factory function.

def get_sorter(a, b, c, d, e):
    def sort_A():
        print('sorting A', a, c, b, d, e)
        hook()
        return 'sort A result'

    def sort_B():
        print('sorting B', e, b, a, c, d)
        return 'sort B result'

    def hook():
        print('HOOK')

    if a == 'a':
        return sort_A
    return sort_B

sorter = get_sorter('a', 'b', 'c', 'd', 'e')
print(sorter())
barciewicz
  • 3,511
  • 6
  • 32
  • 72
  • I think the best way to handle a situation like this is with a class that has a factory method. See [my answer](https://stackoverflow.com/a/28076300/355230) to a related question for an example of what I think is a elegant way to implement one. – martineau Feb 26 '22 at 15:57
  • @KellyBundy Yeah it is an approximation, but I think it get the problem I face across. – barciewicz Feb 26 '22 at 16:08

1 Answers1

0

You should better use classes:

import abc
from dataclasses import dataclass


@dataclass
class Sorter:
    a: str
    b: str
    c: str
    d: str
    e: str

    @abc.abstractmethod
    def sort(self):
        """sort realization"""


@dataclass     
class SorterA(Sorter):
    @staticmethod
    def hook():
         print('HOOK')

    def sort(self):
        print('sorting A', self.a, self.c, self.b, self.d, self.e)
        self.hook()
        return 'sort A result'

 

@dataclass
class SorterB(Sorter):
    def sort(self):
        print('sorting B', self.e, self.b, self.a, self.c, self.d)
        return 'sort B result'

a = 'a'
sorter_class = SorterA if a == 'a' else SorterB
sorter = sorter_class(a, 'b', 'c', 'd', 'e')
print(sorter.sort()) 
  • Thank you! However, I am on Python 3.6. Do you know of any good alternative to dataclass? – barciewicz Feb 27 '22 at 16:51
  • 1
    Yep. Just remove @dataclass decorator and add __init__ method def __init__(self, a: str, b: str, c: str, d: str, e: str): self.a = a self.b = b self.c = c self.d = d – Aleksey Tartynskiy Feb 28 '22 at 08:15
  • @AlekseyTartynskiy there's a backport of dataclasses available for python 3.6. It's an external library, but essentially it's the same code thats available for 3.7. It can be found [on pypi](https://pypi.org/project/dataclasses/). – rv.kvetch Feb 28 '22 at 14:12