Suppose we have a python class like below:
class Test1:
def __init__(self, value):
self.value = value
def f(self):
return Test(self.value+1)
def g(self):
return Test(self.value*2)
test1 = Test1()
Both f
and g
serves as functions on the class instance in some mathematical notation. For example, I desire to run f(g(value))
in the notation. In the above class definition, the way to achieve it is by running test1.g().f()
. Although there is nothing wrong with the code, I just don’t like the fact that the order has changed, and it makes the line not that readable from a mathematics perspective.
I wonder if I could run the line like f(g(value))
with some custom magic or dunder method, but could not find one.
I understand that the way to achieve it is simply taking f
and g
out from the class like:
class Test1:
def __init__(self, value):
self.value = value
def f(obj):
return Test1(obj.value+1)
def g(obj):
return Test1(obj.value*2)
test1 = Test1()
new_test = f(g(test1)
However, I have some other similar classes that also have the same function/method names (f
, g
). So, if I run
from Test1 import Test1, f, g
from Test2 import Test2, f, g
there will be obvious conflicts. I understand that I could do things like
from Test1 import Test1, f as f1, g as g1
from Test2 import Test2, f as f2, g as g2
but, not that pleasant with this.
Is there any custom package like functools
that provide any ways? What I am expecting is something like
class Test1:
def __init__(self, value):
self.value = value
@some_special_stuff
def f(self):
return Test(self.value+1)
@some_special_stuff
def g(self):
return Test(self.value*2)
test1 = Test1()
new_test = f(g(test1)
Thank you in advance.
Advertisement
Answer
Your Test1
and Test2
classes have the same interface. We can model that
from typing import Protocol
# using a protocol here, so we don't have to explicitly inherit from TestProtocol
class TestProtocol(Protocol):
value: int
def f(self) -> 'TestProtocol':
def g(self) -> 'TestProtocol':
Now we know the interface and can define the free function f
and g
in terms of that interface:
def f(test: TestProtocol) -> TestProtocol:
return test.f()
def g(test: TestProtocol) -> TestProtocol:
return test.g()
Now the free functions f
and g
are compatible with every class that implements the protocol:
class Test1:
def __init__(self, value: int) -> None:
self.value = value
def f(self) -> 'Test1':
return Test1(self.value + 1)
def g(self) -> 'Test1':
return Test1(self.value * 2)
class Test2:
def __init__(self, value: int) -> None:
self.value = value
def f(self) -> 'Test2':
return Test2(self.value - 1)
def g(self) -> 'Test2':
return Test2(self.value // 2)
print(f(g(Test1(4))).value) # 9
print(f(g(Test2(4))).value) # 1
Actually, you don’t need the protocol at all. If you don’t care about mypy, remove all type hints and the protocol.
So here is the “short” form without type hints:
class Test1:
def __init__(self, value):
self.value = value
def f(self):
return Test1(self.value + 1)
def g(self):
return Test1(self.value * 2)
class Test2:
def f(test):
return test.f()
def g(test):
return test.g()
print(f(g(Test1(4))).value)
print(f(g(Test2(4))).value)