I understand this is the correct way to include a function in a class, and works as desired:
from statsmodels.tsa.seasonal import seasonal_decompose class Foo3: def my_seasonal_decompose(self, *args, **kwargs): # from statsmodels.tsa.seasonal import seasonal_decompose return seasonal_decompose(*args, **kwargs) a3 = Foo3() print(a3.my_seasonal_decompose([1, 2], period=1))
However, this works for
sin, and not for
class Foo4: from numpy import sin from statsmodels.tsa.seasonal import seasonal_decompose a4 = Foo4() print(a4.sin(2)) # works print(type(a4.seasonal_decompose)) # returns <class 'method'> help(a4.seasonal_decompose) # works as expected print(a4.seasonal_decompose([1, 2], period=1)) # TypeError: float() argument must be a string or a number, not 'Foo4'
seasonal_decompose is a
function instance and
numpy.sin is a
ufunc instance. Both are callable and are used pretty much as functions, but only
seasonal_decompose is treated as a bona fide Python function. And Python functions are treated special.
<class 'method'> you’re printing out should not be confused with class methods but that its class is
method, more specifically a bound method. Bound methods are how Python wraps
function instances to instance or class methods. Maybe a simple example will help:
def outerf(): pass # a true Python function class Foo: f = outerf def g(self): pass print(f) # <function f at ...> print(Foo.f) # <function outerf at ...> print(Foo.g) # <function Foo.g at ...> print() x = Foo() print(x.f) # <bound method outerf of <__main__.Foo object at ...>> print(x.g) # <bound method Foo.g of <__main__.Foo object at ...>>
See how all of these start out as
function instances in the class
Foo but end up as bound methods in the
Foo instance? When an instance is made, Python checks its class attributes for
function instances and makes bound methods, no matter if the
function was defined in the class or not.
So why does Python make bound methods? To implement instance method call syntax where the instance is automatically passed to the wrapped function’s first argument.
f() Foo.f() Foo.g(x) x.f() # TypeError: outerf() takes 0 positional arguments but 1 was given x.g() # same as Foo.g(x)
Well okay, this is convenient and all, but what if you want to opt out? You want your function to stay a function in its instance and its class. Well that’s what static methods are for. Now, you’re not going to use the
@staticmethod decorator here because decorators only work on function or class definitions, and your function is defined in another module. But you have the
from statsmodels.tsa.seasonal import seasonal_decompose class Foo4: my_seasonal_decompose = staticmethod(seasonal_decompose) print(seasonal_decompose) # <function seasonal_decompose at ...> print(Foo4.my_seasonal_decompose) # <function seasonal_decompose at ...> x4 = Foo4() print(x4.my_seasonal_decompose) # <function seasonal_decompose at ...>