2

This question mentions a hack to differentiate between an instance method (passing self) and a staticmethod (passing nothing):

class X:
   def id(self=None):
      if self is None:
          # It's being called as a static method
      else:
          # It's being called as an instance method

(credits to Tom Swirly)

However, this quickly runs into an issue when inheritance comes into play, as the static method has no self or cls and therefore cannot call the appropriate method on the message receiver.

My question is, can I do anything like this?

class X:
   def get(self_or_cls):
      if self_or_cls turns out to be cls:
          return self_or_cls.class_method()
      else:
          return self_or_cls.instance_method()


class Y(X):
   foo = str       

   @classmethod
   def class_method(cls):
       return cls.foo

   def instance_method(self):
       return self.foo()



>>> Y.get()
<class 'str'>
>>> Y().get()
''

Any hacks appreciated!

karlosss
  • 2,816
  • 7
  • 26
  • 42
  • 2
    In Python virtually anything is possible but I'm wondering why? Why would you change the method signature from class method to instance method or static method (or vice-versa)? You may have a good use case but for me it smells like a blatant violation of the [POLA](https://en.wikipedia.org/wiki/Principle_of_least_astonishment). – Paulo Scardine Apr 02 '19 at 22:29
  • Is it worse to break DRY or to break POLA? In my opinion, once the code is written and tested, nobody will ever care how ugly it is. On the other hand, breaking DRY will remind us of its existence every time we add a new subclass. – karlosss Apr 02 '19 at 22:46
  • Well, the answer for flexible interfaces in Python usually is [duck-typing](https://en.wikipedia.org/wiki/Duck_typing) - first try to return `self_or_cls.foo()` and if you get a TypeError then return `self_of_cls.foo` but I'm not sure I understood your use case (or just use the `@property` decorator and make everything an attribute since you are not passing any arguments to the method). – Paulo Scardine Apr 02 '19 at 22:54
  • The thing is that it does not let me to call `Y.get()` at all since it is missing the `self` parameter. `cls` is not passed because the `get()` method is missing `@classmethod` decorator. And if I add it, I can no longer call `Y().get()`. – karlosss Apr 02 '19 at 22:57

1 Answers1

1

With help of this answer I found one possible hack for you:

class Custommethod:
    def __get__(self, ins, cls):
        if ins is None:
            return lambda : cls.class_method()
        else:
            return lambda : ins.instance_method()

class X:
   get = Custommethod()


class Y(X):
   foo = str       

   @classmethod
   def class_method(cls):
       return cls.foo

   def instance_method(self):
       return self.foo()


print(Y.get())  # <class 'str'>
print(Y().get())  # ''
sanyassh
  • 8,100
  • 13
  • 36
  • 70