Funky method reassignments
I saw this kind of example recently and thought it was funky. I wrote out an example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import profile
class ReassignMethods:
def useful_function(self,a):
print("case 1")
self._answer = a*10
self.useful_function = self.handle_second_case
return self._answer
def handle_second_case(self,b):
print("case 2")
self.useful_function = self.handle_third_case
return b*200
def handle_third_case(self,c):
print("case 3")
return c*3000
def example():
r = ReassignMethods()
for x in range(0,3):
print(f"Answer for x = {x}:",r.useful_function(x))
profile.run("example()")
If you run this, the output will be something like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
case 1
Answer for x = 0: 0
case 2
Answer for x = 1: 200
case 3
Answer for x = 2: 6000
14 function calls in 0.006 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(exec)
6 0.000 0.000 0.000 0.000 :0(print)
1 0.006 0.006 0.006 0.006 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.006 0.006 profile:0(example())
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.000 0.000 test.py:26(useful_function)
1 0.000 0.000 0.000 0.000 test.py:32(handle_second_case)
1 0.000 0.000 0.000 0.000 test.py:37(handle_third_case)
1 0.000 0.000 0.000 0.000 test.py:41(example)
In all honestly I’m not sure what the use case is for this.
It’s useful if you want a method to do something specific the first n times then have a default later. If you’re wanting to have the same method called with the same class instance returning differently for various calls, it’s not clear if it’s more efficient.
If I write another implementation doing the same thing I agree it does look ugly!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class NonReassigMethods:
def __init__(self):
self._useful_func_counter = 0
def useful_function(self,a):
if self._useful_func_counter == 0:
print("case 1")
self._useful_func_counter += 1
return a*10
elif self._useful_func_counter == 1:
print("case 2")
self._useful_func_counter += 1
return a*200
else:
print("case 3")
return a*3000
def example2():
r = NonReassigMethods()
for x in range(0,3):
print(f"Answer for x = {x}:",r.useful_function(x))
profile.run("example2()")
On a timeing scale with profile there’s no difference. When we use timeit and get to a million iterations there’s a small efficiency gain but we’re talking 3.3525129199551884e-07
vs 2.9735416699259077e-07
.
All in all, funky but I feel like the code needs better design than either of these examples. But then, absolutely depends on what you’re trying to achieve.