While making a function to manipulate the TypeVar
from the typing module, I came across a problem.
My function do work but I wanted to make it faster so I used the timeit module to test some different versions.
However the result aren't what I expected.
Clearing some potential interrogation :
check_type(object , type)
is a function meant to test if an object correspond to a type (quite time consuming currently).
isTypeVar(type)
test if type is a TypeVar.
NullType
is a type that cannot describe an object so when it's tested with check_type
it always return False.
restrict_TypeVar_for_object(typeVar , object)
is the function that I have made in order to remove all of the constraints of my typeVar that doesn't correspond to object.
How I time my functions :
from timeit import timeit
func_version = """..."""
task="""..."""
timeit(task , setups=func_version , number=10_000_000)
Here is the task that I have timed (as you can see above I have repeated it 10 000 000 times) :
Number = int | float
T = TypeVar("T" , int , str , Number)
restrict_TypeVar_for_object(T , 5)
# --> TypeVar("T" , int , Number)
My test :
- 1st version :
def restrict_TypeVar_for_object(ty , obj):
if not isTypeVar(ty) : #Just to be sure that ty is a TypeVar
raise ValueError ('Expected a TypeVar as an argument')
if not ty.__constraints__ :
return type(obj)
lCons=[cons for cons in ty.__constraints__ if check_type(obj , cons)]
return (TypeVar(ty.__name__ , *lCons)
if len(lCons)>1 else
lCons[0]
if lCons else
NullType)
Takes : 378.5483768 s
- 2nd version :
def restrict_TypeVar_for_object(ty , obj):
if not isTypeVar(ty) : #Just to be sure that ty is a TypeVar
raise ValueError ('Expected a TypeVar as an argument')
if not ty.__constraints__ :
return type(obj)
return (TypeVar(ty.__name__ , *lCons)
if len(lCons:=[cons for cons in ty.__constraints__ if check_type(obj , cons)])>1 else
lCons[0]
if lCons else
NullType)
Takes : 376.9706139 s
- 3rd version :
def restrict_TypeVar_for_object(ty , obj):
if not isTypeVar(ty) : #Just to be sure that ty is a TypeVar
raise ValueError ('Expected a TypeVar as an argument')
if not ty.__constraints__ :
return type(obj)
return (TypeVar(ty.__name__ , *lCons[:-1])
if len(lCons:=[c for c in ty.__constraints__ if check_type(obj,c)]+[NullType]) > 2
else
lCons[0])
Takes : 391.5145658000001 s
As you can see the 2nd one is the quickest. However I wasn't expecting the 3rd one to be that slow compared to the other. To be frank I was even expecting it to be the quickest one because I did less if statement in this one.
I have 2 hypothesis for why this is the case :
-First, the walrus operator but if it is, why is the 2nd version the quickest.
-Second, the fact that I add [NullType]
at the end of lCons
but I don't know why it would be this time consuming.
First of all, as tdelaney pointed out, the difference in term of their execution time comes from the creation of many list in the 3rd version :
[c for c in ty.__constraints__ if check_type(obj,c)]
which is common to all version.[NullType]
, the list resulting from the concatenationlCons + [NullType]
andlCons[:-1]
which are exclusive to the 3rd version.Now comes the 2nd part of my question, about the time consumption of the walrus operator.
The walrus operator takes more time than a simple assignation as shown by the test below :
tsk1 repeated 10 000 000 takes : 0.7440573999992921 s
tsk2 repeated 10 000 000 takes : 1.0597749000007752 s
I did the test many time and the result are always approximately the same.
Finally, here is how I should implement my
restrict_TypeVar_for_object(typeVar , object)
function in Python :Which, repeated 10 000 000 times takes : 383.57044309999765
However as you can see it still takes too much time so I have tried with the walrus operator :
This takes : 373.867099199997 s
I still don't know why the walrus operator is better in this case but will update this answer once I understand it.