Batch_tfms/item_trfms in ImageDataLoaders VS DataBlock

This is a question that has been bothering me for a while. Ususally, I initialize both batch_trfms and item_trfms within the initialization of the datablock, e.g.

b = DataBlock(
blocks=(ImageBlock, CategoryBlock), 
splitter=RandomSplitter(valid_pct=0.2, seed=42),
get_x=ColReader('objID', pref='/home/svu/e0148772/images-sdss/', suff='.jpg'),
get_y=ColReader('AGN'),
batch_tfms=[Normalize()],
item_trfms=[DihedralItem(p=1.)])

But when I used to initialize the trfms in dataloders as follows, the code would still run, w/o performing the actual transformations:

dl = ImageDataLoaders.from_dblock(b, df, bs=64, batch_tfms=[Normalize()], item_trfms=[DihedralItem(p=1.)])

So I checked the source code for ImageDataLoaders. In fastai/fastai/data/core.py:

    @classmethod
    def from_dblock(cls, dblock, source, path='.',  bs=64, val_bs=None, shuffle=True, device=None, **kwargs):
        return dblock.dataloaders(source, path=path, bs=bs, val_bs=val_bs, shuffle=shuffle, device=device, **kwargs)

and when I click on dblock.dataloaders, which goes to dataloaders(…) in fastai/fastai/data/block.py

    def dataloaders(self, source, path='.', verbose=False, **kwargs):
        dsets = self.datasets(source, verbose=verbose)
        kwargs = {**self.dls_kwargs, **kwargs, 'verbose': verbose}
        return dsets.dataloaders(path=path, after_item=self.item_tfms, after_batch=self.batch_tfms, **kwargs)

It seems that the trfms intialized in ImageDataLoaders will be performed, and in fact will override the trfms defined in the initialization of DataBlock. So why aren’t they performed?