{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#default_exp core.foundation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "from local.test import *\n", "from local.core.imports import *" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from local.notebook.showdoc import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Core\n", "\n", "> Basic functions used in the fastai library" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "defaults = SimpleNamespace()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Metaclasses" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See this [blog post](https://realpython.com/python-metaclasses/) for more information about metaclasses. \n", "- `PrePostInitMeta` ensures that the classes defined with it run `__pre_init__` and `__post_init__` (without having to write `self.__pre_init__()` and `self.__post_init__()` in the actual `init`\n", "- `NewChkMeta` gives the `PrePostInitMeta` functionality and ensures classes defined with it don't re-create an object of their type whenever it's passed to the constructor\n", "- `BypassNewMeta` ensures classes defined with it can easily be casted form objects they subclass." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class FixSigMeta(type):\n", " \"A metaclass that fixes the signature on classes that override __new__\"\n", " def __new__(cls, name, bases, dict):\n", " res = super().__new__(cls, name, bases, dict)\n", " if res.__init__ is not object.__init__: res.__signature__ = inspect.signature(res.__init__)\n", " return res" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class PrePostInitMeta(FixSigMeta):\n", " \"A metaclass that calls optional `__pre_init__` and `__post_init__` methods\"\n", " def __call__(cls, *args, **kwargs):\n", " res = cls.__new__(cls)\n", " if type(res)==cls:\n", " if hasattr(res,'__pre_init__'): res.__pre_init__(*args,**kwargs)\n", " res.__init__(*args,**kwargs)\n", " if hasattr(res,'__post_init__'): res.__post_init__(*args,**kwargs)\n", " return res" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

class PrePostInitMeta[source]

\n", "\n", "> PrePostInitMeta(**`name`**, **`bases`**, **`dict`**) :: [`FixSigMeta`](/core.foundation.html#FixSigMeta)\n", "\n", "A metaclass that calls optional `__pre_init__` and `__post_init__` methods" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(PrePostInitMeta, title_level=3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _T(metaclass=PrePostInitMeta):\n", " def __pre_init__(self): self.a = 0; assert self.a==0\n", " def __init__(self,b=0): self.a += 1; assert self.a==1\n", " def __post_init__(self): self.a += 1; assert self.a==2\n", "\n", "t = _T()\n", "test_eq(t.a, 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class NewChkMeta(FixSigMeta):\n", " \"Metaclass to avoid recreating object passed to constructor\"\n", " def __call__(cls, x=None, *args, **kwargs):\n", " if not args and not kwargs and x is not None and isinstance(x,cls):\n", " x._newchk = 1\n", " return x\n", "\n", " res = super().__call__(*((x,) + args), **kwargs)\n", " res._newchk = 0\n", " return res" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _T(metaclass=NewChkMeta):\n", " \"Testing\"\n", " def __init__(self, o=None, b=1):\n", " self.foo = getattr(o,'foo',0) + 1\n", " self.b = b" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _T2():\n", " def __init__(self, o): self.foo = getattr(o,'foo',0) + 1\n", "\n", "t = _T(1)\n", "test_eq(t.foo,1)\n", "t2 = _T(t)\n", "test_eq(t2.foo,1)\n", "test_is(t,t2)\n", "t3 = _T(t, b=2)\n", "test_eq(t3.b, 2)\n", "assert not t3 is t\n", "\n", "t = _T2(1)\n", "test_eq(t.foo,1)\n", "t2 = _T2(t)\n", "test_eq(t2.foo,2)\n", "\n", "test_eq(_T.__doc__, \"Testing\")\n", "# TODO: this shouldn't have \"self, \"\n", "test_eq(str(inspect.signature(_T)), '(self, o=None, b=1)')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class BypassNewMeta(FixSigMeta):\n", " \"Metaclass: casts `x` to this class if it's of type `cls._bypass_type`, initializing with `_new_meta` if available\"\n", " def __call__(cls, x=None, *args, **kwargs):\n", " if hasattr(cls, '_new_meta'): x = cls._new_meta(x, *args, **kwargs)\n", " elif not isinstance(x,getattr(cls,'_bypass_type',object)) or len(args) or len(kwargs):\n", " x = super().__call__(*((x,)+args), **kwargs)\n", " if cls!=x.__class__: x.__class__ = cls\n", " return x" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class T0: pass\n", "class _T(T0, metaclass=BypassNewMeta):\n", " _bypass_type=T0\n", " def __init__(self,x): self.x=x\n", "\n", "t = T0()\n", "t.a = 1\n", "t2 = _T(t)\n", "test_eq(type(t2), _T)\n", "test_eq(t2.a,1)\n", "test_is(t2,t)\n", "t = _T(2)\n", "t.x = 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Foundational functions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def copy_func(f):\n", " \"Copy a non-builtin function (NB `copy.copy` does not work for this)\"\n", " if not isinstance(f,types.FunctionType): return copy(f)\n", " fn = types.FunctionType(f.__code__, f.__globals__, f.__name__, f.__defaults__, f.__closure__)\n", " fn.__dict__.update(f.__dict__)\n", " return fn" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def patch_to(cls, as_prop=False):\n", " \"Decorator: add `f` to `cls`\"\n", " if not isinstance(cls, (tuple,list)): cls=(cls,)\n", " def _inner(f):\n", " for c_ in cls:\n", " nf = copy_func(f)\n", " # `functools.update_wrapper` when passing patched function to `Pipeline`, so we do it manually\n", " for o in functools.WRAPPER_ASSIGNMENTS: setattr(nf, o, getattr(f,o))\n", " nf.__qualname__ = f\"{c_.__name__}.{f.__name__}\"\n", " setattr(c_, f.__name__, property(nf) if as_prop else nf)\n", " return f\n", " return _inner" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _T3(int): pass\n", "\n", "@patch_to(_T3)\n", "def func1(x, a): return x+a\n", "\n", "t = _T3(1)\n", "test_eq(t.func1(2), 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If `cls` is a tuple, `f` is added to all types in the tuple." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _T4(int): pass\n", "@patch_to((_T3,_T4))\n", "def func2(x, a): return x+2*a\n", "\n", "t = _T3(1)\n", "test_eq(t.func2(1), 3)\n", "t = _T4(1)\n", "test_eq(t.func2(1), 3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def patch(f):\n", " \"Decorator: add `f` to the first parameter's class (based on f's type annotations)\"\n", " cls = next(iter(f.__annotations__.values()))\n", " return patch_to(cls)(f)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@patch\n", "def func(x:_T3, a):\n", " \"test\"\n", " return x+2\n", "\n", "t = _T3(1)\n", "test_eq(t.func(2), 3)\n", "test_eq(t.func.__qualname__, '_T3.func')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If annotation is a tuple, the function is added to all types in the tuple." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@patch\n", "def func3(x:(_T3,_T4), a):\n", " \"test\"\n", " return x+2*a\n", "\n", "t = _T3(1)\n", "test_eq(t.func3(2), 5)\n", "test_eq(t.func3.__qualname__, '_T3.func3')\n", "t = _T4(1)\n", "test_eq(t.func3(2), 5)\n", "test_eq(t.func3.__qualname__, '_T4.func3')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def patch_property(f):\n", " \"Decorator: add `f` as a property to the first parameter's class (based on f's type annotations)\"\n", " cls = next(iter(f.__annotations__.values()))\n", " return patch_to(cls, as_prop=True)(f)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@patch_property\n", "def prop(x:_T3): return x+1\n", "\n", "t = _T3(1)\n", "test_eq(t.prop, 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def _mk_param(n,d=None): return inspect.Parameter(n, inspect.Parameter.KEYWORD_ONLY, default=d)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def test_sig(f, b): test_eq(str(inspect.signature(f)), b)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def use_kwargs_dict(keep=False, **kwargs):\n", " \"Decorator: replace `**kwargs` in signature with `names` params\"\n", " def _f(f):\n", " sig = inspect.signature(f)\n", " sigd = dict(sig.parameters)\n", " k = sigd.pop('kwargs')\n", " s2 = {n:_mk_param(n,d) for n,d in kwargs.items() if n not in sigd}\n", " sigd.update(s2)\n", " if keep: sigd['kwargs'] = k\n", " f.__signature__ = sig.replace(parameters=sigd.values())\n", " return f\n", " return _f" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@use_kwargs_dict(y=1,z=None)\n", "def foo(a, b=1, **kwargs): pass\n", "test_sig(foo, '(a, b=1, *, y=1, z=None)')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def use_kwargs(names, keep=False):\n", " \"Decorator: replace `**kwargs` in signature with `names` params\"\n", " def _f(f):\n", " sig = inspect.signature(f)\n", " sigd = dict(sig.parameters)\n", " k = sigd.pop('kwargs')\n", " s2 = {n:_mk_param(n) for n in names if n not in sigd}\n", " sigd.update(s2)\n", " if keep: sigd['kwargs'] = k\n", " f.__signature__ = sig.replace(parameters=sigd.values())\n", " return f\n", " return _f" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@use_kwargs(['y', 'z'])\n", "def foo(a, b=1, **kwargs): pass\n", "test_sig(foo, '(a, b=1, *, y=None, z=None)')\n", "\n", "@use_kwargs(['y', 'z'], keep=True)\n", "def foo(a, *args, b=1, **kwargs): pass\n", "test_sig(foo, '(a, *args, b=1, y=None, z=None, **kwargs)')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def delegates(to=None, keep=False):\n", " \"Decorator: replace `**kwargs` in signature with params from `to`\"\n", " def _f(f):\n", " if to is None: to_f,from_f = f.__base__.__init__,f.__init__\n", " else: to_f,from_f = to,f\n", " from_f = getattr(from_f,'__func__',from_f)\n", " if hasattr(from_f,'__delwrap__'): return f\n", " sig = inspect.signature(from_f)\n", " sigd = dict(sig.parameters)\n", " k = sigd.pop('kwargs')\n", " s2 = {k:v for k,v in inspect.signature(to_f).parameters.items()\n", " if v.default != inspect.Parameter.empty and k not in sigd}\n", " sigd.update(s2)\n", " if keep: sigd['kwargs'] = k\n", " from_f.__signature__ = sig.replace(parameters=sigd.values())\n", " from_f.__delwrap__ = to_f\n", " return f\n", " return _f" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def basefoo(e, c=2): pass\n", "\n", "@delegates(basefoo)\n", "def foo(a, b=1, **kwargs): pass\n", "test_sig(foo, '(a, b=1, c=2)')\n", "\n", "@delegates(basefoo, keep=True)\n", "def foo(a, b=1, **kwargs): pass\n", "test_sig(foo, '(a, b=1, c=2, **kwargs)')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class BaseFoo:\n", " def __init__(self, e, c=2): pass\n", "\n", "@delegates()\n", "class Foo(BaseFoo):\n", " def __init__(self, a, b=1, **kwargs): super().__init__(**kwargs)\n", "\n", "test_sig(Foo, '(a, b=1, c=2)')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def funcs_kwargs(cls):\n", " \"Replace methods in `self._methods` with those from `kwargs`\"\n", " old_init = cls.__init__\n", " def _init(self, *args, **kwargs):\n", " for k in cls._methods:\n", " arg = kwargs.pop(k,None)\n", " if arg is not None:\n", " if isinstance(arg,types.MethodType): arg = types.MethodType(arg.__func__, self)\n", " setattr(self, k, arg)\n", " old_init(self, *args, **kwargs)\n", " functools.update_wrapper(_init, old_init)\n", " cls.__init__ = use_kwargs(cls._methods)(_init)\n", " return cls" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def method(f):\n", " \"Mark `f` as a method\"\n", " # `1` is a dummy instance since Py3 doesn't allow `None` any more\n", " return types.MethodType(f, 1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@funcs_kwargs\n", "class T:\n", " _methods=['b']\n", " def __init__(self, f=1, **kwargs): assert not kwargs\n", " def a(self): return 1\n", " def b(self): return 2\n", " \n", "t = T()\n", "test_eq(t.a(), 1)\n", "test_eq(t.b(), 2)\n", "t = T(b = lambda:3)\n", "test_eq(t.b(), 3)\n", "test_sig(T, '(f=1, *, b=None)')\n", "test_fail(lambda: T(a = lambda:3))\n", "\n", "@method\n", "def _f(self,a=1): return a+1\n", "t = T(b = _f)\n", "test_eq(t.b(2), 3)\n", "\n", "class T2(T):\n", " def __init__(self,a):\n", " super().__init__(b = lambda:3)\n", " self.a=a\n", "t = T2(a=1)\n", "test_eq(t.b(), 3)\n", "test_sig(T2, '(a)')\n", "\n", "def _g(a=1): return a+1\n", "class T3(T): b = staticmethod(_g)\n", "t = T3()\n", "test_eq(t.b(2), 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Runtime type checking is handy, so let's make it easy!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@contextmanager\n", "def working_directory(path):\n", " \"Change working directory to `path` and return to previous on exit.\"\n", " prev_cwd = Path.cwd()\n", " os.chdir(path)\n", " try: yield\n", " finally: os.chdir(prev_cwd)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#def is_listy(x): return isinstance(x,(list,tuple,Generator))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def add_docs(cls, cls_doc=None, **docs):\n", " \"Copy values from `docs` to `cls` docstrings, and confirm all public methods are documented\"\n", " if cls_doc is not None: cls.__doc__ = cls_doc\n", " for k,v in docs.items():\n", " f = getattr(cls,k)\n", " if hasattr(f,'__func__'): f = f.__func__ # required for class methods\n", " f.__doc__ = v\n", " # List of public callables without docstring\n", " nodoc = [c for n,c in vars(cls).items() if callable(c)\n", " and not n.startswith('_') and c.__doc__ is None]\n", " assert not nodoc, f\"Missing docs: {nodoc}\"\n", " assert cls.__doc__ is not None, f\"Missing class docs: {cls}\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def docs(cls):\n", " \"Decorator version of `add_docs`, using `_docs` dict\"\n", " add_docs(cls, **cls._docs)\n", " return cls" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _T:\n", " def f(self): pass\n", " @classmethod\n", " def g(cls): pass\n", "add_docs(_T, \"a\", f=\"f\", g=\"g\")\n", "\n", "test_eq(_T.__doc__, \"a\")\n", "test_eq(_T.f.__doc__, \"f\")\n", "test_eq(_T.g.__doc__, \"g\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def custom_dir(c, add:list):\n", " \"Implement custom `__dir__`, adding `add` to `cls`\"\n", " return dir(type(c)) + list(c.__dict__.keys()) + add" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

is_iter[source]

\n", "\n", "> is_iter(**`o`**)\n", "\n", "Test whether `o` can be used in a `for` loop" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(is_iter)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "assert is_iter([1])\n", "assert not is_iter(array(1))\n", "assert is_iter(array([1,2]))\n", "assert (o for o in range(3))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class _Arg:\n", " def __init__(self,i): self.i = i\n", "arg0 = _Arg(0)\n", "arg1 = _Arg(1)\n", "arg2 = _Arg(2)\n", "arg3 = _Arg(3)\n", "arg4 = _Arg(4)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class bind:\n", " \"Same as `partial`, except you can use `arg0` `arg1` etc param placeholders\"\n", " def __init__(self, fn, *pargs, **pkwargs):\n", " self.fn,self.pargs,self.pkwargs = fn,pargs,pkwargs\n", " self.maxi = max((x.i for x in pargs if isinstance(x, _Arg)), default=-1)\n", "\n", " def __call__(self, *args, **kwargs):\n", " args = list(args)\n", " kwargs = {**self.pkwargs,**kwargs}\n", " for k,v in kwargs.items():\n", " if isinstance(v,_Arg): kwargs[k] = args.pop(v.i)\n", " fargs = [args[x.i] if isinstance(x, _Arg) else x for x in self.pargs] + args[self.maxi+1:]\n", " return self.fn(*fargs, **kwargs)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def myfn(a,b,c,d=1,e=2): return(a,b,c,d,e)\n", "test_eq(bind(myfn, arg1, 17, arg0, e=3)(19,14), (14,17,19,1,3))\n", "test_eq(bind(myfn, 17, arg0, e=3)(19,14), (17,19,14,1,3))\n", "test_eq(bind(myfn, 17, e=3)(19,14), (17,19,14,1,3))\n", "test_eq(bind(myfn)(17,19,14), (17,19,14,1,2))\n", "test_eq(bind(myfn, 17,19,14,e=arg0)(3), (17,19,14,1,3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## GetAttr -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class GetAttr:\n", " \"Inherit from this to have all attr accesses in `self._xtra` passed down to `self.default`\"\n", " _default='default'\n", " @property\n", " def _xtra(self): return self._dir()\n", " def _dir(self): return [o for o in dir(getattr(self,self._default)) if not o.startswith('_')]\n", " def __getattr__(self,k):\n", " if k.startswith('__') or k in ('_xtra',self._default): raise AttributeError(k)\n", " xtra = getattr(self, '_xtra', None)\n", " if xtra is None or k in xtra:\n", " attr = getattr(self,self._default,None)\n", " if attr is not None: return getattr(attr, k)\n", " raise AttributeError(k)\n", " def __dir__(self): return custom_dir(self, self._dir() if self._xtra is None else self._dir())\n", "# def __getstate__(self): return self.__dict__\n", " def __setstate__(self,data): self.__dict__.update(data)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class B:\n", " def __init__(self): self.a = A()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@funcs_kwargs\n", "class A(GetAttr):\n", " wif=after_iter= noops\n", " _methods = 'wif after_iter'.split()\n", " _default = 'dataset'\n", " def __init__(self, **kwargs): pass" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = A()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.wif" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b = A(wif=a.wif)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tst = pickle.dumps(b)\n", "c = pickle.loads(tst)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.a.a" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _C(GetAttr):\n", " _xtra = ['lower']\n", " def __init__(self,a): self.default = a\n", " def foo(self): noop\n", "\n", "t = _C('Hi')\n", "test_eq(t.lower(), 'hi')\n", "test_fail(lambda: t.upper())\n", "assert 'lower' in dir(t)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def delegate_attr(self, k, to):\n", " \"Use in `__getattr__` to delegate to attr `to` without inheriting from `GetAttr`\"\n", " if k.startswith('_') or k==to: raise AttributeError(k)\n", " try: return getattr(getattr(self,to), k)\n", " except AttributeError: raise AttributeError(k) from None" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class _C:\n", " f = 'Hi'\n", " def __getattr__(self, k): return delegate_attr(self, k, 'f')\n", "\n", "t = _C()\n", "test_eq(t.lower(), 'hi')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## L -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def _is_array(x): return hasattr(x,'__array__') or hasattr(x,'iloc')\n", "\n", "def _listify(o):\n", " if o is None: return []\n", " if isinstance(o, list): return o\n", " if isinstance(o, str) or _is_array(o): return [o]\n", " if is_iter(o): return list(o)\n", " return [o]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "def coll_repr(c, max_n=10):\n", " \"String repr of up to `max_n` items of (possibly lazy) collection `c`\"\n", " return f'(#{len(c)}) [' + ','.join(itertools.islice(map(str,c), max_n)) + (\n", " '...' if len(c)>10 else '') + ']'" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(coll_repr(range(1000), 5), '(#1000) [0,1,2,3,4...]')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "def mask2idxs(mask):\n", " \"Convert bool mask or index list to index `L`\"\n", " if isinstance(mask,slice): return mask\n", " mask = list(mask)\n", " if len(mask)==0: return []\n", " it = mask[0]\n", " if hasattr(it,'item'): it = it.item()\n", " if isinstance(it,(bool,NoneType,np.bool_)): return [i for i,m in enumerate(mask) if m]\n", " return [int(i) for i in mask]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# just for tests\n", "import torch" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(mask2idxs([False,True,False,True]), [1,3])\n", "test_eq(mask2idxs(array([False,True,False,True])), [1,3])\n", "test_eq(mask2idxs(torch.tensor([False,True,False,True])), [1,3])\n", "test_eq(mask2idxs(array([1,2,3])), [1,2,3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "listable_types = typing.Collection,Generator,map,filter,zip" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class CollBase:\n", " \"Base class for composing a list of `items`\"\n", " def __init__(self, items): self.items = items\n", " def __len__(self): return len(self.items)\n", " def __getitem__(self, k): return self.items[k]\n", " def __setitem__(self, k, v): self.items[list(k) if isinstance(k,CollBase) else k] = v\n", " def __delitem__(self, i): del(self.items[i])\n", " def __repr__(self): return self.items.__repr__()\n", " def __iter__(self): return self.items.__iter__()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def cycle(o):\n", " \"Like `itertools.cycle` except creates list of `None`s if `o` is empty\"\n", " o = _listify(o)\n", " return itertools.cycle(o) if o is not None and len(o) > 0 else itertools.cycle([None])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(itertools.islice(cycle([1,2,3]),5), [1,2,3,1,2])\n", "test_eq(itertools.islice(cycle([]),3), [None]*3)\n", "test_eq(itertools.islice(cycle(None),3), [None]*3)\n", "test_eq(itertools.islice(cycle(1),3), [1,1,1])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def zip_cycle(x, *args):\n", " \"Like `itertools.zip_longest` but `cycle`s through elements of all but first argument\"\n", " return zip(x, *map(cycle,args))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(zip_cycle([1,2,3,4],list('abc')), [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'a')])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def is_indexer(idx):\n", " \"Test whether `idx` will index a single item in a list\"\n", " return isinstance(idx,int) or not getattr(idx,'ndim',1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def negate_func(f):\n", " \"Create new function that negates result of `f`\"\n", " def _f(*args, **kwargs): return not f(*args, **kwargs)\n", " return _f" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def f(a): return a>0\n", "test_eq(f(1),True)\n", "test_eq(negate_func(f)(1),False)\n", "test_eq(negate_func(f)(a=-1),True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class L(CollBase, metaclass=NewChkMeta):\n", " \"Behaves like a list of `items` but can also index with list of indices or masks\"\n", " _default='items'\n", " def __init__(self, items=None, *rest, use_list=False, match=None):\n", " if rest: items = (items,)+rest\n", " if items is None: items = []\n", " if (use_list is not None) or not _is_array(items):\n", " items = list(items) if use_list else _listify(items)\n", " if match is not None:\n", " if is_coll(match): match = len(match)\n", " if len(items)==1: items = items*match\n", " else: assert len(items)==match, 'Match length mismatch'\n", " super().__init__(items)\n", "\n", " @property\n", " def _xtra(self): return None\n", " def _new(self, items, *args, **kwargs): return type(self)(items, *args, use_list=None, **kwargs)\n", " def __getitem__(self, idx): return self._get(idx) if is_indexer(idx) else L(self._get(idx), use_list=None)\n", " def copy(self): return self._new(self.items.copy())\n", "\n", " def _get(self, i):\n", " if is_indexer(i) or isinstance(i,slice): return getattr(self.items,'iloc',self.items)[i]\n", " i = mask2idxs(i)\n", " return (self.items.iloc[list(i)] if hasattr(self.items,'iloc')\n", " else self.items.__array__()[(i,)] if hasattr(self.items,'__array__')\n", " else [self.items[i_] for i_ in i])\n", "\n", " def __setitem__(self, idx, o):\n", " \"Set `idx` (can be list of indices, or mask, or int) items to `o` (which is broadcast if not iterable)\"\n", " idx = idx if isinstance(idx,L) else _listify(idx)\n", " if not is_iter(o): o = [o]*len(idx)\n", " for i,o_ in zip(idx,o): self.items[i] = o_\n", "\n", " def __iter__(self): return iter(self.items.itertuples() if hasattr(self.items,'iloc') else self.items)\n", " def __contains__(self,b): return b in self.items\n", " def __invert__(self): return self._new(not i for i in self)\n", " def __eq__(self,b): return False if isinstance(b, (str,dict,set)) else all_equal(b,self)\n", " def __repr__(self): return repr(self.items) if _is_array(self.items) else coll_repr(self)\n", " def __mul__ (a,b): return a._new(a.items*b)\n", " def __add__ (a,b): return a._new(a.items+_listify(b))\n", " def __radd__(a,b): return a._new(b)+a\n", " def __addi__(a,b):\n", " a.items += list(b)\n", " return a\n", "\n", " def sorted(self, key=None, reverse=False):\n", " if isinstance(key,str): k=lambda o:getattr(o,key,0)\n", " elif isinstance(key,int): k=itemgetter(key)\n", " else: k=key\n", " return self._new(sorted(self.items, key=k, reverse=reverse))\n", "\n", " @classmethod\n", " def split(cls, s, sep=None, maxsplit=-1): return cls(s.split(sep,maxsplit))\n", "\n", " @classmethod\n", " def range(cls, a, b=None, step=None):\n", " if is_coll(a): a = len(a)\n", " return cls(range(a,b,step) if step is not None else range(a,b) if b is not None else range(a))\n", "\n", " def map(self, f, *args, **kwargs):\n", " g = (bind(f,*args,**kwargs) if callable(f)\n", " else f.format if isinstance(f,str)\n", " else f.__getitem__)\n", " return self._new(map(g, self))\n", "\n", " def filter(self, f, negate=False, **kwargs):\n", " if kwargs: f = partial(f,**kwargs)\n", " if negate: f = negate_func(f)\n", " return self._new(filter(f, self))\n", "\n", " def unique(self): return L(dict.fromkeys(self).keys())\n", " def enumerate(self): return L(enumerate(self))\n", " def val2idx(self): return {v:k for k,v in self.enumerate()}\n", " def itemgot(self, *idxs):\n", " x = self\n", " for idx in idxs: x = x.map(itemgetter(idx))\n", " return x\n", " \n", " def attrgot(self, k, default=None): return self.map(lambda o:getattr(o,k,default))\n", " def cycle(self): return cycle(self)\n", " def map_dict(self, f=noop, *args, **kwargs): return {k:f(k, *args,**kwargs) for k in self}\n", " def starmap(self, f, *args, **kwargs): return self._new(itertools.starmap(partial(f,*args,**kwargs), self))\n", " def zip(self, cycled=False): return self._new((zip_cycle if cycled else zip)(*self))\n", " def zipwith(self, *rest, cycled=False): return self._new([self, *rest]).zip(cycled=cycled)\n", " def map_zip(self, f, *args, cycled=False, **kwargs): return self.zip(cycled=cycled).starmap(f, *args, **kwargs)\n", " def map_zipwith(self, f, *rest, cycled=False, **kwargs): return self.zipwith(*rest, cycled=cycled).starmap(f, **kwargs)\n", " def concat(self): return self._new(itertools.chain.from_iterable(self.map(L)))\n", " def shuffle(self):\n", " it = copy(self.items)\n", " random.shuffle(it)\n", " return self._new(it)\n", " \n", " def append(self,o): return self.items.append(o)\n", " def remove(self,o): return self.items.remove(o)\n", " def count (self,o): return self.items.count(o)\n", " def reverse(self ): return self.items.reverse()\n", " def pop(self,o=-1): return self.items.pop(o)\n", " def clear(self ): return self.items.clear()\n", " def index(self, value, start=0, stop=sys.maxsize): return self.items.index(value, start=start, stop=stop)\n", " def sort(self, key=None, reverse=False): return self.items.sort(key=key, reverse=reverse)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "_docs = {o:\"Passthru to `list` method\" for o in\n", " 'append count remove reverse sort pop clear index'.split()}\n", "add_docs(L,\n", " __getitem__=\"Retrieve `idx` (can be list of indices, or mask, or int) items\",\n", " range=\"Same as `range`, but returns an `L`. Can pass a collection for `a`, to use `len(a)`\",\n", " split=\"Same as `str.split`, but returns an `L`\",\n", " copy=\"Same as `list.copy`, but returns an `L`\",\n", " sorted=\"New `L` sorted by `key`. If key is str then use `attrgetter`. If key is int then use `itemgetter`\",\n", " unique=\"Unique items, in stable order\",\n", " val2idx=\"Dict from value to index\",\n", " filter=\"Create new `L` filtered by predicate `f`, passing `args` and `kwargs` to `f`\",\n", " map=\"Create new `L` with `f` applied to all `items`, passing `args` and `kwargs` to `f`\",\n", " map_dict=\"Like `map`, but creates a dict from `items` to function results\",\n", " starmap=\"Like `map`, but use `itertools.starmap`\",\n", " itemgot=\"Create new `L` with item `idx` of all `items`\",\n", " attrgot=\"Create new `L` with attr `k` of all `items`\",\n", " cycle=\"Same as `itertools.cycle`\",\n", " enumerate=\"Same as `enumerate`\",\n", " zip=\"Create new `L` with `zip(*items)`\",\n", " zipwith=\"Create new `L` with `self` zip with each of `*rest`\",\n", " map_zip=\"Combine `zip` and `starmap`\",\n", " map_zipwith=\"Combine `zipwith` and `starmap`\",\n", " concat=\"Concatenate all elements of list\",\n", " shuffle=\"Same as `random.shuffle`, but not inplace\",\n", " **_docs\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can create an `L` from an existing iterable (e.g. a list, range, etc) and access or modify it with an int list/tuple index, mask, int, or slice. All `list` methods can also be used with `L`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(#12) [11,10,9,j,7,k,5,4,3,2...]" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = L(range(12))\n", "test_eq(t, list(range(12)))\n", "test_ne(t, list(range(11)))\n", "t.reverse()\n", "test_eq(t[0], 11)\n", "t[3] = \"h\"\n", "test_eq(t[3], \"h\")\n", "t[3,5] = (\"j\",\"k\")\n", "test_eq(t[3,5], [\"j\",\"k\"])\n", "test_eq(t, L(t))\n", "test_eq(L(L(1,2),[3,4]), ([1,2],[3,4]))\n", "t" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are optimized indexers for arrays, tensors, and DataFrames." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "arr = np.arange(9).reshape(3,3)\n", "t = L(arr, use_list=None)\n", "test_eq(t[1,2], arr[[1,2]])\n", "\n", "arr = np.arange(9).reshape(3,3)\n", "t = L(arr, use_list=None)\n", "test_eq(t[1,2], arr[[1,2]])\n", "\n", "df = pd.DataFrame({'a':[1,2,3]})\n", "t = L(df, use_list=None)\n", "test_eq(t[1,2], L(pd.DataFrame({'a':[2,3]}, index=[1,2]), use_list=None))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also modify an `L` with `append`, `+`, and `*`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t = L()\n", "test_eq(t, [])\n", "t.append(1)\n", "test_eq(t, [1])\n", "t += [3,2]\n", "test_eq(t, [1,3,2])\n", "t = t + [4]\n", "test_eq(t, [1,3,2,4])\n", "t = 5 + t\n", "test_eq(t, [5,1,3,2,4])\n", "test_eq(L(1,2,3), [1,2,3])\n", "test_eq(L(1,2,3), L(1,2,3))\n", "t = L(1)*5\n", "t = t.map(operator.neg)\n", "test_eq(t,[-1]*5)\n", "test_eq(~L([True,False,False]), L([False,True,True]))\n", "t = L(range(4))\n", "test_eq(zip(t, L(1).cycle()), zip(range(4),(1,1,1,1)))\n", "t = L.range(100)\n", "test_shuffled(t,t.shuffle())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def _f(x,a=0): return x+a\n", "t = L(1)*5\n", "test_eq(t.map(_f), t)\n", "test_eq(t.map(_f,1), [2]*5)\n", "test_eq(t.map(_f,a=2), [3]*5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An `L` can be constructed from anything iterable, although tensors and arrays will not be iterated over on construction, unless you pass `use_list` to the constructor." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L([1,2,3]),[1,2,3])\n", "test_eq(L(L([1,2,3])),[1,2,3])\n", "test_ne(L([1,2,3]),[1,2,])\n", "test_eq(L('abc'),['abc'])\n", "test_eq(L(range(0,3)),[0,1,2])\n", "test_eq(L(o for o in range(0,3)),[0,1,2])\n", "test_eq(L(array(0)),[array(0)])\n", "test_eq(L([array(0),array(1)]),[array(0),array(1)])\n", "test_eq(L(array([0.,1.1]))[0],array([0.,1.1]))\n", "test_eq(L(array([0.,1.1]), use_list=True), [array(0.),array(1.1)]) # `use_list=True` to unwrap arrays/arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If `match` is not `None` then the created list is same len as `match`, either by:\n", "\n", "- If `len(items)==1` then `items` is replicated,\n", "- Otherwise an error is raised if `match` and `items` are not already the same size." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L(1,match=[1,2,3]),[1,1,1])\n", "test_eq(L([1,2],match=[2,3]),[1,2])\n", "test_fail(lambda: L([1,2],match=[1,2,3]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you create an `L` from an existing `L` then you'll get back the original object (since `L` uses the `NewChkMeta` metaclass)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_is(L(t), t)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An `L` is considred equal to a list if they have the same elements. It's never considered equal to a `str` a `set` or a `dict` even if they have the same elements/keys." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L(['a', 'b']), ['a', 'b'])\n", "test_ne(L(['a', 'b']), 'ab')\n", "test_ne(L(['a', 'b']), {'a', 'b'})\n", "test_ne(L(['a', 'b']), {'a':1, 'b':2})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Methods" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.__getitem__[source]

\n", "\n", "> L.__getitem__(**`idx`**)\n", "\n", "Retrieve `idx` (can be list of indices, or mask, or int) items" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.__getitem__)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t = L(range(12))\n", "test_eq(t[1,2], [1,2]) # implicit tuple\n", "test_eq(t[[1,2]], [1,2]) # list\n", "test_eq(t[:3], [0,1,2]) # slice\n", "test_eq(t[[False]*11 + [True]], [11]) # mask\n", "test_eq(t[array(3)], 3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.__setitem__[source]

\n", "\n", "> L.__setitem__(**`idx`**, **`o`**)\n", "\n", "Set `idx` (can be list of indices, or mask, or int) items to `o` (which is broadcast if not iterable)" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.__setitem__)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t[4,6] = 0\n", "test_eq(t[4,6], [0,0])\n", "t[4,6] = [1,2]\n", "test_eq(t[4,6], [1,2])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.unique[source]

\n", "\n", "> L.unique()\n", "\n", "Unique items, in stable order" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.unique)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L(1,2,3,4,4).unique(), [1,2,3,4])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.val2idx[source]

\n", "\n", "> L.val2idx()\n", "\n", "Dict from value to index" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.val2idx)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L(1,2,3).val2idx(), {3:2,1:0,2:1})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.filter[source]

\n", "\n", "> L.filter(**`f`**, **`negate`**=*`False`*, **\\*\\*`kwargs`**)\n", "\n", "Create new [`L`](/core.html#L) filtered by predicate `f`, passing `args` and `kwargs` to `f`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.filter)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 2, 3, 1, 5, 2, 7, 8, 9, 10, 11]" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(t)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(t.filter(lambda o:o<5), [0,1,2,3,1,2])\n", "test_eq(t.filter(lambda o:o<5, negate=True), [5,7,8,9,10,11])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.map[source]

\n", "\n", "> L.map(**`f`**, **\\*`args`**, **\\*\\*`kwargs`**)\n", "\n", "Create new [`L`](/core.html#L) with `f` applied to all `items`, passing `args` and `kwargs` to `f`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.map)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L.range(4).map(operator.neg), [0,-1,-2,-3])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If `f` is a string then it is treated as a format string to create the mapping:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L.range(4).map('#{}#'), ['#0#','#1#','#2#','#3#'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If `f` is a dictionary (or anything supporting `__getitem__`) then it is indexed to create the mapping:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L.range(4).map(list('abcd')), list('abcd'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the special argument `_arg` is passed, then that is the kwarg used in the map." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#What is this? TODO Jeremy: fix\n", "#L.range(4).map(f, b=arg0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def f(a=None,b=None): return b\n", "test_eq(L.range(4).map(f, b=arg0), range(4))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.map_dict[source]

\n", "\n", "> L.map_dict(**`f`**=*`'noop'`*, **\\*`args`**, **\\*\\*`kwargs`**)\n", "\n", "Like `map`, but creates a dict from `items` to function results" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.map_dict)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L(range(1,5)).map_dict(), {1:1, 2:2, 3:3, 4:4})\n", "test_eq(L(range(1,5)).map_dict(operator.neg), {1:-1, 2:-2, 3:-3, 4:-4})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.zip[source]

\n", "\n", "> L.zip(**`cycled`**=*`False`*)\n", "\n", "Create new [`L`](/core.html#L) with `zip(*items)`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.zip)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t = L([[1,2,3],'abc'])\n", "test_eq(t.zip(), [(1, 'a'),(2, 'b'),(3, 'c')])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t = L([[1,2,3,4],['a','b','c']])\n", "test_eq(t.zip(cycled=True ), [(1, 'a'),(2, 'b'),(3, 'c'),(4, 'a')])\n", "test_eq(t.zip(cycled=False), [(1, 'a'),(2, 'b'),(3, 'c')])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.map_zip[source]

\n", "\n", "> L.map_zip(**`f`**, **\\*`args`**, **`cycled`**=*`False`*, **\\*\\*`kwargs`**)\n", "\n", "Combine `zip` and `starmap`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.map_zip)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t = L([1,2,3],[2,3,4])\n", "test_eq(t.map_zip(operator.mul), [2,6,12])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.zipwith[source]

\n", "\n", "> L.zipwith(**\\*`rest`**, **`cycled`**=*`False`*)\n", "\n", "Create new [`L`](/core.html#L) with `self` zip with each of `*rest`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.zipwith)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b = [[0],[1],[2,2]]\n", "t = L([1,2,3]).zipwith(b)\n", "test_eq(t, [(1,[0]), (2,[1]), (3,[2,2])])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.map_zipwith[source]

\n", "\n", "> L.map_zipwith(**`f`**, **\\*`rest`**, **`cycled`**=*`False`*, **\\*\\*`kwargs`**)\n", "\n", "Combine `zipwith` and `starmap`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.map_zipwith)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L(1,2,3).map_zipwith(operator.mul, [2,3,4]), [2,6,12])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.itemgot[source]

\n", "\n", "> L.itemgot(**\\*`idxs`**)\n", "\n", "Create new [`L`](/core.html#L) with item `idx` of all `items`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.itemgot)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(t.itemgot(1), b)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.attrgot[source]

\n", "\n", "> L.attrgot(**`k`**, **`default`**=*`None`*)\n", "\n", "Create new [`L`](/core.html#L) with attr `k` of all `items`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.attrgot)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = [SimpleNamespace(a=3,b=4),SimpleNamespace(a=1,b=2)]\n", "test_eq(L(a).attrgot('b'), [4,2])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.sorted[source]

\n", "\n", "> L.sorted(**`key`**=*`None`*, **`reverse`**=*`False`*)\n", "\n", "New [`L`](/core.html#L) sorted by `key`. If key is str then use `attrgetter`. If key is int then use `itemgetter`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.sorted)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L(a).sorted('a').attrgot('b'), [2,4])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.split[source]

\n", "\n", "> L.split(**`s`**, **`sep`**=*`None`*, **`maxsplit`**=*`-1`*)\n", "\n", "Same as `str.split`, but returns an [`L`](/core.html#L)" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.split)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L.split('a b c'), list('abc'))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.range[source]

\n", "\n", "> L.range(**`a`**, **`b`**=*`None`*, **`step`**=*`None`*)\n", "\n", "Same as `range`, but returns an [`L`](/core.html#L). Can pass a collection for `a`, to use `len(a)`" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.range)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq_type(L.range([1,1,1]), L(range(3)))\n", "test_eq_type(L.range(5,2,2), L(range(5,2,2)))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.concat[source]

\n", "\n", "> L.concat()\n", "\n", "Concatenate all elements of list" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.concat)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(L([0,1,2,3],4,L(5,6)).concat(), range(7))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "

L.copy[source]

\n", "\n", "> L.copy()\n", "\n", "Same as `list.copy`, but returns an [`L`](/core.html#L)" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "show_doc(L.copy)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "t = L([0,1,2,3],4,L(5,6)).copy()\n", "test_eq(t.concat(), range(7))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Export -" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Converted 00_test.ipynb.\n", "Converted 01_core_foundation.ipynb.\n", "Converted 01a_core_utils.ipynb.\n", "Converted 01b_core_dispatch.ipynb.\n", "Converted 01c_core_transform.ipynb.\n", "Converted 02_core_script.ipynb.\n", "Converted 03_torchcore.ipynb.\n", "Converted 03a_layers.ipynb.\n", "Converted 04_data_load.ipynb.\n", "Converted 05_data_core.ipynb.\n", "Converted 06_data_transforms.ipynb.\n", "Converted 07_data_block.ipynb.\n", "Converted 08_vision_core.ipynb.\n", "Converted 09_vision_augment.ipynb.\n", "Converted 09a_vision_data.ipynb.\n", "Converted 09b_vision_utils.ipynb.\n", "Converted 10_pets_tutorial.ipynb.\n", "Converted 11_vision_models_xresnet.ipynb.\n", "Converted 12_optimizer.ipynb.\n", "Converted 13_learner.ipynb.\n", "Converted 13a_metrics.ipynb.\n", "Converted 14_callback_schedule.ipynb.\n", "Converted 14a_callback_data.ipynb.\n", "Converted 15_callback_hook.ipynb.\n", "Converted 15a_vision_models_unet.ipynb.\n", "Converted 16_callback_progress.ipynb.\n", "Converted 17_callback_tracker.ipynb.\n", "Converted 18_callback_fp16.ipynb.\n", "Converted 19_callback_mixup.ipynb.\n", "Converted 20_interpret.ipynb.\n", "Converted 20a_distributed.ipynb.\n", "Converted 21_vision_learner.ipynb.\n", "Converted 22_tutorial_imagenette.ipynb.\n", "Converted 23_tutorial_transfer_learning.ipynb.\n", "Converted 30_text_core.ipynb.\n", "Converted 31_text_data.ipynb.\n", "Converted 32_text_models_awdlstm.ipynb.\n", "Converted 33_text_models_core.ipynb.\n", "Converted 34_callback_rnn.ipynb.\n", "Converted 35_tutorial_wikitext.ipynb.\n", "Converted 36_text_models_qrnn.ipynb.\n", "Converted 37_text_learner.ipynb.\n", "Converted 38_tutorial_ulmfit.ipynb.\n", "Converted 40_tabular_core.ipynb.\n", "Converted 41_tabular_model.ipynb.\n", "Converted 42_tabular_rapids.ipynb.\n", "Converted 50_data_block_examples.ipynb.\n", "Converted 60_medical_imaging.ipynb.\n", "Converted 65_medical_text.ipynb.\n", "Converted 70_callback_wandb.ipynb.\n", "Converted 71_callback_tensorboard.ipynb.\n", "Converted 90_notebook_core.ipynb.\n", "Converted 91_notebook_export.ipynb.\n", "Converted 92_notebook_showdoc.ipynb.\n", "Converted 93_notebook_export2html.ipynb.\n", "Converted 94_notebook_test.ipynb.\n", "Converted 95_index.ipynb.\n", "Converted 96_data_external.ipynb.\n", "Converted 97_utils_test.ipynb.\n", "Converted notebook2jekyll.ipynb.\n", "Converted xse_resnext.ipynb.\n" ] } ], "source": [ "#hide\n", "from local.notebook.export import notebook2script\n", "notebook2script(all_fs=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 2 }