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.