Re: [Python-ideas] Positional only arguments

On 5/18/07, Aaron Brady <castironpi@comcast.net> wrote:
Not sure I understand the question. My point was only that you'll never reproduce this behavior:: >>> dict(self=1, other=2) {'self': 1, 'other': 2} >>> d = {} >>> d.update(container=2, sequence=1) >>> d {'container': 2, 'sequence': 1} Of course, you can rename the 'self' and 'container' parameters to something else, but that just means that you can't use whatever new names you choose. The only way to solve this is to use real positional-only arguments, which currently means using *args and parsing out things within the function body. STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On 5/18/07, Talin <talin@acm.org> wrote:
So should I take that as a +1 for: * Enforce double-underscore names as being positional-only syntactically, e.g.:: def f(__a, __b=None, *, c=42, **kwargs) Pro: requires no new syntax Con: slightly backwards incompatible (but who calls f(__a) anyway?) STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On Sat, May 19, 2007 7:40 am, Steven Bethard wrote: [...]
- There is also incompatibility in the definition of functions: 'def f(a, __b)' is currently correct, it wouldn't be anymore. - How would this work with name mangling and methods? For example: class Foo(object): def posfun(__self, __a, b, ...): * Would self have to be renamed __self? (As positional only arguments should come first) * Would __self and __a be mangled, as is the rule within classes up to now? If so care has to be taken I suppose. -- Arnaud

On 5/19/07, Arnaud Delobelle <arno@marooned.org.uk> wrote:
Good point. Though I also suspect that's pretty low frequency.
Yes.
* Would __self and __a be mangled, as is the rule within classes up to now? If so care has to be taken I suppose.
I don't see any reason to mangle them. The only time you'd ever notice is using locals(). I've also noticed that the current @posonly decorator won't solve the dict() and dict.update() problem::
I think it should probably be defined as something like:: def posonly(n=None): def get_positional_only_wrapper(func, n=n): if n is None: n = func.func_code.co_argcount n -= len(func.func_defaults) def positional_only_wrapper(*args, **kwargs): if len(args) < n: msg = '%s() expected %i positional arguments, found %i' raise TypeError(msg % (func.__name__, n, len(args))) return func(*args, **kwargs) return positional_only_wrapper return get_positional_only_wrapper But note that this still doesn't solve the dict() and dict.update() problem because we still can't get 'self' and 'container' in the **kwargs::
STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On 5/20/07, Steven Bethard <steven.bethard@gmail.com> wrote:
Ouch, hadn't thought of that. Many people find explicit self ugly, and now you'll tell them they *have to* spell it __self (or __s for fewer keystrokes) ? That's indeed ugly. The semicolon proposal looks better, at least for this case: class Foo(object): def posfun(self, a; b, ...): George -- "If I have been able to see further, it was only because I stood on the shoulders of million monkeys."

George Sakkis wrote:
I would also say identifying the semicolon isn't a problem. After all we've been telling the difference between periods and comma's for quite a long time now with no difficulty. So it's more a matter of just getting used to looking for it in function signatures. Ron

On 5/20/07, Ron Adam <rrr@ronadam.com> wrote:
Of course, in most Python code, there are spaces after commas, but not periods:: (foo, bar, baz) foo.bar.baz In the semi-colon proposal, the spacing around commas and semi-colons will be exactly the same. That said, I'd really like positional-only arguments one way or another, so if they have to come with a semi-colon, I'm willing to accept that. STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On Sun, May 20, 2007 4:24 pm, Steven Bethard wrote:
I was asking because this means name mangling will have to be triggered conditionally in this case.
Yes. I don't see how this can be avoided in pure Python. But I came up with the solution below (I'm not saying it's good :) def posonly(arg=0, safe_kwargs=False): def deco(f, nposargs=0): name = f.__name__ if nposargs == 0: nposargs = f.func_code.co_argcount if safe_kwargs: nposargs += 1 posargnames = f.func_code.co_varnames[:nposargs] if safe_kwargs: def posf(*args, **kwargs): safe_kwargs = dict((k, kwargs.pop(k)) for k in posargnames if kwargs.has_key(k)) return f(safe_kwargs, *args, **kwargs) else: def posf(*args, **kwargs): for kw in kwargs: if kw in posargnames: raise TypeError("'%s()' arg %s is posonly" % (name, kw)) return f(*args, **kwargs) posf.__name__ = name return posf if isinstance(arg, int): return lambda f: deco(f, arg) else: return deco(arg) Note: again this is a 'proof of concept' implementation. Perhaps I should have started from my own implementation of 'posonly' instead of mine... If the optional argument 'safe_kwargs' is set to True, then the decorated function must provide an extra first positional argument which will contain the keyword arguments whose name is one of the positional argument names.
d=MyDict()
It solves the problem I think. It's not the most elegant but the question is whether the need for such a feature is strong enough to require new syntax. My personal opinion is that it isn't, as I've never needed it :) but I have limited experience. One could imagine a 'all_safe_kwargs' optional argument to posonly that would pack all the keyword arguments together and pass them on to the decorated function so that dict.update could be written: @posonly(2, all_safe_kwargs=True) def update(kwargs, self, container=None): # All keyword args are now in 'kwargs' ... -- Arnaud

On 5/20/07, Arnaud Delobelle <arno@marooned.org.uk> wrote:
Since currently the only use case for positional-only arguments is the dict() and dict.update() signature, I would simplify the decorator to look like:: def positional_kwargs(func): def func_with_positional_kwargs(*args, **kwargs): n_missing = func.func_code.co_argcount - len(args) - 1 args += func.func_defaults[-n_missing - 1:-1] args += (kwargs,) return func(*args) functools.update_wrapper(func_with_positional_kwargs, func) return func_with_positional_kwargs You could use it like:: >>> class D(object): ... @positional_kwargs ... def __init__(self, container=None, kwargs={}): ... print self, container, kwargs ... >>> d = D(self=1, container=2) <__main__.D object at 0x00F05E30> None {'self': 1, 'container': 2} That's not too bad because you're just replacing **kwargs with kwargs={}. And it's relatively easy to explain. I guess that would cover all the use cases I care about. I still lean slightly towards the lone '*' and '**' proposal, particularly since one of them is already introduced by `PEP 3102`_, but putting this decorator in functools is a good backup plan. .. _PEP 3102: http://www.python.org/dev/peps/pep-3102/ STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On 5/18/07, Talin <talin@acm.org> wrote:
So should I take that as a +1 for: * Enforce double-underscore names as being positional-only syntactically, e.g.:: def f(__a, __b=None, *, c=42, **kwargs) Pro: requires no new syntax Con: slightly backwards incompatible (but who calls f(__a) anyway?) STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On Sat, May 19, 2007 7:40 am, Steven Bethard wrote: [...]
- There is also incompatibility in the definition of functions: 'def f(a, __b)' is currently correct, it wouldn't be anymore. - How would this work with name mangling and methods? For example: class Foo(object): def posfun(__self, __a, b, ...): * Would self have to be renamed __self? (As positional only arguments should come first) * Would __self and __a be mangled, as is the rule within classes up to now? If so care has to be taken I suppose. -- Arnaud

On 5/19/07, Arnaud Delobelle <arno@marooned.org.uk> wrote:
Good point. Though I also suspect that's pretty low frequency.
Yes.
* Would __self and __a be mangled, as is the rule within classes up to now? If so care has to be taken I suppose.
I don't see any reason to mangle them. The only time you'd ever notice is using locals(). I've also noticed that the current @posonly decorator won't solve the dict() and dict.update() problem::
I think it should probably be defined as something like:: def posonly(n=None): def get_positional_only_wrapper(func, n=n): if n is None: n = func.func_code.co_argcount n -= len(func.func_defaults) def positional_only_wrapper(*args, **kwargs): if len(args) < n: msg = '%s() expected %i positional arguments, found %i' raise TypeError(msg % (func.__name__, n, len(args))) return func(*args, **kwargs) return positional_only_wrapper return get_positional_only_wrapper But note that this still doesn't solve the dict() and dict.update() problem because we still can't get 'self' and 'container' in the **kwargs::
STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On 5/20/07, Steven Bethard <steven.bethard@gmail.com> wrote:
Ouch, hadn't thought of that. Many people find explicit self ugly, and now you'll tell them they *have to* spell it __self (or __s for fewer keystrokes) ? That's indeed ugly. The semicolon proposal looks better, at least for this case: class Foo(object): def posfun(self, a; b, ...): George -- "If I have been able to see further, it was only because I stood on the shoulders of million monkeys."

George Sakkis wrote:
I would also say identifying the semicolon isn't a problem. After all we've been telling the difference between periods and comma's for quite a long time now with no difficulty. So it's more a matter of just getting used to looking for it in function signatures. Ron

On 5/20/07, Ron Adam <rrr@ronadam.com> wrote:
Of course, in most Python code, there are spaces after commas, but not periods:: (foo, bar, baz) foo.bar.baz In the semi-colon proposal, the spacing around commas and semi-colons will be exactly the same. That said, I'd really like positional-only arguments one way or another, so if they have to come with a semi-colon, I'm willing to accept that. STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy

On Sun, May 20, 2007 4:24 pm, Steven Bethard wrote:
I was asking because this means name mangling will have to be triggered conditionally in this case.
Yes. I don't see how this can be avoided in pure Python. But I came up with the solution below (I'm not saying it's good :) def posonly(arg=0, safe_kwargs=False): def deco(f, nposargs=0): name = f.__name__ if nposargs == 0: nposargs = f.func_code.co_argcount if safe_kwargs: nposargs += 1 posargnames = f.func_code.co_varnames[:nposargs] if safe_kwargs: def posf(*args, **kwargs): safe_kwargs = dict((k, kwargs.pop(k)) for k in posargnames if kwargs.has_key(k)) return f(safe_kwargs, *args, **kwargs) else: def posf(*args, **kwargs): for kw in kwargs: if kw in posargnames: raise TypeError("'%s()' arg %s is posonly" % (name, kw)) return f(*args, **kwargs) posf.__name__ = name return posf if isinstance(arg, int): return lambda f: deco(f, arg) else: return deco(arg) Note: again this is a 'proof of concept' implementation. Perhaps I should have started from my own implementation of 'posonly' instead of mine... If the optional argument 'safe_kwargs' is set to True, then the decorated function must provide an extra first positional argument which will contain the keyword arguments whose name is one of the positional argument names.
d=MyDict()
It solves the problem I think. It's not the most elegant but the question is whether the need for such a feature is strong enough to require new syntax. My personal opinion is that it isn't, as I've never needed it :) but I have limited experience. One could imagine a 'all_safe_kwargs' optional argument to posonly that would pack all the keyword arguments together and pass them on to the decorated function so that dict.update could be written: @posonly(2, all_safe_kwargs=True) def update(kwargs, self, container=None): # All keyword args are now in 'kwargs' ... -- Arnaud

On 5/20/07, Arnaud Delobelle <arno@marooned.org.uk> wrote:
Since currently the only use case for positional-only arguments is the dict() and dict.update() signature, I would simplify the decorator to look like:: def positional_kwargs(func): def func_with_positional_kwargs(*args, **kwargs): n_missing = func.func_code.co_argcount - len(args) - 1 args += func.func_defaults[-n_missing - 1:-1] args += (kwargs,) return func(*args) functools.update_wrapper(func_with_positional_kwargs, func) return func_with_positional_kwargs You could use it like:: >>> class D(object): ... @positional_kwargs ... def __init__(self, container=None, kwargs={}): ... print self, container, kwargs ... >>> d = D(self=1, container=2) <__main__.D object at 0x00F05E30> None {'self': 1, 'container': 2} That's not too bad because you're just replacing **kwargs with kwargs={}. And it's relatively easy to explain. I guess that would cover all the use cases I care about. I still lean slightly towards the lone '*' and '**' proposal, particularly since one of them is already introduced by `PEP 3102`_, but putting this decorator in functools is a good backup plan. .. _PEP 3102: http://www.python.org/dev/peps/pep-3102/ STeVe -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy
participants (6)
-
Aaron Brady
-
Arnaud Delobelle
-
George Sakkis
-
Ron Adam
-
Steven Bethard
-
Talin