I am trying to understand the design of dispatch table in function tables.
Here is what I initially coded
class Aave():
def __init__(self,address):
self.address = address
def function_map(self):
mapping = {1:self.test(),2:self.test2()}
print(type(self.test()))
return mapping[self.address]()
def test(self):
print(1)
return 1
def test2(self):
print(2)
return 2
a = Aave(1)
print(a.function_map())
However, this function would call the 2 function self.test() and self.test2() instead of just the function associated with key self.test().
On the other hand, coding the dispatch table without brackets would perform the specified mapped function
like as such
def function_map(self):
mapping = {1:self.test,2:self.test2}
print(type(self.test()))
return mapping[self.address]()
I have referred to different resources trying to understand, but they don't explain the difference in behaviour.
I was wondering if anyone could provide some enlightenment regarding this issue.
Referred to:
https://www.oreilly.com/library/view/python-cookbook/0596001673/ch01s07.html
https://betterprogramming.pub/dispatch-tables-in-python-d37bcc443b0b
CodePudding user response:
To understand the issue, let's read the code as the interpreter does, line by line:
# First of all object creation is executed
a = Aave(1)
# This invokes method __init__ of class Aave, hence next step is
a.address = 1
# After the creation of a class instance, interpreter tries to run
print(a.function_map())
# But since value of a.function_map() is unknown it needs to be calculated first
# Hence, interpreter enters function_map method of class Aave
# Execution of
mapping = {1:self.test(), 2:self.test2()}
# requires the test() and test2() to be evaluated. Since brackets indicate function call
# Thus, test() runs, prints 1 and returns 1. If we could see the value of
# mapping after this, it would have been:
# {1: 1, 2: <unknown yet result of self.test2()>}
# ^
# This value is the result of self.test()
# Next self.test2() is evaluated, since it also has brackets and needs to be called
# test2() runs, prints 2 and returns 2.
# After this step, mapping looks like {1: 1, 2: 2}
# ^ ^
# Had this already result of self.test2()
# After this mapping is completely known and next line may be evaluated.
# Evaluating
print(type(self.test()))
# causes one more call to self.test,
# since it also has brackets.
# And when you finally evaluate last line
return mapping[self.address]()
# Keeping in mind, that mapping was {1: 1, 2: 2}, you try to take value by key 1 from mapping,
# which is 1 (int). And call it (since you put "()"). It causes error, because 1 int is not callable.
As a result, you have 2 calls to self.test() in lines
mapping = {1:self.test(), 2:self.test2()}
# AND
print(type(self.test()))
and one call to self.test2() in line
mapping = {1:self.test(), 2:self.test2()}
If you wanted to be able to call what you store in mapping, you would need to remove those brackets in mapping creation. This way you would store function-objects in a dict instead of function results.
mapping = {1:self.test(), 2:self.test2()} # Call functions and store results
# VS
mapping = {1:self.test, 2:self.test2} # Store functions themselves
It's the whole difference. With brackets - result of function call, without - function-object.
