Chapter 19. How to understand "Self"

In Chapter 19. here is the relevant code:

from glob import glob
files = L(glob(f'{path}/**/*.JPEG', recursive=True)).map(Path)
lbls = files.map(Self.parent.name()).unique(); lbls

If I use labmda, I would write:
lbls = files.map(labmda x: x.parent.name).unique(); lbls

I try to understand how Self.parent.name() works. By reading the underlying code for Self, I cannot figure out. If anyone understands the code below, could you provide some hints on how to interpret the logic? Thanks!

The related underlying code (in utils.py):

class _SelfCls:
    def __getattr__(self,k): return getattr(_Self(),k)

# Cell
class _Self:
    "An alternative to `lambda` for calling methods on passed object."
    def __init__(self): self.nms,self.args,self.kwargs,self.ready = [],[],[],True
    def __repr__(self): return f'self: {self.nms}({self.args}, {self.kwargs})'

    def __call__(self, *args, **kwargs):
        if self.ready:
            x = args[0]
            for n,a,k in zip(self.nms,self.args,self.kwargs):
                x = getattr(x,n)
                if callable(x) and a is not None: x = x(*a, **k)
            return x
        else:
            self.args.append(args)
            self.kwargs.append(kwargs)
            self.ready = True
            return self

    def __getattr__(self,k):
        if not self.ready:
            self.args.append(None)
            self.kwargs.append(None)
        self.nms.append(k)
        self.ready = False
        return self

Self is another way to do that lambda more easily more or less. You can read the docs for it here: https://fastcore.fast.ai/utils.html (go down to “Self (with an uppercase S)” (otherwise I leave it as fastcore black magic myself :slight_smile: )