[Experimental] Inconsistency of checking Par object in list of Par objects

Please see the snippet below and comments, checking a par object against a list of par objects:

if hasattr(_par, 'isSamePar'): # 2025.*; didn't need to use it before
	if any(_par.isSamePar(_otherPar) for _otherPar in _p.bindReferences):
		return True
elif hasattr(_par, 'isPar'): # Available in 2023.12480 but actually not needed, not available in 2023.11880 for example
	if any(_par.isPar(_otherPar) for _otherPar in _p.bindReferences):
		return True
else: # Any version below 2025.*, even if `isPar` is available
	if _par in _p.bindReferences:
		return True

Checking the live docs I don’t see a mention of in operations/comparisons, as well as it actually working without using isPar. The experimental docs has the same wording, but in is not actually working.

This being a major difference and inconsistency, as well as in comparisons being something that is widely used in “legacy” tools and in Pythonic thinking, I thought was worth a post.

EDIT: In 2025.* it seems to eval the _par implicitly when checking with in — in my opinion this is very un-intuitive.

1 Like

In general parameters seem to typecast when evaluated, which makes it impossible to find out if a variable contains the same parameter object (not value).

Also, the is seems to not work correctly either to compare instances.

The isPar docs unfortunately outline those operations and has been like that. I also fully disagree with the notion and general implications of this, but call me a Python purist.

In my opinion at least is, which is typically used for a deeper equivalency check of object memory, should do what it says on the Python box.

However the issue outlined in my original post goes one level deeper where op(‘base1’).par.Float in op(‘base1’).customPars would return False in any case, which feels so so wrong I really hope it’s a bug. Edit: probably will return True if the Float value is non-zero… so confusing

Hi.

Sorry for the confusion. It’s something we’ve battled with as well.

In 2025.30K experimental series, we decided to abandon the Par.isPar() function entirely and instead introduced: Par.isSamePar(par) and ParGroup.isSameParGroup(pargroup).
We found the Par.isPar naming was too vague and conflicted with similar sounding Par.isFloat, Par.isOP etc.

The reason the is and in operators don’t work as expected in 2025.30K is because they rely on the Parameter evaluation, not the object memory location.
We’ve explored the idea of making those python object memory locations persistent and consistent with their actual internal TouchDesigner parameter references, but it currently unwieldly.

A workaround for:
op(‘base1’).par.Float in op(‘base1’).customPars

might be:
op(‘base1’).par.Float.owner == op(‘base1’) and op(‘base1’).par.Float.isCustom()

I’ve updated the wiki with a bit more info, but we’re always open to more input of course.
Cheers,
Rob B

1 Like

Thanks for the clarification.

I think I’ve proposed something similar before, but would be great to have the ability to force the explicit Par object out of ambiguity similar to how we can explicitly invoke/denote the evaluation of it with .eval(); something like .parObj()

I have an idea how that might work, Dan. Will put an RFE in.

1 Like

On the other hand could we forgo this by introducing a new parObject “corePar” or “purePar” which simply does not Autocast but requires .eval() and .val to be accessed and set?
Just loud thinking :wink:

That would be the idea. One question is how do we want to get those objects? OP.parObj.name? Par.parObj()? To solve the original problem in this post, I think we’d also want an argument to OP.pars()that asks for the list to contain this new object type.

1 Like

I think since .par is already a parameter object, similarly how .eval() explicitly solves the implicit ambiguity, it makes sense to have the object retrieval as a method as well.

Otherwise you end up with a duplicate class in the hierarchy where the only difference is how it’s not evaluated “accidentally” in certain situations.

The problem is that you can’t currently resolve to a par object that doesn’t auto-cast, because that’s what a par object does!. There has to be either a setting for par objects or a separate kind of object that doesn’t auto-cast.

Oh I see :melting_face: