While there are answers specific to the OP's situation that legitimately advise away from calling an instance method from a class,
I have a similar use-case that really needs to call an instance method from the same class.
Because that's impossible, or hacky, I have worked this way of doing it.
I think it's probably the shortest legitimate route, but it would be great to get any improvements or corrections.
Use case demo (non-working)
class MyClass:
context = {"a": 1} # establish class variable
def __init__(self, details):
self.details = details # establish instance variable
def get(self, key):
val = self.details.get(key) # access instance variable
if val is None:
val = self.context.get(key) # access class variable
return val
my = MyClass({"a": 2})
print(my.get("a")) # call instance method from instance
print(MyClass.get("a")) # call instance method from same class
Running this works from the instance but not from the class.
It demonstrates the rough functionality that is being sought.
The "a"
argument is assigned to the self
parameter and Python complains that there are no arguments left for the key
parameter.
2
Traceback (most recent call last):
File "myclass.py", line 17, in <module>
print(MyClass.get("a"))
TypeError: MyClass.get() missing 1 required positional argument: 'key'
Solution concept
A separate class method and instance method of the same name are needed.
But defining two methods with the same name on the same object can only mean that the one defined later over-writes the one defined earlier. A single object certainly won't support two different attributes of the same name.
The solution is to have two separate methods, both with the same name, on separate objects. One is a normal class method and one a normal instance method.
Solution design
To allow this a sub-class can be introduced so that the sub-class redefines the method with the same name. Python OO takes care of calling them appropriately.
class MyClass:
context = {"a": 1}
@classmethod
def get(cls, key):
val = cls.context.get(key)
return val
class MySubClass(MyClass):
def __init__(self, details):
self.details = details
def get(self, key):
val = self.details.get(key)
if val is None:
# val = super().get(key)
val = self.__class__.get(key)
return val
my = MySubClass({"a": 2})
print(my.get("a"))
print(MyClass.get("a"))
This solution works.
2
1
The singleton behaviour of the class is still available for use, but instead of making instances of that class, instances of a sub-class are used.
This means that an additional sub-class is defined, and it means the implementation of the method is spread out, but that seems necessary and appropriate to handle the differences between a class and an instance.
get_params
a classmethod also? – FractionalMyClass
is juts an example,get_params
represents a method that is widely called from other instance methods in my class, and I think there should be some way to call it from a classmethod instead of changing ALL the methods that currently call it. Thanks anyway :) – Oistrakhget_params
do? If it's an instance method, you should need to have an instance to call it. If you don't, it probably shouldn't be an instance method. You can call an instance method from any other scope though, simply by providing an instance of the class as an explicit first argument. – Prismaticget_params
aclassmethod
shouldn't break any code that doesinstance.get_params()
. It will break code that doTheClass.get_params(instance)
only. – Coruscateget_params
doesn't need access to the instance, then just make it a classmethod. That may require making some changes elsewhere, but there's no way around that. You can't call an instancemethod without an instance. Note that you can call a classmethod from an instance method with no problems, so you may not actually have to make as many changes as you think. – Fractional