Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed description of shared_parameters #568

Merged
merged 3 commits into from
Nov 10, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions examples/user_guide/Parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
"- **allow_None**: Whether this parameter accepts None as an allowed value, in addition to whatever other types it accepts. Automatically set to True if the default value of this Parameter is None.\n",
"- **doc**: Docstring describing this parameter, which can be used by automatic documentation systems.\n",
"- **constant**: Parameter whose value can only be changed at the class level or in a Parameterized constructor. Once the Parameterized instance has been created, the value is constant except in the context of `with param.edit_constant(obj)` (see below).\n",
"- **readonly**: Parameter whose value cannot be set by a user either on an instance or at the class level. Can still be changed inside a codebase by temporarily overriding this value, e.g. to report internal state\n",
"- **instantiate**: Whether to deepcopy the default value into a Parameterized instance when it is created. \n",
"- **per_instance**: whether a separate Parameter instance will be created for every Parameterized instance created.\n",
"- **readonly**: Parameter whose value cannot be set by a user either on an instance or at the class level. Can still be changed inside a codebase by temporarily overriding this value, e.g. to report internal state.\n",
"- **instantiate**: Whether to deepcopy the default value into a Parameterized instance when it is created. False by default for Parameter and most of its subtypes, but some Parameter types commonly used with mutable containers default to `instantiate=True` to avoid interaction between separate Parameterized instances, and users can control this when declaring the Parameter (see below). \n",
"- **per_instance**: whether a separate Parameter instance will be created for every Parameterized instance created. Similar to `instantiate`, but applies to the Parameter object rather than to its value.\n",
"- **precedence**: Optional numeric value controlling whether this parameter is visible in a listing and if so in what order.\n",
"\n",
"Most of these settings (apart from **name**) are accepted as keyword arguments to the Parameter's constructor, with `default` also accepted as a positional argument:"
Expand Down Expand Up @@ -97,7 +97,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Here, we created a Parameterized class `A`, with parameters `question` and `answer`, each with default values. We then instantiated a Python object `a` of type `A`. Without having to write a constructor for `A`, we were able to provide our own values for `question` and `answer`, while inheriting the default value of `ultimate_answer`. This approach gives a lot of (but not too much!) configurability to the user of this class, without much effort by the class author. Any values we provide at instantiation need to be allowed by the `Parameter` declaration; e.g. here we could not provide a value for `ultimate_answer` when declaring `a`, because that parameter is read only."
"Here, we created a Parameterized class `A`, with parameters `question` and `answer`, each with default values. We then instantiated a Python object `a` of type `A`. Without having to write a constructor for `A`, we were able to provide our own values for `question` and `answer`, while inheriting the default value of `ultimate_answer`. This approach gives a lot of (but not too much!) configurability to the user of this class, without much effort by the class author. Any values we provide at instantiation need to be allowed by the `Parameter` declaration; e.g. here we could not provide a value for `ultimate_answer` when declaring `a`, because that parameter is declared read only:"
]
},
{
Expand Down Expand Up @@ -174,7 +174,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"If accessing the attribute always gives us a value whether on the instance or the class, what happened to the `Parameter` objects? They are stored on the Parameterized instance or class, and are accessible via a `param` accessor object at either the instance or class levels:"
"If accessing the attribute always gives us a value whether on the instance or the class, what happened to the `Parameter` objects? They are stored on the Parameterized instance or class, and are accessible via a special `param` accessor object at either the instance or class levels:"
]
},
{
Expand Down Expand Up @@ -244,7 +244,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that if you do need to change the value of a constant parameter (typically inside of your Parameterized object's own code), you can do so using the `param.edit_constant` context manager:"
"Note that if for some reason you do need to change the value of a constant parameter (typically inside of your Parameterized object's own code), you can do so using the `param.edit_constant` context manager:"
]
},
{
Expand Down Expand Up @@ -341,7 +341,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Parameter inheritance like this lets you (a) define a Parameter only once, no matter how many subclasses it might be used in, and (b) control the value of that parameter conveniently across the entire set of subclasses and instances, as long as that attribute has not been set on those objects already. Using inheritance in this way is a very convenient mechanism for setting default values and other \"global\" parameters, whether before a program starts executing or during it.\n",
"Parameter inheritance like this lets you (a) use a parameter in many subclasses without having to define it more than once, and (b) control the value of that parameter conveniently across the entire set of subclasses and instances, as long as that attribute has not been set on those objects already. Using inheritance in this way is a very convenient mechanism for setting default values and other \"global\" parameters, whether before a program starts executing or during it.\n",
"\n",
"However, what happens if the value (unlike Python strings) is mutable? Things get a lot more complex:"
]
Expand Down Expand Up @@ -527,7 +527,7 @@
"source": [
"## Parameter metadata inheritance and instantiation\n",
"\n",
"`instantiate` controls how parameter values behave, but similar issues arise for Parameter objects, which offer similar control via the `per_instance` metadata declaration. `per_instance` (True by default) provides a logically distinct Parameter object for every Parameterized instance, allowing each such instance to have different metadata for that parameter. For example, we can set the label separately for each instance without clobbering each other:"
"`instantiate` controls how parameter _values_ behave, but similar issues arise for Parameter _objects_, which offer similar control via the `per_instance` metadata declaration. `per_instance` (True by default) provides a logically distinct Parameter object for every Parameterized instance, allowing each such instance to have different metadata for that parameter. For example, we can set the label separately for each instance without clobbering each other:"
]
},
{
Expand Down Expand Up @@ -556,7 +556,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This capability is useful for situations with dynamically updated metadata, e.g. if you need setting one parameter's value (e.g. 'Continent') to change the allowed values of another parameter (e.g. 'Country'). The underlying Parameter objects are copied lazily (only when actually changed), so that objects are not actually multiplied unless necessary. If you do want parameters to share a single Parameter object so that you can control its behavior globally, you can achieve that with `per_instance=False`, though the effects are confusing in the same way as `instantiate=True` for mutable objects (above):"
"This capability is useful for situations with dynamically updated metadata, e.g. if you need setting one parameter's value (e.g. 'Continent') to change the allowed values of another parameter (e.g. 'Country'). The underlying Parameter objects are copied lazily (only when actually changed), so that objects are not actually multiplied unless necessary. If you do want parameters to share a single Parameter object so that you can control its behavior globally, you can achieve that with `per_instance=False`, though the effects can be confusing in the same way as `instantiate=True` for mutable objects (above):"
]
},
{
Expand All @@ -580,9 +580,9 @@
"source": [
"## Instantiating with shared parameters\n",
"\n",
"When creating a large collection of Parameterized objects of the same type, the overhead of having separate parameters for each object can be significant. If you want, you can create the objects to share parameter instances for efficiency, and also so that you can easily change a value on all such objects at the same time. \n",
"When creating a large collection of Parameterized objects of the same type, the overhead of having separate parameters for each object can be significant. If you want, you can create the objects to share parameter values for efficiency, and also so that you can easily change a value on all such objects at the same time. \n",
"\n",
"As an example, the default behavior of a set of objects will be to have independent parameter values, such that changing one of them will not affect the others:"
"As an example, let's say you've defined a Parameter value to be independent, such that changing one instance's value will not affect the others:"
]
},
{
Expand All @@ -592,7 +592,7 @@
"outputs": [],
"source": [
"class S(param.Parameterized):\n",
" l = param.Parameter([1,2,3])\n",
" l = param.Parameter([1,2,3], instantiate=True)\n",
"\n",
"ss = [S() for i in range(10)]\n",
"ss[0].l[2]=5\n",
Expand All @@ -603,7 +603,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"If you use the context manager `shared_parameters`, any Parameterized objects created within that context will share parameter instances, such that changing one of them will affect all of them:"
"Here changing the value of `l` on `ss[0]` doesn't affect `ss[1]` or any other instances.\n",
"\n",
"What if you as a user of this class are creating a very large number of similar objects and actually do want them to share the same parameter value, either to save memory or to make it easy to change all of their values at once? In that case you can use the context manager `shared_parameters`, and any Parameterized objects created within that context will share parameter values, such that changing one of them will affect all of them:"
]
},
{
Expand All @@ -623,7 +625,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This approach can provide significant speedup and memory savings in certain cases."
"This approach can provide significant speedup and memory savings in certain cases, but should only be used for good reasons, since it can cause confusion for any code expecting instances to be independent as they have been declared."
]
},
{
Expand Down Expand Up @@ -728,7 +730,7 @@
"\n",
"As you can see above, a `Parameter` provides a lot of power already on its own, but in practice you will want to use much more specific parameter types that reject invalid inputs and keep your code clean and simple. A specialized Parameter acts as a \"contract\" with the users of the code you write, declaring and defending precisely what configuration is allowed and how to achieve it. If you need to accept specific inputs like that but don't add an appropriate Parameter type, you'll be stuck adding exceptions and validation code throughout your codebase, whereas anything you can express at the Parameter level will be enforced automatically without any further checks or code.\n",
"\n",
"For instance, what if you want to accept a numeric parameter, but (for some reason) can only accept numbers that are integer powers of 2? You'll need a custom Parameter class to express a restriction like that:"
"For instance, what if you want to accept a numeric parameter, but (for some reason) can only accept numbers that are integer powers of 2? You'll need a custom Parameter class to express a restriction like that. In this case you can do it by overriding the `_validate_value` method of the `Parameter` class:"
]
},
{
Expand Down