No, the arguments are not always symmetric:
class Cosmology:
def proper_distance(self, z1=0., z2, /):
return self.scale_factor(z1) * self.comoving_distance(z1, z2)
And of course there are ways around this, that’s missing the point. The arbitrary defaults, if done at low level, could be useful to get rid of unnecessary input handling for small functions that are supposed to be fast.
For reference, I have managed to bring the performance of my decorator up to par with a plain wrapped(*args, **kwargs) call,[1] but that is still a couple of times slower than not having the decorator at all.
PS: Let me add that by doing the func(arg, other=None) workaround, you have effectively introduced two distinct calling patterns, and now have bigger problems.
-
By introducing a placeholder for args to be filled in, and precomputing the call pattern for each number of given positional arguments:
(defaults[0], ARG, ARG, ...). ↩︎