diff --git a/benchmarks/benchmarks/benchmarks.py b/benchmarks/benchmarks/benchmarks.py index 3619604fb..5700358b0 100644 --- a/benchmarks/benchmarks/benchmarks.py +++ b/benchmarks/benchmarks/benchmarks.py @@ -287,12 +287,32 @@ class P1(param.Parameterized): self.P1 = P1 self.p1 = P1() - def test_class(self): + def time_class(self): self.P1.param - def test_instance(self): + def time_instance(self): self.p1.param +class ParameterizedParamContainsSuite: + + def setup(self): + class P1(param.Parameterized): + x0 = param.Parameter() + x1 = param.Parameter() + x2 = param.Parameter() + x3 = param.Parameter() + x4 = param.Parameter() + x5 = param.Parameter() + + self.P1 = P1 + self.p1 = P1() + + def time_class(self): + 'x5' in self.P1.param + + def time_instance(self): + 'x5' in self.p1.param + class ParameterizedSetattrSuite: diff --git a/param/parameterized.py b/param/parameterized.py index dd8ecdd15..cc07a3826 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -1849,24 +1849,25 @@ def __getitem__(self_, key): Returns the class or instance parameter """ inst = self_.self - params = self_ if inst is None else inst.param - p = params.objects(False)[key] - return p if inst is None else _instantiated_parameter(inst, p) + if inst is None: + return self_._cls_parameters[key] + p = self_.objects(instance=False)[key] + return _instantiated_parameter(inst, p) def __dir__(self_): """ Adds parameters to dir """ - return super().__dir__() + list(self_) + return super().__dir__() + list(self_._cls_parameters) def __iter__(self_): """ Iterates over the parameters on this object. """ - yield from self_.objects(instance=False) + yield from self_._cls_parameters def __contains__(self_, param): - return param in list(self_) + return param in self_._cls_parameters def __getattr__(self_, attr): """ @@ -1876,12 +1877,7 @@ def __getattr__(self_, attr): if cls is None: # Class not initialized raise AttributeError - params = list(cls._param__private.params) - if not params: - params = [n for class_ in classlist(cls) for n, v in class_.__dict__.items() - if isinstance(v, Parameter)] - - if attr in params: + if attr in self_._cls_parameters: return self_.__getitem__(attr) elif self_.self is None: raise AttributeError(f"type object '{self_.cls.__name__}.param' has no attribute {attr!r}") @@ -1914,7 +1910,8 @@ def _setup_params(self_, **params): ## Deepcopy all 'instantiate=True' parameters params_to_deepcopy = {} params_to_ref = {} - for pname, p in self_.objects(instance=False).items(): + objects = self_._cls_parameters + for pname, p in objects.items(): if p.instantiate and pname != "name": params_to_deepcopy[pname] = p elif p.constant and pname != 'name': @@ -1927,7 +1924,6 @@ def _setup_params(self_, **params): ## keyword arg setting deps, refs = {}, {} - objects = self.param.objects(instance=False) for name, val in params.items(): desc = self_.cls.get_param_descriptor(name)[0] # pylint: disable-msg=E1101 if not desc: @@ -2235,8 +2231,8 @@ def add_parameter(self_, param_name, param_obj): # would need to handle the params() cache as well # (which is tricky but important for startup speed). cls = self_.cls - type.__setattr__(cls,param_name,param_obj) - ParameterizedMetaclass._initialize_parameter(cls,param_name,param_obj) + type.__setattr__(cls, param_name, param_obj) + ParameterizedMetaclass._initialize_parameter(cls, param_name, param_obj) # delete cached params() cls._param__private.params.clear() @@ -2352,6 +2348,31 @@ def set_param(self_, *args,**kwargs): (self_or_cls.name)) return self_.update(kwargs) + @property + def _cls_parameters(self_): + """ + Class parameters are cached because they are accessed often, + and parameters are rarely added (and cannot be deleted) + """ + cls = self_.cls + pdict = cls._param__private.params + if pdict: + return pdict + + paramdict = {} + for class_ in classlist(cls): + for name, val in class_.__dict__.items(): + if isinstance(val, Parameter): + paramdict[name] = val + + # We only want the cache to be visible to the cls on which + # params() is called, so we mangle the name ourselves at + # runtime (if we were to mangle it now, it would be + # _Parameterized.__params for all classes). + # cls._param__private.params[f'_{cls.__name__}__params'] = paramdict + cls._param__private.params = paramdict + return paramdict + def objects(self_, instance=True): """ Returns the Parameters of this instance or class @@ -2376,25 +2397,7 @@ def objects(self_, instance=True): stacklevel=2, ) - cls = self_.cls - # We cache the parameters because this method is called often, - # and parameters are rarely added (and cannot be deleted) - pdict = cls._param__private.params - if not pdict: - paramdict = {} - for class_ in classlist(cls): - for name, val in class_.__dict__.items(): - if isinstance(val, Parameter): - paramdict[name] = val - - # We only want the cache to be visible to the cls on which - # params() is called, so we mangle the name ourselves at - # runtime (if we were to mangle it now, it would be - # _Parameterized.__params for all classes). - # cls._param__private.params[f'_{cls.__name__}__params'] = paramdict - cls._param__private.params = paramdict - pdict = paramdict - + pdict = self_._cls_parameters if instance and self_.self is not None: if instance == 'existing': if getattr(self_.self._param__private, 'initialized', False) and self_.self._param__private.params: