Fastai v2 code walk-thru 5

TypeDispatch

I did a bit of checking around how TypeDispatch() works. I used the example of flip_img used in code walkthrough 4 wherein we set the same to work for MyTensorImage type.

Then I did a %%debug on using an instance of Transform() that calls the flip_img function to apply only on MyTensorImage type.

%%debug
flip_t = Transform(flip_img)

The working of how the Transform constructs flip_img so that it is invoked via encodes function when you call flip_t() is explained using a similar example here. I will explain as to how the TypeDispatch() works here in the below post.

Let us come to the init function of Transform which is called by the call function of _TfmMeta. Here the enc is the flip_img function.

def __init__(self, enc=None, dec=None, filt=None, as_item=False):
        self.filt,self.as_item = ifnone(filt, self.filt),as_item
        self.init_enc = enc or dec
        if not self.init_enc: return

        # Passing enc/dec, so need to remove (base) class level enc/dec
        del(self.__class__.encodes,self.__class__.decodes)
        self.encodes,self.decodes = (TypeDispatch(),TypeDispatch())
        if enc:
            self.encodes.add(enc)
            self.order = getattr(self.encodes,'order',self.order)
        if dec: self.decodes.add(dec)

Let’s specifically focus on

# Passing enc/dec, so need to remove (base) class level enc/dec
            del(self.__class__.encodes,self.__class__.decodes)
            self.encodes,self.decodes = (TypeDispatch(),TypeDispatch())
            if enc:
                self.encodes.add(enc)

Here the encodes and decodes of Transform is deleted in first line and recreated as TypeDispatch() again in the second line. The init of TypeDispatch() takes in a list of functions and adds them to the attribute self.funcs. In the absence of any functions, the self.funcs is an empty dictionary. The self.cache is also an empty dictionary as there no functions during the init here.

def __init__(self, *funcs):
        self.funcs,self.cache = {},{}
        for f in funcs: self.add(f)
        self.inst = None

Let’s now look at these lines in the init of Transform.

if enc:
        self.encodes.add(enc)

We know that enc is flip_img function. This is now added to self.encodes via self.encodes.add(enc). We know that there is no add function in Transform. It is there in TypeDispatch()

def add(self, f):
        "Add type `t` and function `f`"
        self.funcs[_p1_anno(f) or object] = f
        self._reset()

Here the _p1_anno(flip_img) returns class '__main__.MyTensorImage'. Therefore we have

self.funcs[class '__main__.MyTensorImage'] = function flip_img at 0x1302d6dd0

The output is like this

ipdb>  self.funcs
{<class '__main__.MyTensorImage'>: <function flip_img at 0x1302d6dd0>}

In self._reset the _reset function of TypeDispatch() is called. This again sets all the keys and values of self.funcs in reverse order using cmp_instance key and then adds all of them to self.cache as well.

def _reset(self):
        self.funcs = {k:self.funcs[k] for k in sorted(self.funcs, key=cmp_instance, reverse=True)}
        self.cache = {**self.funcs}

The debugger output on each line of this code is shown below.

From here on the rest of the code in init of Transform gets run. But this gives a good way to look at how TypeDispatch functions.

1 Like