'getPar' python function, or similar to improve consistency of using .par collection

Many times I’ve faced the issue of wanting to check if a parameter exists, only for .par.Parname to be treated as an evaluation of the parameter, and forced do use hasattr.

See this example that best explains the problem, with comments:

base = op('base1') # has a Toggle CustomPar

if _toggle := base.par['Toggle']:
	'''
	runs when Toggle is On
	And is confusing, since suddenly it acts as a Par object
	While before (when Toggle was Off) it did not
	'''
	debug(f'Case #1: {_toggle.name} = {_toggle.eval()}')
	


if _toggle := getattr(base.par, 'Toggle'):
	'''
	Same case as above, however this is a very explicit way of retreiving an object,
	so should behave that way
	'''
	debug(f'Case #2 {_toggle.name} = {_toggle.eval()}')


# forced to use hasattr to check if the object exists
if hasattr(base.par, 'Toggle'):
	'''
	Runs when Toggle is On
	'''
	debug(f'Case #3: {base.par.Toggle.name} = {base.par.Toggle.eval()}')
else:
	debug(f'Case #3: Toggle does not exist')

I understand the design choice for ease of access of parameter values, however even in the documentation this is not explained in which situations this occurs, and is pretty vague about it:

Often using a parameter object will work, but occasionally you will run into this problem and need to convert from a parameter to a value.

My suggestion, similar how we can force the parameter to be evaluated with .eval() to implement a similar function that explicitly retreives the Par Object. At minimum getattr should do that.

The parameter object is explicitly being retrieved in both cases. I think the issue is Par Objects have various implicit casts to different types, so once it’s retrieved, it’s then being asked to cast to something else such as a bool.
You likely have to be a bit more explicit here, and retrieve the Par object on the line above, then check if it’s None, to avoid the implicit cast away from a Par object before you are ready.

Thanks malcolm I understand, however there is an argument to be made to avoid the multi-line explicit way to a single line explicit way.

Agreed, but since the implicit conversions from Par to other types lead to simpler code is many more places (such as in parameter expressions) it feels like this tradeoff is fair. What you are asking for is what the functions already do though, so it’s possible that the expression can be adjusted somehow to take into account that bool(Par) is going to return the Toggle value of the par, not if it’s None or not.

1 Like

The strange thing (but to late to change, I made another post about that somewhere else) is that using getattr or the [ name ] notation will return None if the element is not found (instead of raising a Key-Exception).
So what you can do instead here is using is None

if op("displace1").par["foo"] is not None:
    do_stuff()
1 Like

I guess the 3 state version on one line looks like this:

print('None') if base.par['Toggle'] is None else print('True') if base.par.Toggle else print('False')

Or if you prefer:

(print('True') if base.par.Toggle else print('False')) if base.par['Toggle'] is not None else print('None')