Close
Community Post

Python Tips'N'Tricks #5

 

Cache

Sometimes, you have some heavy calculations done in a function, like using a getattr method or doing a deep-search for operators. If you know that the result of this function will not change after the initital call, you might be tempted to store the result in a global variable and call it a day. Instead, python has a built-in way of doing that for you, using the functools lru_cache module.

For example, when using the following function

def find_panelcomps( target_comp:COMP ):
	return target_comp.findChildren( type = panelCOMP )

it will always run the full findChildren method of the target_comp. In larger networks, this can get expensive. As long as you know the result will not change, you can easily use the lru_cache.

from functools import lru_cache
 
@lru_cache(maxsize=None)
def find_panelcomps( target_comp:COMP ):
	return target_comp.findChildren( type = panelCOMP )

Now, it will store the result based on the arguments given, and return them from storage instead of running the result again, saving precious time.

Python Documentation

Parameter

To get access to a parameter of an operator via a string, you can use the getattr method shown in PTAT#1, but this is much slower then a somewhat new method of accessing the value as a dictionary. Simply write

parameter_variable = op("my_operator").par[ "my_parameter_name" ]

To get the current value of a parameter, always use eval(), instead of just the parameter or the .val member.

val will only return the static value of a parameter, ignoring elements like expression or export. It also more or less ignores the type of the parameter. OP-Parameters store their value for example as string. Imagine the following situation:

Take a moment to think how you would access the values and what you would expect.

debug( op("base1").par.Firstop.val )        #String "constant1"
debug( op("base1").par.Firstop.eval() )     #Operaotr Object op("constant1")
debug( op("base1").par.Firstop )            #Parameter Object
 
debug( op("base1").par.Secondop.val )       #String constant2
debug( op("base1").par.Secondop.eval() )    #None Object
debug( op("base1").par.Secondop )           #Parameter Object
 
debug( op("base1").par.Float.val )          #Float 0.26
debug( op("base1").par.Float.eval() )       #Float 1377.3

 

functions are objects

Shocker! But functions and methods themself are indeed also objects in python. Why is this important? Well, this means you can store and even pass around refferences to methods and call them later.

With a lambda-definition, you can even create new functions on the fly to pass around. I think the most important part where this can come in super handy without getting to deep in to more complex python structures is the run-module.

Imagine this silly example code, deleting a target operator with a delay of on frame (the string-composition gets even more complicated in more complex systems).

def delete_operator( op ):
	op.destroy()
 
run(
	f"me.module.delete_operator(args[0])", 
	op("opferlamm"), 
	delayFrames = 1,
	fromOP = me )

And now look at the following code, doing exactly the same:
 

def delete_operator( op ):
	op.destroy()
 
run("args[0]()", 
	lambda : delete_operator( op("opferlamm") ), 
	delayFrames = 1, )

 

Methods can be dependable

Thats a quick one. If a function or method is depending on a dependable object (Parameter, Cell, tdu.Dependencie etc), this function will also becomde dependable and force a reevaluation.

We add the following method to our extension:

	@property
	def Full_Name(self):
		return f"{self.ownerComp.par.Firstname.eval()} {self.ownerComp.par.Lastname.eval()}"

No, when refferencing Full_Name of our object, it will update in realtime! Awsome!

 

Comments