I'd like a particular function to be callable as a classmethod, and to behave differently when it's called on an instance.
For example, if I have a class Thing, I want Thing.get_other_thing() to work, but also thing = Thing(); thing.get_other_thing() to behave differently.
I think overwriting the get_other_thing method on initialization should work (see below), but that seems a bit hacky.  Is there a better way?
class Thing:
    def __init__(self):
        self.get_other_thing = self._get_other_thing_inst()
    @classmethod
    def get_other_thing(cls):
        # do something...
    def _get_other_thing_inst(self):
        # do something else
                Great question! What you seek can be easily done using descriptors.
Descriptors are Python objects which implement the descriptor protocol, usually starting with __get__().
They exist, mostly, to be set as a class attribute on different classes. Upon accessing them, their __get__() method is called, with the instance and owner class passed in.
class DifferentFunc:
    """Deploys a different function accroding to attribute access
    I am a descriptor.
    """
    def __init__(self, clsfunc, instfunc):
        # Set our functions
        self.clsfunc = clsfunc
        self.instfunc = instfunc
    def __get__(self, inst, owner):
        # Accessed from class
        if inst is None:
            return self.clsfunc.__get__(None, owner)
        # Accessed from instance
        return self.instfunc.__get__(inst, owner)
class Test:
    @classmethod
    def _get_other_thing(cls):
        print("Accessed through class")
    def _get_other_thing_inst(inst):
        print("Accessed through instance")
    get_other_thing = DifferentFunc(_get_other_thing,
                                    _get_other_thing_inst)
And now for the result:
>>> Test.get_other_thing()
Accessed through class
>>> Test().get_other_thing()
Accessed through instance
That was easy!
By the way, did you notice me using __get__ on the class and instance function? Guess what? Functions are also descriptors, and that's the way they work!
>>> def func(self):
...   pass
...
>>> func.__get__(object(), object)
<bound method func of <object object at 0x000000000046E100>>
Upon accessing a function attribute, it's __get__ is called, and that's how you get function binding.
For more information, I highly suggest reading the Python manual and the "How-To" linked above. Descriptors are one of Python's most powerful features and are barely even known.
Or Why not set self.func = self._func inside __init__?
Setting the function on instantiation comes with quite a few problems:
self.func = self._funccauses a circular reference. The instance is stored inside the function object returned by self._func. This on the other hand is stored upon the instance during the assignment. The end result is that the instance references itself and will clean up in a much slower and heavier manner.__get__(), which is the usual expected method, to bind it. They will receive the wrong function.__slots__.__init__ isn't as clean and requires setting multiple functions on __init__.There are many more that I didn't add as the list goes on and on.
Here is a bit hacky solution:
class Thing(object):
    @staticmethod
    def get_other_thing():
        return 1
    def __getattribute__(self, name):
        if name == 'get_other_thing':
            return lambda: 2
        return super(Thing, self).__getattribute__(name)
print Thing.get_other_thing()  # 1
print Thing().get_other_thing()  # 2
If we are on class, staticmethod is executed. If we are on instance, __getattribute__ is first to be executed, so we can return not Thing.get_other_thing but some other function (lambda in my case)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With