In Python, class methods and static methods can be defined using decorators such as @ classmethod
and @staticmethod
.
On the other hand, ordinary methods (instance methods) can be defined normally, but in fact they can be called in two ways:
class MyClass:
def mymethod(self,x):
print(x)
x = 1
myclass = MyClass()
myclass.mymethod(x) #Normal usage
MyClass(None,1) # !?!?
The second usage is to use the defined function as it is (the first argument is self
, but it is not used in the function, so None
is fine).
However, even though it is an instance method, if you inadvertently call it like a class method, it will cause an confusing error.
For example, if you take multiple arguments as below:
class MyClass:
def mymethod(self,*x):
print(x)
x = list(range(4))
myclass = MyClass()
myclass.mymethod(*x) # [0,1,2,3]
MyClass.mymethod(*x) # [1,2,3]
In order to avoid this, I want to be angry when I call it something like the second one.
Reference 1: stackoverflow-"Get defining class of unbound method object in Python 3" [Reference 2: Mastering Python-Who is converting from a method to a function? ](Https://python.ms/descriptor/#%E7%96%91%E5%95%8F-get-%E3%83%A1%E3%82%BD%E3%83%83%E3%83 % 89% E3% 81% A3% E3% 81% A6% E3% 81% AA% E3% 81% AB% EF% BC% 9F)
Define a decorator.
import functools
import inspect
# a.x -> type(a).__dict__['x'].__get__(a, type(a))
# A.x -> A.__dict__['x'].__get__(None, A)
#
# --> __get__When the first argument of is None, when it is called from the class.
#So A.__dict__['x']To__get__If you leave the object with
#You can hack it nicely. However,__get__(obe,objtype)Also pay attention to the return value of.
# a.For x, the return value must be a method. In other words, the one with the first argument of the function fixed.
# A.If it's x, don't worry (make an error).
#def pure_instancemethod(func):
# functools.wraps(func)
# def wrapped_func(self,*args,**kwargs):
# if not isinstance(self,)
# raise TypeError("Not a class method")
class PureInstanceMethod:
def __init__(self,func):
self.func = func
def __get__(self,obj,objtype):
if obj is None: #Call from class:
raise TypeError("Don't call from class Octopus")
else: #Call from instance
def instancemethod(*args,**kwargs):
return self.func(obj,*args,**kwargs)
return instancemethod #I'll return the method
def __call__(self,*args,**kwargs):
return self.func(*args,**kwargs)
class MyClass:
@PureInstanceMethod
def mymethod(self,*x):
print(x)
# MyClass.__dict__["mymethod"] = pureinstancemethod(mymethod)Almost the same as.
#In other words, here is an instance of the pure instance method initialized by mymethod.
myclass.mymethod(*[1,2,3])
# > (1, 2, 3)
MyClass.mymethod(*[1,2,3])
# > ---------------------------------------------------------------------------
# > TypeError Traceback (most recent call last)
# > <ipython-input-152-99447b4e3435> in <module>
# > ----> 1 MyClass.mymethod(*[1,2,3]) # [1,2,3]
# >
# > <ipython-input-147-39b9424a9215> in __get__(self, obj, objtype)
# > 23 def __get__(self,obj,objtype):
# > 24 if obj is None: #Call from class:
# > ---> 25 raise TypeError("Don't call from class Octopus")
# > 26
# > 27 else: #Call from instance
# >
# > TypeError:Don't call from class Octopus
I was angry safely.