{ "cells": [ { "cell_type": "markdown", "id": "7e085e53", "metadata": {}, "source": [ "# Parameter types\n", "\n", "You can get some of the benefit of Param from Parameter and Parameterized alone, such as having constant or readonly parameters, parameter value inheritance, and parameter docstrings. However, you'll typically want a more specialized Parameter type so that you can enforce type and bounds restrictions and enable suitable behavior specialized for that type of parameter. Param ships with a large set of pre-defined more-specific Parameter types, and additional custom parameters can and should be added for any domain-specific parameter types needed.\n", "\n", "The predefined types are organized into a class hierarchy where each type is a subclass of Parameter:\n", "\n", "- [String](#strings): String value, optionally constrained by a regular expression\n", "- [Color](#colors): A hex string specifying an RGB color, or a standard named color\n", "- [Boolean](#booleans): True or False (or None, if allowed)\n", " * [Event](#invocations): True/False parameter used to trigger actions (see [Dependencies and watchers](Dependencies_and_Watchers.ipynb)).\n", "- [Dynamic](#numbers): Base class for values that can be set to a callable that returns a concrete value\n", " * [Number](#numbers): Any type of numeric value\n", " - [Integer](#numbers): Positive or negative integer value\n", " - [Magnitude](#numbers): Positive value in the inclusive range 0.0,1.0\n", " - [Date](#numbers): Date or datetime type\n", " - [CalendarDate](#numbers): Date (not datetime)\n", "- [Tuple](#tuples): Python tuple of a fixed length and optional fixed type\n", " * [NumericTuple](#tuples): Python tuple of a fixed length and a numeric type\n", " - [XYCoordinates](#tuples): Position in an x,y plane\n", " - [Range](#tuples): A numeric range or interval\n", " * [DateRange](#tuples): A range of dates or datetimes\n", " * [CalendarDateRange](#tuples): A range of dates (not datetimes)\n", "- [List](#lists): A list of objects, potentially of a fixed, min, or max length\n", " * [HookList](#lists): A list of callables, for calling user-defined code at a processing stage\n", "- [Path](#paths): A POSIX-style string specifying the location of a local file or folder\n", " * [Filename](#paths): A POSIX-style string specifying the location of a local file\n", " * [Foldername](#paths): A POSIX-style string specifying the location of a local folder\n", "- [SelectorBase](#selectors): Abstract superclass covering various selector parameter types\n", " * [Selector](#selectors): One object selected out of a provided ordered list of objects\n", " - [FileSelector](#selectors): One filename selected out of those matching a provided glob\n", " - [ListSelector](#selectors): Multiple objects selected out of a provided list of objects\n", " - [MultiFileSelector](#selectors): Multiple filenames selected out of those matching a provided glob\n", " * [ClassSelector](#classselectors): An instance or class of a specified Python class or superclass\n", " - [Dict](#classselectors): A Python dictionary\n", " - [Array](#classselectors): NumPy array\n", " - [Series](#classselectors): A Pandas Series\n", " - [DataFrame](#classselectors): A Pandas DataFrame\n", "- [Callable](#invocations): A callable object, such as a function.\n", " * [Action](#invocations): A callable with no arguments, ready to invoke\n", "- [Composite](#invocations): A list of other attributes or parameters of this class, settable and readable as a group" ] }, { "cell_type": "markdown", "id": "e9787a67", "metadata": {}, "source": [ "The full behavior of these types is covered in the [Reference Manual](https://param.holoviz.org/reference.html#param-module). Here we will discuss the major categories of Parameter type and how to use them, including examples of what each type does _not_ allow (labeled `with param.exceptions_summarized():`). Each of these classes is also suitable for subclassing to create more specialized types enforcing your own specific constraints. After reading about Parameters in general, feel free to skip around in this page and only look at the Parameter types of interest to you!" ] }, { "cell_type": "markdown", "id": "0edcb97b", "metadata": {}, "source": [ "## Strings\n", "\n", "- [`param.String`](param.String): String value, with optional regular expression (regex) constraints\n", "\n", "A [`param.String`](param.String) may be set to any Python string value by default, or it may be constrained to match a provided regular expression `regex`. Like all other Parameters, it can optionally also `allow_None`, which will be true by default if the default value is None." ] }, { "cell_type": "code", "execution_count": null, "id": "625ce570", "metadata": {}, "outputs": [], "source": [ "import param\n", "\n", "class S(param.Parameterized):\n", " s = param.String('Four score', regex='[A-Z][a-z][a-z][a-z ]*')\n", "\n", "s = S()\n", "s.s" ] }, { "cell_type": "code", "execution_count": null, "id": "b72b6908", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " s.s = 5" ] }, { "cell_type": "code", "execution_count": null, "id": "1dac26fc", "metadata": {}, "outputs": [], "source": [ "s.s = 'Forever after'" ] }, { "cell_type": "code", "execution_count": null, "id": "924db709", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " s.s = 'four of spades'" ] }, { "cell_type": "code", "execution_count": null, "id": "d094459d", "metadata": {}, "outputs": [], "source": [ "ip_regex = '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'\n", "email_regex = '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'\n", "\n", "class I(param.Parameterized):\n", " ip_address = param.String('192.1.0.1', regex=ip_regex)\n", " email = param.String('example@me.com', regex=email_regex)\n", "i = I()\n", "i.ip_address" ] }, { "cell_type": "code", "execution_count": null, "id": "79585706", "metadata": {}, "outputs": [], "source": [ "i.ip_address=\"10.0.0.2\"\n", "i.email = \"user@gmail.com\"" ] }, { "cell_type": "code", "execution_count": null, "id": "82efd99d", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " i.ip_address='192.x.1.x'" ] }, { "cell_type": "markdown", "id": "65f03a46", "metadata": {}, "source": [ "## Colors\n", "\n", "- `param.Color`: Named color or hex RGB string (with or without a # prefix)\n", "\n", "\n", "A Color parameter specifies one of the standard [web color names](https://www.w3.org/TR/css-color-3/#svg-color) or an arbitrary hex RGB string. To support only hex RGB strings, specify `allow_named=False`.\n", "\n", "lemonchiffon" ] }, { "cell_type": "code", "execution_count": null, "id": "93ab3a83", "metadata": {}, "outputs": [], "source": [ "class C(param.Parameterized):\n", " c = param.Color('#EEFF00')\n", "\n", "c = C()\n", "c.c" ] }, { "cell_type": "code", "execution_count": null, "id": "9db80a95", "metadata": {}, "outputs": [], "source": [ "c.c = 'lemonchiffon'\n", "c.c" ] }, { "cell_type": "code", "execution_count": null, "id": "e14d63f1", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " c.c = 'puce'" ] }, { "cell_type": "code", "execution_count": null, "id": "b8d880a5", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " c.c = '#abcdefg'" ] }, { "cell_type": "markdown", "id": "91e09506", "metadata": {}, "source": [ "## Booleans\n", "\n", "- `param.Boolean`: A True or False value (or None, if allow_None is true)\n", "\n", "A Boolean may be True or False. Like all other Parameters, it can optionally also `allow_None`, which will be true by default if the default value is None." ] }, { "cell_type": "code", "execution_count": null, "id": "7a145fab", "metadata": {}, "outputs": [], "source": [ "class B(param.Parameterized):\n", " b = param.Boolean(True)\n", " n = param.Boolean(None)\n", "\n", "b = B()\n", "b.b" ] }, { "cell_type": "code", "execution_count": null, "id": "cb91b352", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " b.b=1" ] }, { "cell_type": "code", "execution_count": null, "id": "da7a9a2f", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " b.b=None" ] }, { "cell_type": "code", "execution_count": null, "id": "e5f68ef1", "metadata": {}, "outputs": [], "source": [ "b.n = True\n", "b.n = None" ] }, { "cell_type": "markdown", "id": "f31c0f1e", "metadata": {}, "source": [ "## Numbers\n", "\n", "- `param.Number`: Python floats, int, and Numpy values.\n", "- `param.Integer`: Python and Numpy integer values.\n", "- `param.Magnitude`: Same as `param.Number(..., bounds=(0.0,1.0))`.\n", "- `param.Date`: Date or datetime value of type `datetime.datetime`, `datetime.date`, or `numpy.datetime64`.\n", "- `param.CalendarDate`: Date value of type `datetime.date`.\n", "\n", "A Number is the most common type of Parameter. All Numbers in param are of class Dynamic, which allows them to be set not just to a single value but to a value that can repeatedly be drawn from a distribution or a sequence. (See [Dynamic Parameters](Dynamic_Parameters.ipynb) for more information about using these dynamic features, which will not be further discussed here.) Any Number has a default value (potentially None if allowed) and optional bounds.\n", "\n", "There are two types of bounds: ``bounds`` and ``softbounds``. ``bounds`` are hard bounds: the parameter must have a value within the specified range. The default bounds are (None,None), meaning there are actually no hard bounds. One or both bounds can be set by specifying a value (e.g. `bounds=(None,10)` means there is no lower bound, and an upper bound of 10). Bounds are inclusive by default, but exclusivity can be specified for each bound by setting inclusive_bounds (e.g. `inclusive_bounds=(True,False)` specifies an exclusive upper bound). \n", "\n", "When not being dynamically generated, `bounds` are checked whenever a Number is created or set. Using a default value outside the hard bounds, or one that is not numeric, results in an exception. When being dynamically generated, bounds are checked when the value of a Number is _requested_ (since it has no specific numeric value when first set). A generated value that is not numeric, or is outside the hard bounds, results in an exception. \n", "\n", "A separate set of ``softbounds`` is present to indicate the _typical_ range of the parameter, but these bounds are not enforced by Param. Setting the soft bounds allows a user to know what ranges of values are likely to be useful and allows a GUI to know what values to display on sliders for the Number; `softbounds` are thus suggestions or hints rather than enforced limits. \n", "\n", "Similarly, an optional ``step`` value can be provided to indicate the granularity of this parameter. As for `softbounds`, Param does not force values to conform to the provided step value, but (if provided) the step can be queried by user code and used for parameter sweeps (starting at the `softbounds` low and incrementing in value by `step` until the `softbounds` high), or by GUI code to determine steps on a settings dial.\n", "\n", "Several convenience methods for working with bounds are provided:\n", "- `get_soft_bounds()`: return the soft bounds (or hard bounds, if no soft bounds), for code that needs to know the typical range for this Parameter.\n", "- `crop_to_bounds(val)`: crop the provided value to fit into the hard bounds.\n", "- `set_in_bounds(obj,val)`: silently crop the given value into the legal range and set to the result, for building an API or user interface that accepts free-form input. \n", "\n", "Using Number parameters:" ] }, { "cell_type": "code", "execution_count": null, "id": "2ff07754", "metadata": {}, "outputs": [], "source": [ "import param\n", "\n", "class N(param.Parameterized):\n", " n = param.Number(5.6, bounds=(0,None), softbounds=(None,50))\n", " i = param.Integer(5, bounds=(0,50))\n", " \n", "a = N()\n", "a.n=2\n", "a.n" ] }, { "cell_type": "code", "execution_count": null, "id": "8e95f919", "metadata": {}, "outputs": [], "source": [ "N.param.n.set_in_bounds(a,-10)\n", "a.n" ] }, { "cell_type": "code", "execution_count": null, "id": "c8463d63", "metadata": {}, "outputs": [], "source": [ "a.param.n.set_in_bounds(a,-5)\n", "a.n" ] }, { "cell_type": "code", "execution_count": null, "id": "5db79041", "metadata": {}, "outputs": [], "source": [ "a.param.n.set_in_bounds(a,75)\n", "a.n" ] }, { "cell_type": "code", "execution_count": null, "id": "17a2faaf", "metadata": {}, "outputs": [], "source": [ "a.param.n.get_soft_bounds()" ] }, { "cell_type": "code", "execution_count": null, "id": "e887e3e4", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " a.n = -5" ] }, { "cell_type": "code", "execution_count": null, "id": "3ae9ca52", "metadata": {}, "outputs": [], "source": [ "a.n = 500\n", "a.n" ] }, { "cell_type": "code", "execution_count": null, "id": "764dd765", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " a.i=5.7" ] }, { "cell_type": "code", "execution_count": null, "id": "d3369af4", "metadata": {}, "outputs": [], "source": [ "import datetime\n", "\n", "class D(param.Parameterized):\n", " d = param.CalendarDate(datetime.date(1900, 1, 1))\n", " t = param.Date(datetime.datetime.fromisoformat('2002-12-25T00:00'))\n", "\n", "d = D()\n", "d.d = datetime.date.fromisoformat('2000-01-01')\n", "d.d" ] }, { "cell_type": "code", "execution_count": null, "id": "caef921b", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " d.d = 2022" ] }, { "cell_type": "code", "execution_count": null, "id": "995807bd", "metadata": {}, "outputs": [], "source": [ "d.t = datetime.date(1900, 1, 1)\n", "d.t" ] }, { "cell_type": "code", "execution_count": null, "id": "2c667efd", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " d.d = datetime.datetime.fromisoformat('2002-12-25T00:00')" ] }, { "cell_type": "markdown", "id": "4847bc8b", "metadata": {}, "source": [ "## Tuples" ] }, { "cell_type": "markdown", "id": "0602f947", "metadata": {}, "source": [ "- `param.Tuple`: Python tuple of a fixed length.\n", "- `param.NumericTuple`: Python tuple of a fixed length, with numeric values.\n", "- `param.XYCoordinates`: Python pair (2-tuple) of numeric values. Same as `param.NumericTuple(..., length=2)`, but semantically representing a 2D coordinate in a plane (e.g. for drawing programs or GUIs)\n", "- `param.Range`: `NumericTuple` representing a numeric range with optional bounds and softbounds.\n", "- `param.DateRange`: `Range` where the numeric type is a date or datetime (using same date types as `param.Date`).\n", "- `param.CalendarDateRange`: `Range` where the numeric type is a `datetime.date`. \n", "\n", "A tuple Parameter accepts a Python tuple for the value. Tuple parameters have a fixed length, typically set by the default value of the parameter but settable as the `length` if the default value is None. Only a tuple of the specified length will be accepted when a value is set.\n", "\n", "There are many tuple types as listed above, accepting either any type, numeric types, datetimes, dates, etc. `Range` types support `bounds`, `softbounds`, `inclusive_bounds`, and `step` on the numeric values in the tuple, similar to [Number](#numbers) types." ] }, { "cell_type": "code", "execution_count": null, "id": "aa7494a7", "metadata": {}, "outputs": [], "source": [ "class T(param.Parameterized):\n", " t = param.Range((-10,10), bounds=(-100,None), softbounds=(None,100))\n", "b = T()\n", "b.t" ] }, { "cell_type": "code", "execution_count": null, "id": "89b12646", "metadata": {}, "outputs": [], "source": [ "b.t = (50.2,50.3)\n", "b.t" ] }, { "cell_type": "code", "execution_count": null, "id": "b1f1f4c9", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " b.t = 5" ] }, { "cell_type": "code", "execution_count": null, "id": "2e481b37", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " b.t = (5,5,5)" ] }, { "cell_type": "code", "execution_count": null, "id": "03fb63e9", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " b.t = (5,\"5\")" ] }, { "cell_type": "code", "execution_count": null, "id": "cebd0b19", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " b.t = (-500,500)" ] }, { "cell_type": "code", "execution_count": null, "id": "e01e474a", "metadata": {}, "outputs": [], "source": [ "import datetime\n", "class D(param.Parameterized):\n", " d = param.CalendarDateRange((datetime.date.fromisoformat('1900-01-01'),\n", " datetime.date.fromisoformat('1910-12-31')))\n", "c = D()\n", "c.d" ] }, { "cell_type": "code", "execution_count": null, "id": "b84a21ed", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " c.d=(1905, 1907)" ] }, { "cell_type": "markdown", "id": "4befeab9", "metadata": {}, "source": [ "## Lists" ] }, { "cell_type": "markdown", "id": "76db67ce", "metadata": {}, "source": [ "- `param.List`: A Python list of objects, usually of a specified type.\n", "- `param.HookList`: A list of callable objects, for executing user-defined code at some processing stage\n", "\n", "List Parameters accept a Python list of objects. Typically the `item_type` will be specified for those objects, so that the rest of the code does not have to further check types when it refers to those values. Where appropriate, the `bounds` of the list can be set as (_min_length_, _max_length_), defaulting to `(0,None)`. Because List parameters already have an empty value ([]), they do not support `allow_None`.\n", "\n", "A `param.HookList` is a list whose elements are callable objects (typically either functions or objects with a `__call__` method). A `HookList` is intended for providing user configurability at various stages of some processing algorithm or pipeline. At present, there is no validation that the provided callable takes any particular number or type of arguments." ] }, { "cell_type": "code", "execution_count": null, "id": "e8351f51", "metadata": {}, "outputs": [], "source": [ "import param\n", "class L(param.Parameterized):\n", " ls = param.List([\"red\",\"green\",\"blue\"], item_type=str, bounds=(0,10))\n", "\n", "e = L()\n", "e.ls" ] }, { "cell_type": "code", "execution_count": null, "id": "6dbabdcb", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " e.ls = [1,2]" ] }, { "cell_type": "code", "execution_count": null, "id": "5accd58c", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " e.ls = [str(i) for i in range(20)]" ] }, { "cell_type": "code", "execution_count": null, "id": "811df0b8", "metadata": {}, "outputs": [], "source": [ "class multi_stage_example(param.Parameterized):\n", " before = param.HookList()\n", " during = param.HookList()\n", " after = param.HookList()\n", " \n", " values = param.List([1.5,-8.1,6.9,100.0], item_type=float)\n", " \n", " def __call__(self):\n", " for h in self.before: h(self)\n", " s = 0\n", " for v in self.values:\n", " v_ = v\n", " for h in self.during: v_ = h(v_)\n", " s += v_\n", " for h in self.after: h()\n", " return s\n", "\n", "ex = multi_stage_example()\n", "ex()" ] }, { "cell_type": "code", "execution_count": null, "id": "51c77d9e", "metadata": {}, "outputs": [], "source": [ "def validate(obj):\n", " for i in obj.values:\n", " if i<0:\n", " print(\"Negative value found in argument\")\n", "\n", "m = multi_stage_example(before=[validate])\n", "\n", "m()" ] }, { "cell_type": "code", "execution_count": null, "id": "db1b102e", "metadata": {}, "outputs": [], "source": [ "from math import fabs\n", "\n", "ex.during=[abs]\n", "ex()" ] }, { "cell_type": "markdown", "id": "3571b46b", "metadata": {}, "source": [ "## Paths\n", "\n", "- `param.Path`: A POSIX-style string specifying the location of a local file or folder\n", "- `param.Filename`: A POSIX-style string specifying the location of a local file\n", "- `param.Foldername`: A POSIX-style string specifying the location of a local folder\n", "\n", "A Path can be set to a string specifying the path of a file or folder. In code, the string should be specified in POSIX (UNIX) style, using forward slashes / and starting from / if absolute or some other character if relative. When retrieved, the string will be in the format of the user's operating system. \n", "\n", "Relative paths are converted to absolute paths by searching for a matching filename on the filesystem in the current working directory (obtained with `os.getcwd()`). If `search_paths` is provided and not empty, the folders in that list are searched for the given filename, in order, returning the absolute path for the first match found by appending the provided path to the search path. An IOError is raised if the file or folder is not found. If `search_paths` is empty (the default), the file or folder is expected to be in the current working directory.\n", "\n", "When `check_exists` is set to `False` (default is `True`) the provided path can optionally exist. This is for instance useful to declare an output file path that is meant to be created at a later stage in a process. In the default case the path must exist, on Parameter instantiation and setting.\n", "\n", "Either a file or a folder name is accepted by `param.Path`, while `param.Filename` accepts only file names and `param.Foldername` accepts only folder names." ] }, { "cell_type": "code", "execution_count": null, "id": "cb449123", "metadata": {}, "outputs": [], "source": [ "class P(param.Parameterized):\n", " p = param.Path('Parameter_Types.ipynb')\n", " f = param.Filename('Parameter_Types.ipynb')\n", " d = param.Foldername('lib', search_paths=['/','/usr','/share'])\n", " o = param.Filename('output.csv', check_exists=False)\n", " \n", "p = P()\n", "p.p" ] }, { "cell_type": "code", "execution_count": null, "id": "d3d08aa2", "metadata": {}, "outputs": [], "source": [ "p.p = '/usr/lib'\n", "p.p" ] }, { "cell_type": "code", "execution_count": null, "id": "f5a50ea9", "metadata": {}, "outputs": [], "source": [ "p.f" ] }, { "cell_type": "code", "execution_count": null, "id": "79112698", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " p.f = '/usr/lib'" ] }, { "cell_type": "code", "execution_count": null, "id": "266a525c", "metadata": {}, "outputs": [], "source": [ "p.d" ] }, { "cell_type": "code", "execution_count": null, "id": "6da8779e", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " p.d = 'Parameter_Types.ipynb'" ] }, { "cell_type": "code", "execution_count": null, "id": "53225144", "metadata": {}, "outputs": [], "source": [ "p.o # the output file doesn't exist yet" ] }, { "cell_type": "code", "execution_count": null, "id": "863b9817", "metadata": {}, "outputs": [], "source": [ "with open(p.o, 'w') as f:\n", " f.write('Param is awesome!')\n", "\n", "p.o # it now exists, getting its value resolve the full file path" ] }, { "cell_type": "markdown", "id": "573079ef", "metadata": {}, "source": [ "## Selectors\n", "\n", "- `param.Selector`: One object selected out of a provided ordered list of objects\n", "- `param.ListSelector`: Multiple objects selected out of a provided list of objects\n", "- `param.FileSelector`: One filename selected out of those matching a provided glob\n", "- `param.MultiFileSelector`: Multiple filenames selected out of those matching a provided glob\n", "\n", "The value of a Selector is one or more items from a set of allowed values. All Selector types must implement `get_range()`, providing a concrete list of available options for the value.\n", "\n", "A `param.Selector` accepts a list or dictionary of `objects`, and has a single default (current) value that must be one of those objects. If not otherwise specified, the default will be the first item from the list or dictionary. \n", "\n", "Providing the objects as a list is appropriate for selecting among a set of strings, or among a set of Parameterized objects that each have a \"name\" parameter. That way, a UI that lets users select by string will have a suitable string available for each object to let the user make a choice between them.\n", "\n", "Otherwise, the objects should be provided as a _name_:_value_ dictionary, where the string name will be stored for use in such a UI, but is not otherwise accessed by Param. The values from setting and getting the parameter are always the actual underlying object, not the string names.\n", "\n", "To make it easier to modify the collection of `objects`, they are wrapped in a container that supports both list-style and dictionary-style methods. This approach ensures that there is a consistent API for updating the `objects` and that modifying the objects in place still triggers an event.\n", "\n", "If the list of available objects is not meant be exhaustive, you can specify `check_on_set=False` (which automatically applies if the initial list is empty). Objects will then be added to the `objects` list whenever they are set, including as the initial default. `check_on_set=False` can be useful when the predefined set of objects is not exhaustive, letting a user select from the existing list for convenience while also being able to supply any other suitable object they can construct. When `check_on_set=True`, the initial value (and all subsequent values) must be in the `objects` list.\n", "\n", "Because `Selector` is usually used to allow selection from a list of existing (instantiated) objects, `instantiate` is False by default, but you can specify `instantiate=True` if you want each copy of this Parameter value to be independent of other instances and superclasses.\n", "\n", "In cases where the objects in the list cannot be known when writing the Parameterized class but can be calculated at runtime, you can supply a callable (of no arguments) to `compute_default_fn`, and then ensure that at runtime you call `compute_default` on that Parameter to initialize the value.\n", "\n", "A `param.ListSelector` works just the same as a regular Selector, but the value is a _list_ of valid objects from the available objects, rather than just one. Each item in the list is checked against the `objects`, and thus the current value is thus a _subset_ of the `objects`, rather than just one of the objects.\n", "\n", "A `param.FileSelector` works like a regular Selector with the value being a filename and the `objects` being computed from files on a file system. The files are specified as a `path` [glob](https://docs.python.org/3/library/glob.html), and all filenames matching the glob are valid `objects` for the parameter.\n", "\n", "A `param.MultiFileSelector` is the analog of ListSelector but for files, i.e., again supporting a path glob but allowing the user to select a list of filenames rather than a single filename. The default value in this case is _all_ of the matched files, not just the first one." ] }, { "cell_type": "code", "execution_count": null, "id": "98852f74", "metadata": {}, "outputs": [], "source": [ "colors = [\"red\",\"green\",\"blue\"]\n", "\n", "class S(param.Parameterized):\n", " o = param.Selector(objects=colors)\n", " ls = param.ListSelector(colors[0:2], objects=colors)\n", " \n", "s = S()\n", "s.o" ] }, { "cell_type": "code", "execution_count": null, "id": "49fb6839", "metadata": {}, "outputs": [], "source": [ "s.o = \"green\"\n", "s.o" ] }, { "cell_type": "code", "execution_count": null, "id": "b1d118a2", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " s.o = \"yellow\"" ] }, { "cell_type": "code", "execution_count": null, "id": "2f08db09", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " s.o = 42" ] }, { "cell_type": "code", "execution_count": null, "id": "2beac492", "metadata": {}, "outputs": [], "source": [ "s.ls" ] }, { "cell_type": "code", "execution_count": null, "id": "9ddf7d53", "metadata": {}, "outputs": [], "source": [ "s.ls=['blue']\n", "s.ls" ] }, { "cell_type": "code", "execution_count": null, "id": "1c22dcd1", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " s.ls=['red','yellow']\n", " s.ls" ] }, { "cell_type": "code", "execution_count": null, "id": "dc30268c", "metadata": {}, "outputs": [], "source": [ "class F(param.Parameterized):\n", " f = param.FileSelector(path='/usr/share/*')\n", " fs = param.MultiFileSelector(path='/usr/share/*')\n", " \n", "f = F()\n", "f.f" ] }, { "cell_type": "code", "execution_count": null, "id": "f4abfeee", "metadata": {}, "outputs": [], "source": [ "f.param.f.objects[0:3]" ] }, { "cell_type": "code", "execution_count": null, "id": "b58509a6", "metadata": {}, "outputs": [], "source": [ "f.fs = f.param.fs.objects[0:2]\n", "f.fs" ] }, { "cell_type": "markdown", "id": "3084902c", "metadata": {}, "source": [ "## ClassSelectors\n", "\n", "- `param.ClassSelector`: An instance or class of a specified Python class or superclass\n", "- `param.Dict`: A Python dictionary\n", "- `param.Array`: NumPy array\n", "- `param.Series`: A Pandas Series\n", "- `param.DataFrame`: A Pandas DataFrame\n", "\n", "A ClassSelector has a value that is either an instance or a subclass of a specified Python `class_`. By default, requires an instance of that class, but specifying `is_instance=False` means that a subclass must be selected instead. \n", "\n", "Like Selector types, all ClassSelector types implement `get_range()`, in this case providing an introspected list of all the concrete (not abstract) subclasses available for the given class. If you want a class to be treated as abstract so that it does not show up in such a list, you can have it declare `__abstract=True` as a class attribute. In a GUI, the range list allows a user to select a type of object they want to create, and they can then separately edit the new object's parameters (if any) to configure it appropriately." ] }, { "cell_type": "code", "execution_count": null, "id": "70809e48", "metadata": {}, "outputs": [], "source": [ "class C(param.Parameterized):\n", " e_instance = param.ClassSelector(default=ZeroDivisionError(\"1/0\"), class_=ArithmeticError)\n", " e_class = param.ClassSelector(default=ZeroDivisionError, class_=ArithmeticError, is_instance=False)\n", " \n", "c = C(e_class=OverflowError)\n", "c.e_class, c.e_instance" ] }, { "cell_type": "code", "execution_count": null, "id": "b3be8c3d", "metadata": {}, "outputs": [], "source": [ "c.param.e_instance.get_range()" ] }, { "cell_type": "code", "execution_count": null, "id": "92c50c04", "metadata": {}, "outputs": [], "source": [ "c.param.e_class.get_range()" ] }, { "cell_type": "code", "execution_count": null, "id": "0b499411", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " c.e_class = Exception" ] }, { "cell_type": "code", "execution_count": null, "id": "150cc654", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " c.e_instance = ArithmeticError" ] }, { "cell_type": "code", "execution_count": null, "id": "0d3d9c56", "metadata": {}, "outputs": [], "source": [ "c.e_instance = ArithmeticError()\n", "c.e_instance" ] }, { "cell_type": "markdown", "id": "dce7b733", "metadata": {}, "source": [ "Various types of ClassSelector are provided for specific data types:\n", "\n", "- `param.Dict`: `class_=dict`, accepting a Python dictionary\n", "- `param.Array`: `class=numpy.ndarray`, accepting a NumPy array\n", "- `param.Series`: `class_=pandas.Series`, a Pandas Series. Accepts constraints on the number of `rows`, either as an integer length (e.g. `rows=10`) or a range tuple `rows=(2,4)`).\n", "- `param.DataFrame`: `class_=pandas.DataFrame`, a Pandas DataFrame. Accepts constraints on the number of `rows` (as for `param.Series`) or `columns` (with numerical or range values as for `rows` or as a list of column names (which must appear in that order) or as a set of column names (which can appear in any order))." ] }, { "cell_type": "code", "execution_count": null, "id": "067f20c1", "metadata": {}, "outputs": [], "source": [ "import numpy as np, pandas as pd\n", "\n", "class D(param.Parameterized):\n", " d = param.Dict(dict(a=1, b=\"not set\", c=2.0))\n", " a = param.Array(np.array([1,-1]))\n", " s = param.Series(pd.Series([1,-1]))\n", " f = param.DataFrame(pd.DataFrame({'a':[1,2], 'b':[2,3], 'c':[4,5]}), rows=(2,None), columns=set(['a','b']))\n", "\n", "d = D()\n", "d.d = {5:np.nan}\n", "d.d" ] }, { "cell_type": "code", "execution_count": null, "id": "a321de5d", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " d.d=[(\"a\",1)]" ] }, { "cell_type": "code", "execution_count": null, "id": "50f01ab8", "metadata": {}, "outputs": [], "source": [ "df = pd.DataFrame({'b':[-2,-3], 'a':[-1,-2]})\n", "d.f = df\n", "d.f" ] }, { "cell_type": "code", "execution_count": null, "id": "bb1097b7", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " df = pd.DataFrame({'a':[-2,-3], 'c':[-1,-2]})\n", " d.f = df" ] }, { "cell_type": "code", "execution_count": null, "id": "44a2a32d", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " df = pd.DataFrame({'a':[-2], 'b':[-1]})\n", " d.f = df" ] }, { "cell_type": "markdown", "id": "dfbef370", "metadata": {}, "source": [ "### `classlist` and `param.descendents`\n", "\n", "If you are building a GUI or some other mechanism allowing the user to choose a class or an instance of a specified class in a ClassSelector, you may want to construct a list of all subclasses or superclasses of the given class. To make it easy to traverse upwards or downwards in the inheritance hierarchy in this way, param provides the `classlist` and `param.descendents` functions. `classlist` provides a list of the superclasses of the provided object, including itself, in order from least to most specific:" ] }, { "cell_type": "code", "execution_count": null, "id": "f4b40d09", "metadata": {}, "outputs": [], "source": [ "from param.parameterized import classlist\n", "\n", "classlist(D)" ] }, { "cell_type": "markdown", "id": "fbd3c5d3", "metadata": {}, "source": [ "As you can see, `D` is a type of `Parameterized`, and a `Parameterized` is a type of Python object. Conversely (and typically more usefully), `param.descendents` provides a list of the subclasses of the provided object, including itself:" ] }, { "cell_type": "code", "execution_count": null, "id": "abf691ca", "metadata": {}, "outputs": [], "source": [ "param.descendents(param.SelectorBase)" ] }, { "cell_type": "markdown", "id": "7cfe3c7b", "metadata": {}, "source": [ "As you can see, there are many subtypes of SelectorBase. This list is calculated from whatever subtypes are currently defined in this Python session. If you derive an additional subtype or load code that defines an additional subtype, this list will get longer, so you need to make sure that all such code has been executed before letting the user make a selection of a subtype." ] }, { "cell_type": "markdown", "id": "bd887647", "metadata": {}, "source": [ "## Invocations\n", "\n", "- `param.Callable`: A callable object, such as a function\n", "- `param.Action`: A callable with no arguments, ready to invoke\n", "- `param.Event`: Empty action, for use in triggering events for watchers\n", "- `param.Composite`: Wrapper around other parameters, letting them be set as a tuple\n", "\n", "Invocation parameters are a loose group of types that either contain an executable (callable) object, are invoked to execute some other code, or are set to change the value of some other parameter(s) or attribute(s).\n", "\n", "A Callable may be set to any callable object, typically either a function or else an instance of a class that provides a `__call__` method. At present, there is no validation that the provided callable takes any particular number or type of arguments. Lambdas can be provided, but note that the resulting parameter value will no longer be picklable, so if you need to use pickling (`setstate` and `getstate`), be sure to use a named function instead.\n", "\n", "An Action is the same as a Callable, but is expected to have no arguments. In a GUI an Action is typically mapped to a button whose name or label is the name of this parameter. \n", "\n", "An Event Parameter has a Boolean value but is primarily intended for triggering events on its watchers. See [Dependencies and Watchers](Dependencies_and_Watchers.ipynb) for the details.\n", "\n", "A Composite Parameter has a value that is looked up from the value of a list of attributes of this class (which may or may not be parameters) and that when set changes the values of those other attributes or parameters. This type of Parameter can be useful for treating a set of related values as a group for setting purposes, but as individual parameters for code that reads from them. As of Param 1.10, Composite parameters have not been tested with watchers and dependencies and may not behave appropriately for such uses." ] }, { "cell_type": "code", "execution_count": null, "id": "1f3b7fc2", "metadata": {}, "outputs": [], "source": [ "def identity(x): return x\n", " \n", "def print_time_of_day():\n", " print(datetime.date.today())\n", "\n", "class A(param.Parameterized):\n", " transformer = param.Callable(identity)\n", " a = param.Action(print_time_of_day)\n", " \n", " def __call__(self, x):\n", " return self.transformer(x)\n", " \n", "a = A()\n", "a(5)" ] }, { "cell_type": "code", "execution_count": null, "id": "70cd5a08", "metadata": {}, "outputs": [], "source": [ "def double(x):\n", " return 2*x\n", " \n", "d = A(transformer=double)\n", "d(5)" ] }, { "cell_type": "code", "execution_count": null, "id": "9c9aad93", "metadata": {}, "outputs": [], "source": [ "d.a()" ] }, { "cell_type": "code", "execution_count": null, "id": "26927d79", "metadata": {}, "outputs": [], "source": [ "with param.exceptions_summarized():\n", " d.a = 5" ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 5 }