Reputation: 2928
In Python3, instance methods can be called in two ways, obj.ix()
or Foo.ix(obj)
. Setting aside whether it is a good idea or not: When using the latter, is there a way to get the class that the instance method was accessed through?
class Foo(object):
@classmethod
def cx(cls, obj):
print(cls.X)
def ix(self):
# Any way to get the class that ix was accessed through?
print(self.X)
class AFoo(Foo):
X = "A"
class BFoo(Foo):
X = "B"
a = AFoo()
AFoo.cx(a) # Prints "A"
AFoo.ix(a) # Prints "A"
b = BFoo()
BFoo.cx(b) # Prints "B"
BFoo.ix(b) # Prints "B"
AFoo.cx(b) # Prints "A"
AFoo.ix(b) # Prints "B" -> I would like "A", like classmethod.
BFoo.cx(a) # Prints "B"
BFoo.ix(a) # Prints "A" -> I would like "B", like classmethod.
As you can see, the desired behavior is trivial to achieve with a class method, but there does not appear to be a way to do the same with an instance method.
Upvotes: 1
Views: 132
Reputation: 2928
I have already accepted user2357112's answer, but just in case anyone is interested I found another way to do it (based on A class method which behaves differently when called as an instance method?):
import types
class Foo(object):
@classmethod
def x(cls, obj):
print(cls.X)
def __init__(self):
self.x = types.MethodType(type(self).x, self)
class AFoo(Foo):
X = "A"
class BFoo(Foo):
X = "B"
a = AFoo()
b = BFoo()
a.x() # Prints "A"
AFoo.x(a) # Prints "A"
AFoo.x(b) # Prints "A"
b.x() # Prints "B"
BFoo.x(b) # Prints "B"
BFoo.x(a) # Prints "B"
Upvotes: 1
Reputation: 282043
Nope. This information is not preserved. If you want that info, you'd have to write a custom descriptor to implement a new method type. For example:
import functools
class CrazyMethod:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return functools.partial(self.func, owner)
return functools.partial(self.func, instance, instance)
class Foo:
@CrazyMethod
def foo(accessed_through, self):
print(accessed_through)
class Bar(Foo): pass
obj = Bar()
obj.foo() # <__main__.Bar object at 0xb727dd4c>
Bar.foo(obj) # <class '__main__.Bar'>
Foo.foo(obj) # <class '__main__.Foo'>
Upvotes: 2