David,

I saw now that I missed the biggest problem with your proposal: yet again you 
deliberately throw away errors. I'm talking about making Python code _less_ 
error prone, while you seem to want to make it _more_. Anyway, I'll modify your 
reach() to not have the if in it that has this error hiding property, it also 
simplifies it a lot. It should look  like this:

def reach(name):
    return inspect.stack()[-2][0].f_locals[name]


> 1. Huge performance penalty
> 
> Huh? Have you actually benchmarked this is some way?!  A couple lookups into 
> the namespace are really not pricey operations.  The cost is definitely more 
> than zero, but for any function that does anything even slightly costly, the 
> lookups would be barely in the noise.

I'm talking about using this for all or most function calls that aren't 
positional only. So no, you can absolutely not assume I only use it to call 
expensive functions. And yea, I did benchmark it, and since you didn't define 
what you would think is acceptable for a benchmark you've left the door open 
for me to define it. This is the result of a benchmark for 10k calls (full 
source at the very end of this email):

CPython 3.6

time with use: 0:00:02.587355
time with standard kwargs: 0:00:00.003079
time with positional args: 0:00:00.003023

pypy 6.0

time with use: 0:00:01.177555
time with standard kwargs: 0:00:00.002565
time with positional args: 0:00:00.001953

So for CPython 3.6 it's 2.587355/0.003079 = 840x times slower
and pypy: 1.177555/0.002565 =  460x slower

I'm quite frankly a bit amazed pypy is so good. I was under the impression it 
would be much worse there. They've clearly improved the speed of the stack 
inspection since I last checked.

>  
> 2. Rather verbose, so somewhat fails on the stated goal of improving 
> readability
> 
> The "verbose" idea I propose is 3-4 characters more, per function call, than 
> your `fun(a, b, *, this, that)` proposal.  It will actually be shorter than 
> your newer `fun(a, b, =this, =that)` proposal once you use 4 or more keyword 
> arguments.

True enough. 

> 3. Tooling* falls down very hard on this
> 
> It's true that tooling doesn't currently support my hypothetical function.  
> It also does not support your hypothetical syntax. 

If it was included in Python it would of course be added super fast, while the 
use() function would not. This argument is just bogus.

> It would be *somewhat easier* to add special support for a function with a 
> special name like `use()` than for new syntax.  But obviously that varies by 
> which tool and what purpose it is accomplishing.

Easier how? Technically? Maybe. Politically? Absolutely not. If it's in Python 
then all tools _must_ follow. This solved the political problem of getting tool 
support and that is the only hard one. The technical problem is a rounding 
error in this situation.

> Of course, PyCharm and MyPy and PyLint aren't going to bother special casing 
> a `use()` function unless or until it is widely used and/or part of the 
> builtins or standard library.  I don't actually advocate for such inclusion, 
> but I wouldn't be stridently against that since it's just another function 
> name, nothing really special.


Ah, yea, I see here you're granting my point above. Good to see we can agree on 
this at least.

/ Anders


Benchmark code:
-----------------------

import inspect
from datetime import datetime


def reach(name):
    return inspect.stack()[-2][0].f_locals[name]

def use(names):
    kws = {}
    for name in names.split():
        kws[name] = reach(name)
    return kws

def function(a=11, b=22, c=33, d=44):
    pass

def foo():
    a, b, c = 1, 2, 3
    function(a=77, **use('b'))

c = 10000

start = datetime.now()
for _ in range(c):
    foo()
print('time with use: %s' % (datetime.now() - start))


def bar():
    a, b, c = 1, 2, 3
    function(a=77, b=b)


start = datetime.now()
for _ in range(c):
    bar()
print('time with standard kwargs: %s' % (datetime.now() - start))


def baz():
    a, b, c = 1, 2, 3
    function(77, b)


start = datetime.now()
for _ in range(c):
    baz()
print('time with positional args: %s' % (datetime.now() - start))

_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to