{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#default_exp basics" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "from fastcore.imports import *\n", "import builtins" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from fastcore.test import *\n", "from nbdev.showdoc import *\n", "from fastcore.nb_imports import *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Basic functionality\n", "\n", "> Basic functionality used in the fastai library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basics" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "defaults = SimpleNamespace()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# export\n", "def ifnone(a, b):\n", " \"`b` if `a` is None else `a`\"\n", " return b if a is None else a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since `b if a is None else a` is such a common pattern, we wrap it in a function. However, be careful, because python will evaluate *both* `a` and `b` when calling `ifnone` (which it doesn't do if using the `if` version directly)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(ifnone(None,1), 1)\n", "test_eq(ifnone(2 ,1), 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def maybe_attr(o, attr):\n", " \"`getattr(o,attr,o)`\"\n", " return getattr(o,attr,o)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Return the attribute `attr` for object `o`. If the attribute doesn't exist, then return the object `o` instead. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class myobj: myattr='foo'\n", "\n", "test_eq(maybe_attr(myobj, 'myattr'), 'foo')\n", "test_eq(maybe_attr(myobj, 'another_attr'), myobj)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def basic_repr(flds=None):\n", " if isinstance(flds, str): flds = re.split(', *', flds)\n", " flds = list(flds or [])\n", " def _f(self):\n", " sig = ', '.join(f'{o}={getattr(self,o)!r}' for o in flds)\n", " return f'{self.__class__.__name__}({sig})'\n", " return _f" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lookup a user-supplied list of attributes (`flds`) of an object and generate a string with the name of each attribute and its corresponding value. The format of this string is `key=value`, where `key` is the name of the attribute, and `value` is the value of the attribute. For each value, attempt to use the `__name__` attribute, otherwise fall back to using the value's `__repr__` when constructing the string. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class SomeClass:\n", " a=1\n", " b='foo'\n", " __repr__=basic_repr('a,b')\n", " __name__='some-class'\n", " \n", "class AnotherClass:\n", " c=SomeClass()\n", " d='bar'\n", " __repr__=basic_repr(['c', 'd'])\n", " \n", "sc = SomeClass() \n", "ac = AnotherClass()\n", "\n", "test_eq(repr(sc), \"SomeClass(a=1, b='foo')\")\n", "test_eq(repr(ac), \"AnotherClass(c=SomeClass(a=1, b='foo'), d='bar')\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def is_array(x):\n", " \"`True` if `x` supports `__array__` or `iloc`\"\n", " return hasattr(x,'__array__') or hasattr(x,'iloc')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, False)" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "is_array(np.array(1)),is_array([1])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def listify(o=None, *rest, use_list=False, match=None):\n", " \"Convert `o` to a `list`\"\n", " if rest: o = (o,)+rest\n", " if use_list: res = list(o)\n", " elif o is None: res = []\n", " elif isinstance(o, list): res = o\n", " elif isinstance(o, str) or is_array(o): res = [o]\n", " elif is_iter(o): res = list(o)\n", " else: res = [o]\n", " if match is not None:\n", " if is_coll(match): match = len(match)\n", " if len(res)==1: res = res*match\n", " else: assert len(res)==match, 'Match length mismatch'\n", " return res" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conversion is designed to \"do what you mean\", e.g:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(listify('hi'), ['hi'])\n", "test_eq(listify(array(1)), [array(1)])\n", "test_eq(listify(1), [1])\n", "test_eq(listify([1,2]), [1,2])\n", "test_eq(listify(range(3)), [0,1,2])\n", "test_eq(listify(None), [])\n", "test_eq(listify(1,2), [1,2])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[array([[0, 1, 2],\n", " [3, 4, 5],\n", " [6, 7, 8]])]" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "arr = np.arange(9).reshape(3,3)\n", "listify(arr)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[array([1, 2])]" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "listify(array([1,2]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Generators are turned into lists too:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "gen = (o for o in range(3))\n", "test_eq(listify(gen), [0,1,2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `match` to provide a length to match:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(listify(1,match=3), [1,1,1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If `match` is a sequence, it's length is used:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(listify(1,match=range(3)), [1,1,1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the listified item is not of length `1`, it must be the same length as `match`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(listify([1,1,1],match=3), [1,1,1])\n", "test_fail(lambda: listify([1,1],match=3))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def tuplify(o, use_list=False, match=None):\n", " \"Make `o` a tuple\"\n", " return tuple(listify(o, use_list=use_list, match=match))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "test_eq(tuplify(None),())\n", "test_eq(tuplify([1,2,3]),(1,2,3))\n", "test_eq(tuplify(1,match=[1,2,3]),(1,1,1))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def true(x):\n", " \"Test whether `x` is truthy; collections with >0 elements are considered `True`\"\n", " try: return bool(len(x))\n", " except: return bool(x)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(array(0), False),\n", " (array(1), True),\n", " (array([0]), True),\n", " (array([0, 1]), True),\n", " (1, True),\n", " (0, False),\n", " ('', False),\n", " (None, False)]" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[(o,true(o)) for o in\n", " (array(0),array(1),array([0]),array([0,1]),1,0,'',None)]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "class NullType:\n", " \"An object that is `False` and can be called, chained, and indexed\"\n", " def __getattr__(self,*args):return null\n", " def __call__(self,*args, **kwargs):return null\n", " def __getitem__(self, *args):return null\n", " def __bool__(self): return False\n", "\n", "null = NullType()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bool(null.hi().there[3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def tonull(x):\n", " \"Convert `None` to `null`\"\n", " return null if x is None else x" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": null, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bool(tonull(None).hi().there[3])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#export\n", "def get_class(nm, *fld_names, sup=None, doc=None, funcs=None, **flds):\n", " \"Dynamically create a class, optionally inheriting from `sup`, containing `fld_names`\"\n", " attrs = {}\n", " for f in fld_names: attrs[f] = None\n", " for f in listify(funcs): attrs[f.__name__] = f\n", " for k,v in flds.items(): attrs[k] = v\n", " sup = ifnone(sup, ())\n", " if not isinstance(sup, tuple): sup=(sup,)\n", "\n", " def _init(self, *args, **kwargs):\n", " for i,v in enumerate(args): setattr(self, list(attrs.keys())[i], v)\n", " for k,v in kwargs.items(): setattr(self,k,v)\n", "\n", " all_flds = [*fld_names,*flds.keys()]\n", " def _eq(self,b):\n", " return all([getattr(self,k)==getattr(b,k) for k in all_flds])\n", "\n", " if not sup: attrs['__repr__'] = basic_repr(all_flds)\n", " attrs['__init__'] = _init\n", " attrs['__eq__'] = _eq\n", " res = type(nm, sup, attrs)\n", " if doc is not None: res.__doc__ = doc\n", " return res" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "
get_class
[source]get_class
(**`nm`**, **\\*`fld_names`**, **`sup`**=*`None`*, **`doc`**=*`None`*, **`funcs`**=*`None`*, **\\*\\*`flds`**)\n",
"\n",
"Dynamically create a class, optionally inheriting from `sup`, containing `fld_names`"
],
"text/plain": [
"class
ignore_exceptions
[source]ignore_exceptions
()\n",
"\n",
"Context manager to ignore exceptions"
],
"text/plain": [
"noop
[source]noop
(**`x`**=*`None`*, **\\*`args`**, **\\*\\*`kwargs`**)\n",
"\n",
"Do nothing"
],
"text/plain": [
"noops
[source]noops
(**`x`**=*`None`*, **\\*`args`**, **\\*\\*`kwargs`**)\n",
"\n",
"Do nothing (method)"
],
"text/plain": [
"class
Inf
[source]Inf
()\n",
"\n",
"Infinite lists"
],
"text/plain": [
"class
AttrDict
[source]\n",
"\n",
"> AttrDict
() :: `dict`\n",
"\n",
"`dict` subclass that also provides access to keys as attrs"
],
"text/plain": [
"class
GetAttr
[source]\n",
"\n",
"> GetAttr
()\n",
"\n",
"Inherit from this to have all attr accesses in `self._xtra` passed down to `self.default`"
],
"text/plain": [
"class
fastuple
[source]\n",
"\n",
"> fastuple
(**`x`**=*`None`*, **\\*`rest`**) :: `tuple`\n",
"\n",
"A `tuple` with elementwise ops and more friendly __init__ behavior"
],
"text/plain": [
"fastuple.add
[source]fastuple.add
(**\\*`args`**)\n",
"\n",
"`+` is already defined in `tuple` for concat, so use `add` instead"
],
"text/plain": [
"fastuple.mul
[source]fastuple.mul
(**\\*`args`**)\n",
"\n",
"`*` is already defined in `tuple` for replicating, so use `mul` instead"
],
"text/plain": [
"class
bind
[source]bind
(**`func`**, **\\*`pargs`**, **\\*\\*`pkwargs`**)\n",
"\n",
"Same as `partial`, except you can use [`arg0`](/basics.html#arg0) [`arg1`](/basics.html#arg1) etc param placeholders"
],
"text/plain": [
"ImportEnum
[source]Enum
= []\n",
"\n",
"An `Enum` that can have its values imported"
],
"text/plain": [
"StrEnum
[source]Enum
= []\n",
"\n",
"An [`ImportEnum`](/basics.html#ImportEnum) that behaves like a `str`"
],
"text/plain": [
"class
Stateful
[source]Stateful
(**\\*`args`**, **\\*\\*`kwargs`**)\n",
"\n",
"A base class/mixin for objects that should not serialize all their state"
],
"text/plain": [
"class
PrettyString
[source]PrettyString
() :: `str`\n",
"\n",
"Little hack to get strings to show properly in Jupyter."
],
"text/plain": [
"ipython_shell
[source]ipython_shell
()\n",
"\n",
"Same as `get_ipython` but returns `False` if not in IPython"
],
"text/plain": [
"in_ipython
[source]in_ipython
()\n",
"\n",
"Check if code is running in some kind of IPython environment"
],
"text/plain": [
"in_colab
[source]in_colab
()\n",
"\n",
"Check if the code is running in Google Colaboratory"
],
"text/plain": [
"in_jupyter
[source]in_jupyter
()\n",
"\n",
"Check if the code is running in a jupyter notebook"
],
"text/plain": [
"in_notebook
[source]in_notebook
()\n",
"\n",
"Check if the code is running in a jupyter notebook"
],
"text/plain": [
"