0

I have read the other questions related to this issue, but none of them seemed to provide a proper general answer and hence I am posting this question.

Assume I have to following code:

class A():
  def do(x:int):
    print(x)  

class B():
  def do(y:str, z:List[int]):
    print(y)
    print(z)

class C():
  def do(x:int, y:str):
    print(x)
    print(y)

def my_factory(name)
  if name=="a":
    return A()
  elif name=="b":
    return B()
  elif name=="c":
    return C()

class my_class():
  def __init__(self, config):
    self.child = my_factory(config)
  def jo(x, y, z, a, b, c):
    self.child.do(???)
    # other stuff

As you can see, my code implements a factory pattern but each of the potential factory objects has unique signature. I wonder which pattern is suited for this case and how it is called. My current idea is something like this:

@dataclass
class factory_object_input:
  x:int
  y:str
  z:List[int]

class A():
  def do(i:factory_object_input):
    print(i.x)
    
#classes B and C accordingly.

class my_class():
  def __init__(self, config):
    self.child = my_factory(config)
  def jo(x, y, z, a, b, c):
    i = factory_object_input(x,y,z)
    self.child.do(i)
    # other stuff

Is that the way to go or do I violate the factory pattern with that?

dinaa123
  • 49
  • 6

1 Answers1

0

Calling the same method with different arguments is called function overloading, and it is not directly supported by Python. Polymorphism in Python is typically achieved through generic programming.

Here you are doing some kind of trans-class function overloading. While this is not forbidden, I have never seen a use-case where this could actually be useful and not achievable by some other means -- more explicitly, and ultimately probably better.

For example, you could use default parameters (where f could be your jo -- no need for classes to make the point):

def f(x=None, y=None, z=None):
    if x is not None and y is None and z is None:
        f_a(x)
    elif x is None and y is not None and z is not None:
        f_b(y, z)
    elif x is not None and y is not None and z is None:
        f_c(x, y)

Or you could use the star-magic to accept/provide arguments to your methods, e.g.:

def f(name, **kws):
    if name == "a":
        f_a(kws["x"])
    elif name == "b":
        f_b(kws["y"], kws["z"])
    elif name == "c":
        f_c(kws["x"], kws["y"])


def g(x, y, z, name):
    kws = dict(x=x, y=y, z=z)
    f(name, **kws)

Or more, you could be benefiting from an even more abstract approach, like using meta-classes.

It is difficult to provide you with better hints without more detailed knowledge of what you are trying to achieve.

norok2
  • 25,683
  • 4
  • 73
  • 99
  • I am not looking for something that works. My question already contains some code that works. I am looking for a pattern with a clean code-like solution (readable, maintainable). Fixed in my use case is the factory pattern and that the factory objects only require some arguments that are passed to `jo()`. – dinaa123 Feb 22 '23 at 17:43
  • Your suggestions won't result into clean code when you imagine 5-10 different factory versions. – dinaa123 Feb 22 '23 at 17:44
  • @dinaa123 It all depend on the assumptions you can make on your actual classes / method. For example, if `a` had a completely different parameter, the dataclass approach would fail. The approaches I provided are more common as they are more general yet fairly simple. What I am trying to say is that you should provide some context as to why you want to write a code like that, and perhaps you can get better suggestion. Asking "is this good or bad for me?", without specifying what "for me" is, is not likely to bring you any answer aligned with what you have in mind but failed to communicate. – norok2 Feb 22 '23 at 19:09
  • Not sure what kind of information you are looking for, but I have a class `my_class` that has several members. One of that members namely `child` can be initialized with a defined set of known classes (which I usually handle with a factory pattern). The problem I face, is that these classes do not share the same signature for the method I will call. So the question I have, is if the factory pattern is wrong in that case and what pattern I should use to have some generic code. I am aware of the solution to hard code everything. – dinaa123 Feb 23 '23 at 04:08
  • This part is clear. What is not clear is why you want to do that. This approach is adding an extra layer of indirection/complexity and it is not clear what the benefit is. You explicitly force the signature of the wrapper method to accept values it will not use. This is already an antipattern, and it needs to be justified. The factory is not a problem per se, and avoiding it while hard coding everything is not likely to be the clean solution. You should probably rethink how the wrapper is used and possibly have it closer to what I originally suggested. – norok2 Feb 23 '23 at 06:22
  • No, that is not the case. All variables that are passed to the wrapper `my_class.jo()` are used inside. – dinaa123 Feb 23 '23 at 09:00