2

This could be a so called XY problem, so let me start with what I want:

ed = Edit.add(1).add(2).add(3)
print(ed.op)    # [1,2,3]

Following is what I tried and basically I got working code, but I'm not sure if I did it right or if there are other options.

The first add(1) is a class method call and the following add(2) and add(3) are instance method calls. I started here:

class Edit:
    def __init__(self):
        self.op = []

    @classmethod
    def add(cls, x): 
        self = cls()
        return self.add_im(x)

    def add_im(self, x): # im = instance method
        self.op.append(x)
        return self

ed = Edit.add(1).add_im(2).add_im(3)
print(ed.op)

but I wanted the same name everywhere, so I added some attribute name rewriting:

class Edit:
    def __init__(self):
        self.op = []

    def __getattribute__(self, name):
        if name == "add":
            name = "add_im"
        return super().__getattribute__(name)

    @classmethod
    def add(cls, x): 
        self = cls()
        return self.add(x)

    # im = instance method
    def add_im(self, x): 
        self.op.append(x)
        return self

ed = Edit.add(1).add(2).add(3)
print(ed.op)

UPDATE:

as @lllrnr101 noted in the comments, one part of my question has been answered here: Same name for classmethod and instancemethod

My question is broader, I think it does not have to be closed as a duplicate.

UPDATE2:

After studying the mentioned thread I have now new version I am satisfied with:

class dualmethod(classmethod):
    def __get__(self, instance, typ):
        if instance is None:
            instance = typ()  # create an instance "on the fly"
        return self.__func__.__get__(instance, typ)

class Edit:
    def __init__(self):
        self.op = []

    @dualmethod
    def add(self, x): 
        self.op.append(x)
        return self

ed = Edit.add(1).add(2).add(3)
print(ed.op)    # [1,2,3]
VPfB
  • 14,927
  • 6
  • 41
  • 75
  • Do you want to forward class method to instance method? – tstanisl Apr 10 '21 at 06:52
  • use descriptor? – Aaron Apr 10 '21 at 06:54
  • Why not Edit.get_object().add().add().add()? – lllrnr101 Apr 10 '21 at 06:56
  • @tstanisl I'm sorry, not sure what you mean – VPfB Apr 10 '21 at 06:57
  • @lllrnr101 Such chaining would be backward compatible with `Edit.add()` which is already in the API. – VPfB Apr 10 '21 at 06:59
  • As much as I dislike the idea of you redefining a function again and user not having any idea of that unless he sees the class, maybe this asnwer can help you. https://stackoverflow.com/questions/28237955/same-name-for-classmethod-and-instancemethod The accepted answer is doing what you want be defining a decorator. Maybe it would be helpful to you also. – lllrnr101 Apr 10 '21 at 07:06
  • @lllrnr101 That's helpful indeed. Thank you. – VPfB Apr 10 '21 at 07:35
  • I don't understand what *problem you hope to solve* this way. Is it in fact intended that there are multiple instances of the class, each tracking their own `op` list? If so, *other than* making it so that you don't have to write the first pair of `()` in `ed = Edit().add(1).add(2).add(3)`, what is the actual goal here? – Karl Knechtel Apr 10 '21 at 07:44
  • @KarlKnechtel The goal is to provide a concise way to create a function doing one or more operations, e.g.: `ed = Edit.add(1).mul(10)` , then `ed(3)` returns (3+1)*10 = 40. – VPfB Apr 10 '21 at 07:55
  • 2
    Okay, so I still have the question of why not just write `ed = Edit().add(1).mul(10)`. Alternately, if you really want to save typing and/or make it look cleverer or more elegant, you could make top-level factory functions for the first operation: e.g. `def add(x): return Edit().add(x)`. – Karl Knechtel Apr 10 '21 at 07:58
  • @KarlKnechtel I mentioned it already, a single operation is already in the API, e.g. `Edit.add(1)`. I want to extend that to multiple operations. – VPfB Apr 10 '21 at 08:06

0 Answers0