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

Use sentinel value for inheriting parameter slots #605

Merged
merged 51 commits into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
7f3bc09
Replace None with _Undefined in constructors, and move validation to …
jbednar Feb 11, 2022
8225ae4
Force TimeSampledFn to have a None default
jbednar Feb 11, 2022
6aca386
Reverted numbergen change
jbednar Feb 12, 2022
98cc7c8
Changed validation to accept _Undefined instead of being deferred
jbednar Feb 12, 2022
df0c9f7
Minor cleanup
jbednar Feb 12, 2022
c776609
Renamed _Undefined to Undefined
jbednar Feb 12, 2022
ce9b249
Replaced un() with _slot_defaults + getattribute
jbednar Feb 16, 2022
20215a4
Added support for callable default values
jbednar Mar 16, 2022
4633477
Merge branch 'main' into undefined
maximlt Feb 20, 2023
0ad7368
make dict_update private
maximlt Feb 20, 2023
5978e38
remove unused and broken check
maximlt Feb 20, 2023
8d5b095
set slot defaults for String
maximlt Feb 21, 2023
86e8d85
move _dict_update to parameterized
maximlt Feb 21, 2023
cd2537a
add _slots_defaults to Bytes
maximlt Feb 21, 2023
73a2d4b
update slot defaults of the direct super class
maximlt Feb 21, 2023
42266e7
update the docstring to refer to Undefined
maximlt Feb 21, 2023
208356f
update Bytes parameter to use the sentinel
maximlt Feb 21, 2023
9e95d28
make allow_None_default private
maximlt Feb 21, 2023
3d87987
Merge branch 'main' into undefined
maximlt Mar 12, 2023
cc6b1da
refactor setting allow_None
maximlt Mar 14, 2023
107df6f
refactor setting instantiate
maximlt Mar 14, 2023
5f28ac5
Merge branch 'main' into undefined
maximlt Mar 14, 2023
538e826
clean up
maximlt Mar 15, 2023
2379afb
fix comparison with Undefined
maximlt Mar 15, 2023
2298f03
undo FileSelector changes
maximlt Mar 15, 2023
8b5a5e0
re-set signature of Bytes
maximlt Mar 15, 2023
8fb5f09
all slots must have a default value
maximlt Mar 15, 2023
1077f2c
Merge branch 'main' into undefined
maximlt Mar 16, 2023
dd634a6
dynamic update of Tuple length
maximlt Mar 16, 2023
f765b56
define Selector default values
maximlt Mar 16, 2023
88f9a9d
define List default values
maximlt Mar 16, 2023
d1c44aa
fix List inheritance
maximlt Mar 16, 2023
15c3cd6
fix comments
maximlt Mar 16, 2023
1211088
update Number behavior test
maximlt Mar 16, 2023
3d9f6fd
fix Selector behavior and adjust tests
maximlt Mar 16, 2023
854a8cb
adjust tuple tests
maximlt Mar 16, 2023
59843b2
adjust parameterized tests
maximlt Mar 16, 2023
5175ad0
Merge branch 'main' into undefined
maximlt Mar 17, 2023
b522d4a
set Tuple length when default is provided
maximlt Mar 17, 2023
953edb4
raise when Undefined is compared
maximlt Mar 17, 2023
576e079
raise better error when a slot has no default value
maximlt Mar 17, 2023
c021efe
Apply suggestions from code review
jbednar Mar 25, 2023
58c688c
rename to _compute_length_of_default
maximlt Apr 4, 2023
f294b57
apply suggestion and rename
maximlt Apr 4, 2023
03b9f4c
rename selector default function
maximlt Apr 4, 2023
8a3e4f2
Merge branch 'main' into undefined
maximlt Apr 4, 2023
81cd1b4
validate the default attribute of Boolean
maximlt Apr 4, 2023
68b6e01
add __repr__ to Undefined
maximlt Apr 11, 2023
2e19f34
improve docs on inheritance
maximlt Apr 11, 2023
9e3f2a9
add docstring to _compute_selector_default
maximlt Apr 11, 2023
0ca0042
add comment on empty_default
maximlt Apr 11, 2023
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
172 changes: 124 additions & 48 deletions examples/user_guide/Parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"import param\n",
"from param import Parameter, Parameterized\n",
"\n",
"p = Parameter(42, doc=\"The answer\", constant=True)\n",
"p = Parameter(default=42, doc=\"The answer\", constant=True)\n",
"p.default"
]
},
Expand Down Expand Up @@ -87,8 +87,8 @@
"source": [
"class A(Parameterized):\n",
" question = Parameter(\"What is it?\", doc=\"The question\")\n",
" answer = Parameter(2, constant=True, doc=\"The answer\")\n",
" ultimate_answer = Parameter(42, readonly=True, doc=\"The real answer\")\n",
" answer = Parameter(default=2, constant=True, doc=\"The answer\")\n",
" ultimate_answer = Parameter(default=42, readonly=True, doc=\"The real answer\")\n",
"\n",
"a = A(question=\"How is it?\", answer=\"6\")"
]
Expand Down Expand Up @@ -166,15 +166,24 @@
"metadata": {},
"outputs": [],
"source": [
"b=A()\n",
"b = A()\n",
"b.answer"
]
},
{
"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 special `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, via attribute or key:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"a.param['question']"
]
},
{
Expand Down Expand Up @@ -227,8 +236,8 @@
"outputs": [],
"source": [
"with param.exceptions_summarized():\n",
" a.question=True\n",
" a.answer=5"
" a.question = True\n",
" a.answer = 5"
]
},
{
Expand All @@ -254,7 +263,7 @@
"outputs": [],
"source": [
"with param.edit_constant(a):\n",
" a.answer=30\n",
" a.answer = 30\n",
"a.answer"
]
},
Expand All @@ -269,9 +278,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parameter inheritance and instantiation\n",
"## Parameter inheritance\n",
"\n",
"Much of the parameter metadata is there to help you control whether and how the parameter value is instantiated on Parameterized objects as they are created or new Parameterized subclasses as they are defined. Depending on how you want to use that Parameter and what values it might take, controlling instantiation can be very important when mutable values are involved. First, let's look at the default behavior, which is appropriate for immutable attributes:"
"`Parameter` objects and their metadata are inherited in a hierarchy of `Parameterized` objects. Let's see how that works:"
]
},
{
Expand All @@ -280,8 +289,13 @@
"metadata": {},
"outputs": [],
"source": [
"class A(Parameterized):\n",
" question = Parameter(\"What is it?\", doc=\"The question\")\n",
" answer = Parameter(default=2, constant=True, doc=\"The answer\")\n",
" ultimate_answer = Parameter(default=42, readonly=True, doc=\"The real answer\")\n",
"\n",
"class B(A):\n",
" ultimate_answer = Parameter(84, readonly=True)\n",
" ultimate_answer = Parameter(default=84)\n",
"\n",
"b = B()\n",
"b.question"
Expand All @@ -293,7 +307,7 @@
"metadata": {},
"outputs": [],
"source": [
"A.question=\"How are you?\""
"A.question = \"How are you?\""
]
},
{
Expand All @@ -309,7 +323,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Here you can see that B inherits the `question` parameter from A, and as long as `question` has not been set explicitly on `b`, `b.question` will report the value from where that Parameter was defined, i.e. A in this case. If `question` is subsequently set on `b`, `b.question` will no longer be affected by the value in `A`:"
"Here you can see that B inherits `question` from A, and as long as `question` has not been set explicitly on `b`, `b.question` will report the value from where that Parameter was defined, i.e. A in this case. If `question` is subsequently set on `b`, `b.question` will no longer be affected by the value in `A`:"
]
},
{
Expand All @@ -318,16 +332,76 @@
"metadata": {},
"outputs": [],
"source": [
"b.question=\"Why?\"\n",
"A.question=\"Who?\"\n",
"b.question = \"Why?\"\n",
"A.question = \"Who?\"\n",
"b.question"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, parameters not specified in B are still fully usable in it, if they were declared in a superclass. Metadata associated with that parameter is also inherited if not explicitly overidden in `B`. E.g. `help(b)` or `help(B)` will list all parameters:"
"As you can see, parameters not specified in B are still fully usable in it, if they were declared in a superclass. **Metadata associated with that parameter is also inherited**, if not explicitly overidden in `B`.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"b.param.ultimate_answer.constant"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"b.param.ultimate_answer.readonly"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"b.ultimate_answer"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"b.param.ultimate_answer.default"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"b.param.ultimate_answer.doc"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Looking at the metadata values of `ultimate_answer` on `b` or `B` you can see that:\n",
"\n",
"- All the default metadata values like `constant`, `allow_none`, ..., were inherited from the base `Parameter` object provided by Param\n",
"- The `read_only` and `doc` metadata values were inherited from `A`\n",
"- The `default` metadata value of `ultimate_answer` in `B` overrode the value provided in `A`.\n",
"\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",
"`help(b)` or `help(B)` will list all parameters:"
]
},
{
Expand All @@ -341,9 +415,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"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",
"## Parameter value instantiation\n",
"\n",
"However, what happens if the value (unlike Python strings) is mutable? Things get a lot more complex:"
"So much of the parameter metadata is there to help you control whether and how the parameter value is instantiated on Parameterized objects as they are created or new Parameterized subclasses as they are defined. Depending on how you want to use that Parameter and what values it might take, controlling instantiation can be very important when mutable values are involved. While the default behavior shown above is appropriate for **immutable attributes**, what happens if the value (unlike Python strings) is mutable? Things get a lot more complex."
]
},
{
Expand All @@ -352,11 +426,11 @@
"metadata": {},
"outputs": [],
"source": [
"s = [1,2,3]\n",
"s = [1, 2, 3]\n",
"\n",
"class C(Parameterized):\n",
" s1 = param.Parameter(s, doc=\"A sequence\")\n",
" s2 = param.Parameter(s, doc=\"Another sequence\")\n",
" s1 = Parameter(s, doc=\"A sequence\")\n",
" s2 = Parameter(s, doc=\"Another sequence\")\n",
"\n",
"c = C()"
]
Expand All @@ -365,7 +439,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Here, both parameters `s1` and `s2` effectively point to the same underlying sequence `s`:"
"Here, both parameters `s1` and `s2`, on both `A` and `B` effectively point to the same underlying sequence `s`:"
]
},
{
Expand All @@ -383,7 +457,7 @@
"metadata": {},
"outputs": [],
"source": [
"s[1]*=5"
"s[1] *= 5"
]
},
{
Expand All @@ -410,7 +484,7 @@
"metadata": {},
"outputs": [],
"source": [
"c.s1[2]='a'"
"c.s1[2] = 'a'"
]
},
{
Expand All @@ -435,7 +509,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, there is only one actual sequence here, and `s`, `s1`, and `s2` all point to it. In some cases such behavior is desirable, e.g. if the mutable object is a specific global list (e.g. a set of search paths) with a unique identity and all of the parameters are meant to point to that specific item. In other cases, it's the contents of the mutable item that are important, and no sharing of contents is intended. Luckily, Param supports that case as well, if you provide `instantiate=True`:"
"As you can see, there is only one actual sequence here, and `s`, `s1`, and `s2` all point to it. In some cases such behavior is desirable, e.g. if the mutable object is a specific global list (e.g. a set of search paths) with a unique identity and all of the parameters are meant to point to that specific item. In other cases, it's the contents of the mutable item that are important, and no sharing of contents is intended. Luckily, Param supports that case as well, if you provide `instantiate=True` (default is `False`):"
]
},
{
Expand All @@ -447,8 +521,8 @@
"s = [1,2,3]\n",
"\n",
"class D(Parameterized):\n",
" s1 = Parameter(s, doc=\"A sequence\", instantiate=True)\n",
" s2 = Parameter(s, doc=\"Another sequence\", instantiate=True)\n",
" s1 = Parameter(default=s, doc=\"A sequence\", instantiate=True)\n",
" s2 = Parameter(default=s, doc=\"Another sequence\", instantiate=True)\n",
"\n",
"d = D()"
]
Expand All @@ -475,7 +549,7 @@
"metadata": {},
"outputs": [],
"source": [
"s*=2"
"s *= 2"
]
},
{
Expand All @@ -502,7 +576,7 @@
"metadata": {},
"outputs": [],
"source": [
"d.s1[2]='a'"
"d.s1[2] = 'a'"
]
},
{
Expand All @@ -525,9 +599,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parameter metadata inheritance and instantiation\n",
"## Parameter object 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 All @@ -538,8 +612,8 @@
"source": [
"d1 = D()\n",
"d2 = D()\n",
"d1.param.s1.label=\"sequence 1\"\n",
"d2.param.s1.label=\"Sequence 1\"\n",
"d1.param.s1.label = \"sequence 1\"\n",
"d2.param.s1.label = \"(sequence 1)\"\n",
"d2.param.s1.label"
]
},
Expand All @@ -566,11 +640,11 @@
"outputs": [],
"source": [
"class E(Parameterized):\n",
" a = param.Parameter(3.14, label=\"pi\", per_instance=False)\n",
" a = Parameter(default=3.14, label=\"pi\", per_instance=False)\n",
"\n",
"e1 = E()\n",
"e2 = E()\n",
"e2.param.a.label=\"Pie\"\n",
"e2.param.a.label = \"Pie\"\n",
"e1.param.a.label"
]
},
Expand All @@ -592,10 +666,10 @@
"outputs": [],
"source": [
"class S(param.Parameterized):\n",
" l = param.Parameter([1,2,3], instantiate=True)\n",
" l = Parameter(default=[1,2,3], instantiate=True)\n",
"\n",
"ss = [S() for i in range(10)]\n",
"ss[0].l[2]=5\n",
"ss[0].l[2] = 5\n",
"ss[1].l"
]
},
Expand All @@ -617,7 +691,7 @@
"with param.shared_parameters():\n",
" ps = [S() for i in range(10)]\n",
" \n",
"ps[0].l[2]=5\n",
"ps[0].l[2] = 5\n",
"ps[1].l"
]
},
Expand Down Expand Up @@ -650,13 +724,13 @@
"import param\n",
"\n",
"class Q(param.Parameterized):\n",
" a = param.Number(39, bounds=(0,50))\n",
" b = param.String(\"str\")\n",
" a = param.Number(default=39, bounds=(0,50))\n",
" b = param.String(default=\"str\")\n",
"\n",
"class P(Q):\n",
" c = param.ClassSelector(Q, Q())\n",
" e = param.ClassSelector(param.Parameterized, param.Parameterized())\n",
" f = param.Range((0,1))\n",
" c = param.ClassSelector(default=Q(), class_=Q)\n",
" e = param.ClassSelector(default=param.Parameterized(), class_=param.Parameterized)\n",
" f = param.Range(default=(0,1))\n",
"\n",
"p = P(f=(2,3), c=P(f=(42,43)), name=\"demo\")"
]
Expand Down Expand Up @@ -745,14 +819,16 @@
" \"\"\"Integer Parameter that must be even\"\"\"\n",
"\n",
" def _validate_value(self, val, allow_None):\n",
" super(EvenInteger, self)._validate_value(val, allow_None)\n",
" super()._validate_value(val, allow_None)\n",
" if not isinstance(val, numbers.Number):\n",
" raise ValueError(\"EvenInteger parameter %r must be a number, \"\n",
" \"not %r.\" % (self.name, val))\n",
" raise ValueError(\n",
" f\"EvenInteger parameter {self.name!r} must be a number, not {val!r}.\"\n",
" )\n",
" \n",
" if not (val % 2 == 0):\n",
" raise ValueError(\"EvenInteger parameter %r must be even, \"\n",
" \"not %r.\" % (self.name, val))\n",
" raise ValueError(\n",
" f\"EvenInteger parameter {self.name!r} must be even, not {val!r}.\"\n",
" )\n",
"\n",
"class P(param.Parameterized):\n",
" n = param.Number()\n",
Expand Down
Loading