Other answers are great, but they create an entire dictionary which may be slow. If you only want a read-only composition, then the following class will solve your problems:
class Composition_mapping(collections.abc.Mapping):
def __init__(self, f, g):
self.f = f
self.g = g
def __iter__(self):
return iter(self.g)
def __len__(self):
return len(self.g)
def keys(self):
return self.g.keys()
def __getitem__(self, item):
return self.f[self.g[item]]
For example:
a = {1: 5}
b = {5:9, 7 : 8}
c = Composition_mapping(b,a)
print(c[1])
>>> 9
If you decide to make it a dict, you can always do:
c = dict(c)
print(c)
>>> {1: 9}
This is safe, because Composition_mapping satisfies the mapping protocol, which means it is considered as a mapping. A mapping is an interface (protocol) for read-only dictionary-like structures. Note that it is not necessary to inherit from collections.abc.Mapping, you just need to implement the methods __getitem__, __iter__, __len__ __contains__, keys, items, values, get, __eq__, and __ne__ to be a mapping; after all Python favors duck typing. The reason my code inherits from collections.abc.Mapping is that it implements all the other methods for you except __getitem__, __iter__, __len__. See the official documentation for the details about protocols.
Creating a composition with this method will have constant time cost no matter how big the composed functions are. The look-ups can be a little bit slower due to double look-ups and extra function call, but you can always use c = dict(c) if you are going to make lots of calls in a performance critical part. More importantly, if you change a and b anywhere in your code, the changes will be reflected on c.