Skip to content

Commit 0acd212

Browse files
committed
support suffix in param_scope
Signed-off-by: reiase <[email protected]>
1 parent 7436931 commit 0acd212

File tree

1 file changed

+40
-74
lines changed

1 file changed

+40
-74
lines changed

hyperparameter/hyperparameter.py

Lines changed: 40 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,17 @@ def all_params():
6868
class _Accessor(dict):
6969
"""Helper for accessing hyper-parameters."""
7070

71-
def __init__(self, root, path=None, suffix=None):
71+
def __init__(self, root, path=None, scope=None):
7272
super().__init__()
7373
self._root = root
7474
self._path = path
75-
self._suffix = suffix
75+
self._scope = scope
7676

7777
def get_or_else(self, default: Any = None):
7878
"""Get value for the parameter, or get default value if the parameter is not defined."""
79-
if self._suffix is not None:
80-
suffixes = self._suffix.replace(".", "#").split("#")
79+
if self._scope is not None:
80+
suffixes = self._scope.replace(".", "#").split("#")
81+
_tracker.read(f"{self._path}@{self._scope}")
8182
while suffixes:
8283
suffix = "#".join(suffixes)
8384
full_name = f"{self._path}@{suffix}"
@@ -92,22 +93,22 @@ def get_or_else(self, default: Any = None):
9293

9394
def __getattr__(self, name: str) -> Any:
9495
# _path and _root are not allowed as keys for user.
95-
if name in ["_path", "_root", "_suffix"]:
96+
if name in ["_path", "_root", "_scope"]:
9697
return self[name]
9798
return _Accessor(
9899
self._root,
99100
f"{self._path}.{name}" if self._path else name,
100-
suffix=self._suffix,
101+
scope=self._scope,
101102
)
102103

103104
def __setattr__(self, name: str, value: Any):
104105
# _path and _root are not allowed as keys for user.
105-
if name in ["_path", "_root", "_suffix"]:
106+
if name in ["_path", "_root", "_scope"]:
106107
return self.__setitem__(name, value)
107108
full_name = f"{self._path}.{name}" if self._path is not None else name
108109
full_name = (
109-
f"{full_name}@{self._suffix.replace('.', '#')}"
110-
if self._suffix is not None
110+
f"{full_name}@{self._scope.replace('.', '#')}"
111+
if self._scope is not None
111112
else full_name
112113
)
113114
_tracker.write(full_name)
@@ -175,7 +176,7 @@ def dynamic_dispatch(func, name=None, index=None):
175176

176177

177178
class HyperParameter(dict):
178-
"""HyperParameter is an extended dict with features for better parameter management.
179+
"""HyperParameter is an extended dict designed for parameter storage.
179180
180181
**create and access hyper-parameters**
181182
======================================
@@ -242,15 +243,17 @@ def put(self, name: str, value: Any) -> None:
242243
243244
Examples
244245
--------
246+
247+
1. put parameter with simple name
245248
>>> cfg = HyperParameter()
246249
>>> cfg.put('param1', 1)
247-
>>> cfg.put('obj1.propA', 'A')
248-
249-
>>> cfg.param1
250-
1
250+
>>> cfg
251+
{'param1': 1}
251252
252-
>>> cfg.obj1.propA
253-
'A'
253+
2. put parameter with object-style name
254+
>>> cfg.put('obj1.propA', 'A')
255+
>>> cfg
256+
{'param1': 1, 'obj1': {'propA': 'A'}}
254257
"""
255258

256259
path = name.split(".")
@@ -307,60 +310,24 @@ def __setitem__(self, key: str, value: Any) -> None:
307310
return dict.__setitem__(self, key, value)
308311

309312
def __getattr__(self, name: str) -> Any:
310-
"""read parameter with object-style api
311-
312-
Parameters
313-
----------
314-
name : str
315-
parameter name
316-
317-
Returns
318-
-------
319-
Any
320-
parameter value
321-
322-
Examples
323-
--------
324-
for simple parameters:
325-
>>> hp = HyperParameter(a=1, b = {'c':2, 'd': 3})
326-
>>> hp.a
327-
1
328-
329-
for nested parameters:
330-
>>> hp.b.c
331-
2
332-
333-
>>> getattr(hp, 'b.c')
334-
2
335-
"""
336313
return self.get(name)
337314

338315
def __setattr__(self, name: str, value: Any) -> None:
339-
"""create/update parameter with object-style api
340-
341-
Parameters
342-
----------
343-
name : str
344-
parameter name
345-
value : Any
346-
parameter value
316+
self.put(name, value)
347317

348-
Examples
349-
--------
350-
>>> hp = HyperParameter(a=1, b = {'c':2, 'd': 3})
351-
>>> hp.e = 4
318+
def __call__(self, scope=None) -> Any:
319+
"""Return a parameter accessor.
352320
353-
>>> hp['e']
354-
4
321+
This is the suggested method for accessing parameter, it supports the following features:
355322
356-
>>> setattr(hp, 'A.B.C', 1)
357-
>>> hp.A.B.C
358-
1
359-
"""
360-
self.put(name, value)
323+
1. default value for undefined parameter;
324+
2. parameter access with named scope;
325+
3. access tracking that records which parameter is accessed;
361326
362-
def __call__(self, suffix=None) -> Any:
363-
"""Return a parameter accessor.
327+
Parameters
328+
----------
329+
scope : str
330+
scope name, in which the accessor looks for the parameters
364331
365332
Returns
366333
-------
@@ -380,20 +347,19 @@ def __call__(self, suffix=None) -> Any:
380347
>>> cfg().b.undefined('default')
381348
'default'
382349
383-
2. hyper-parameter with suffix
384-
>>> cfg(suffix="ns").a("undefined")
350+
2. hyper-parameter with scope
351+
>>> cfg(scope="ns").a("undefined")
385352
1
386353
387-
>>> cfg(suffix="ns").a = 4
354+
>>> cfg(scope="ns").a = 4
388355
>>> cfg
389356
{'a': 1, 'b': {'c': 2, 'd': 3}, 'a@ns': 4}
390357
391358
>>> cfg("ns.ns").a()
392359
4
393360
"""
394361

395-
suffix = dict.get(self, "#suffix#", None) if suffix is None else suffix
396-
return _Accessor(self, suffix=suffix)
362+
return _Accessor(self, scope=scope)
397363

398364

399365
class _param_scope(HyperParameter):
@@ -452,7 +418,7 @@ def __enter__(self):
452418
def __exit__(self, exc_type, exc_value, traceback):
453419
_param_scope.tls.history.pop()
454420

455-
def __call__(self, suffix=None) -> Any:
421+
def __call__(self, scope=None) -> Any:
456422
"""
457423
>>> @auto_param('myns.foo.params')
458424
... def foo(a, b=2, c='c', d=None):
@@ -476,8 +442,8 @@ def __call__(self, suffix=None) -> Any:
476442
ps = {'myns': {'foo': {'params': {'b@sec1#sec2': 3}}}}
477443
1 3 c None
478444
"""
479-
suffix = dict.get(self, "_suffix", None) if suffix is None else suffix
480-
return _Accessor(self, suffix=suffix)
445+
scope = dict.get(self, "_scope", None) if scope is None else scope
446+
return _Accessor(self, scope=scope)
481447

482448
@staticmethod
483449
def current():
@@ -501,10 +467,10 @@ def __init__(self, index=None):
501467
def __call__(self, *args, **kwargs):
502468
retval = _param_scope(*args, **kwargs)
503469
if self._index is not None:
504-
if dict.get(retval, "_suffix", None) is not None:
505-
retval._suffix = f"{retval._suffix}#{self._index}"
470+
if dict.get(retval, "_scope", None) is not None:
471+
retval._scope = f"{retval._scope}#{self._index}"
506472
else:
507-
retval._suffix = self._index
473+
retval._scope = self._index
508474
return retval
509475

510476
def __getitem__(self, index):

0 commit comments

Comments
 (0)