arguments policy: **kwargs.pop()
Hi guys, I tried to find advice for hours, but failed so fer, so here is my question: Whenever I think to adopt a new module that does a good job, I always can't stand the temptation to look at the coding style and certain principles. Then I rather often see things like this: class someclass(object): # note that there is no comment about argument destruction... def __init__(self, **kwargs): first_arg = kwargs.pop('option_1', somedefault) ... nth_arg = kwargs.pop('option_n', somedefault') ... That is: There are arguments "consumed" rigorously by the __init__ of a class, although it would be equivalent to use kwargs.get(..., ...). Happened to me again, today and blocked me completely, since I don't know if I saw bad programming style or if I'm mislead. I agree there are valid cases when if makes sense to filter out some arguments that a subsequently called super() might not be able to handle. Bu even then, I would probably have a copy and make the filtering more explicit. But most of the time, there is no reason for using this pattern, and there is also no comment stating that the argument dict is modified by the callee for no good reason. I always used the policy that arguments are never changed by a function, unless explicitly stated. But since I see this pattern quite frequently, I wanted to ask if I am right by thinking this way, or if the general policy is more like "arguments may be destroyed by default". What do you think? Is this bad style and should be noticed somewhere, or is the caller supposed to protect the arguments, or are my worries useless? Thanks & cheers -- Chris -- Christian Tismer :^) mailto:tismer@stackless.com Software Consulting : Have a break! Take a ride on Python's Karl-Liebknecht-Str. 121 : *Starship* http://starship.python.net/ 14482 Potsdam : PGP key -> http://pgp.uni-mainz.de phone +49 173 24 18 776 fax +49 (30) 700143-0023 PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04 whom do you want to sponsor today? http://www.stackless.com/
I'm not sure what you're worried about here. Modifying kwds doesn't
actually modify the dict that was passed in. So are you just talking about
the style issue? Or would you also object to this:
def foo(x=1):
x += 1
?
On Thu, Apr 10, 2014 at 10:12 PM, Christian Tismer
Hi guys,
I tried to find advice for hours, but failed so fer, so here is my question:
Whenever I think to adopt a new module that does a good job, I always can't stand the temptation to look at the coding style and certain principles.
Then I rather often see things like this:
class someclass(object): # note that there is no comment about argument destruction...
def __init__(self, **kwargs): first_arg = kwargs.pop('option_1', somedefault) ... nth_arg = kwargs.pop('option_n', somedefault') ...
That is: There are arguments "consumed" rigorously by the __init__ of a class, although it would be equivalent to use kwargs.get(..., ...).
Happened to me again, today and blocked me completely, since I don't know if I saw bad programming style or if I'm mislead.
I agree there are valid cases when if makes sense to filter out some arguments that a subsequently called super() might not be able to handle. Bu even then, I would probably have a copy and make the filtering more explicit.
But most of the time, there is no reason for using this pattern, and there is also no comment stating that the argument dict is modified by the callee for no good reason.
I always used the policy that arguments are never changed by a function, unless explicitly stated. But since I see this pattern quite frequently, I wanted to ask if I am right by thinking this way, or if the general policy is more like "arguments may be destroyed by default".
What do you think? Is this bad style and should be noticed somewhere, or is the caller supposed to protect the arguments, or are my worries useless?
Thanks & cheers -- Chris
-- Christian Tismer :^) mailto:tismer@stackless.com Software Consulting : Have a break! Take a ride on Python's Karl-Liebknecht-Str. 121 : *Starship* http://starship.python.net/ 14482 Potsdam : PGP key -> http://pgp.uni-mainz.de phone +49 173 24 18 776 fax +49 (30) 700143-0023 PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04 whom do you want to sponsor today? http://www.stackless.com/
_______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (python.org/~guido)
Ah, now I see it. For some reason, I forgot that the dict is always newly created. That was really wrong thinking. Sorry! On 11/04/14 05:47, Guido van Rossum wrote:
I'm not sure what you're worried about here. Modifying kwds doesn't actually modify the dict that was passed in. So are you just talking about the style issue? Or would you also object to this:
def foo(x=1): x += 1
No, this is ok, although I personally tend not to modify args. No, it was just my temporary forgetting that the star args/kwds are always in a new container, and the whole reasoning chain was pointless, therefore. Thanks and cheers - Chris
On Thu, Apr 10, 2014 at 10:12 PM, Christian Tismer
wrote: Hi guys,
I tried to find advice for hours, but failed so fer, so here is my question:
Whenever I think to adopt a new module that does a good job, I always can't stand the temptation to look at the coding style and certain principles.
Then I rather often see things like this:
class someclass(object): # note that there is no comment about argument destruction...
def __init__(self, **kwargs): first_arg = kwargs.pop('option_1', somedefault) ... nth_arg = kwargs.pop('option_n', somedefault') ...
That is: There are arguments "consumed" rigorously by the __init__ of a class, although it would be equivalent to use kwargs.get(..., ...).
Happened to me again, today and blocked me completely, since I don't know if I saw bad programming style or if I'm mislead.
I agree there are valid cases when if makes sense to filter out some arguments that a subsequently called super() might not be able to handle. Bu even then, I would probably have a copy and make the filtering more explicit.
But most of the time, there is no reason for using this pattern, and there is also no comment stating that the argument dict is modified by the callee for no good reason.
I always used the policy that arguments are never changed by a function, unless explicitly stated. But since I see this pattern quite frequently, I wanted to ask if I am right by thinking this way, or if the general policy is more like "arguments may be destroyed by default".
What do you think? Is this bad style and should be noticed somewhere, or is the caller supposed to protect the arguments, or are my worries useless?
Thanks & cheers -- Chris
-- Christian Tismer :^) mailto:tismer@stackless.com Software Consulting : Have a break! Take a ride on Python's Karl-Liebknecht-Str. 121 : *Starship* http://starship.python.net/ 14482 Potsdam : PGP key -> http://pgp.uni-mainz.de phone +49 173 24 18 776 fax +49 (30) 700143-0023 PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04 whom do you want to sponsor today? http://www.stackless.com/
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 04/10/2014 10:12 PM, Christian Tismer wrote:
I always used the policy that arguments are never changed by a function, unless explicitly stated. But since I see this pattern quite frequently, I wanted to ask if I am right by thinking this way, or if the general policy is more like "arguments may be destroyed by default".
What do you think? Is this bad style and should be noticed somewhere, or is the caller supposed to protect the arguments, or are my worries useless?
The caller can't know or care that the function / method pops arguments:: $ python Python 2.7.3 (default, Feb 27 2014, 19:58:35) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> def foo(**kw): ... bar = kw.pop('bar', 'BAR') ... print 'bar: %s' % bar ... print 'kw: %s' % kw ... >>> foo() bar: BAR kw: {} >>> foo(bar='baz') bar: baz kw: {} >>> foo(bar='baz', bam='qux') bar: baz kw: {'bam': 'qux'} >>> mykw = {'bar': 'baz', 'bam': 'qux'} >>> foo(**mykw) bar: baz kw: {'bam': 'qux'} >>> mykw {'bam': 'qux', 'bar': 'baz'} because the caller gets its own copy of 'kw', even when called with an existing dict. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/ iEYEARECAAYFAlNHZhwACgkQ+gerLs4ltQ5RLQCeMaFvMDNexmCw9ggbg34w+AjP DKMAn1U1WRGW9PV8R/xqJs1HPWUBVEse =A8nP -----END PGP SIGNATURE-----
Thank you too, Tres. Somehow I had a brain shortcut and forgot that the dict is locally generated, *because* of the stars. Good to become adjusted and restarted, sorry about the noise. ciao - Chris On 11/04/14 05:48, Tres Seaver wrote:
On 04/10/2014 10:12 PM, Christian Tismer wrote:
I always used the policy that arguments are never changed by a function, unless explicitly stated. But since I see this pattern quite frequently, I wanted to ask if I am right by thinking this way, or if the general policy is more like "arguments may be destroyed by default".
What do you think? Is this bad style and should be noticed somewhere, or is the caller supposed to protect the arguments, or are my worries useless?
The caller can't know or care that the function / method pops arguments::
$ python Python 2.7.3 (default, Feb 27 2014, 19:58:35) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information.
def foo(**kw): ... bar = kw.pop('bar', 'BAR') ... print 'bar: %s' % bar ... print 'kw: %s' % kw ... foo() bar: BAR kw: {} foo(bar='baz') bar: baz kw: {} foo(bar='baz', bam='qux') bar: baz kw: {'bam': 'qux'} mykw = {'bar': 'baz', 'bam': 'qux'} foo(**mykw) bar: baz kw: {'bam': 'qux'} mykw {'bam': 'qux', 'bar': 'baz'}
because the caller gets its own copy of 'kw', even when called with an existing dict.
Tres.
_______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/tismer%40stackless.com
-- Christian Tismer :^) mailto:tismer@stackless.com Software Consulting : Have a break! Take a ride on Python's Karl-Liebknecht-Str. 121 : *Starship* http://starship.python.net/ 14482 Potsdam : PGP key -> http://pgp.uni-mainz.de phone +49 173 24 18 776 fax +49 (30) 700143-0023 PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04 whom do you want to sponsor today? http://www.stackless.com/
On Thu, Apr 10, 2014 at 7:12 PM, Christian Tismer
Then I rather often see things like this:
class someclass(object): # note that there is no comment about argument destruction...
def __init__(self, **kwargs): first_arg = kwargs.pop('option_1', somedefault) ... nth_arg = kwargs.pop('option_n', somedefault') ...
While it's been clarified that this isn't dangerous, I find it to be a really annoying style, as you've lost the opurtuniyt to docuemnt somethign in the signature. Is: def __init__(self, option_1=some_default, option_n=some_default, **kwargs): first_arg = kwargs.pop('option_1') nth_arg = kwargs.pop('option_n') *that* much harder to write? And many of these come with virtually no docstring, either..... oh well, -Chris -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception Chris.Barker@noaa.gov
Hi Chris, On 11/04/14 21:50, Chris Barker wrote:
On Thu, Apr 10, 2014 at 7:12 PM, Christian Tismer
wrote: Then I rather often see things like this:
class someclass(object): # note that there is no comment about argument destruction...
def __init__(self, **kwargs): first_arg = kwargs.pop('option_1', somedefault) ... nth_arg = kwargs.pop('option_n', somedefault') ...
While it's been clarified that this isn't dangerous, I find it to be a really annoying style, as you've lost the opurtuniyt to docuemnt somethign in the signature. Is:
def __init__(self, option_1=some_default, option_n=some_default, **kwargs): first_arg = kwargs.pop('option_1') nth_arg = kwargs.pop('option_n')
*that* much harder to write?
And many of these come with virtually no docstring, either.....
Thank you for re-validating my rant, after my wrong start was clarified. Yes, the style is of course annoying, still. This is the style of """hey look how cool I am, just taking an interface, picking args if they happen to be there and otherwise not treating them""". So while I'm still ashamed of my mis-interpretion, I am happy to still not like that very much. At least for myself, I like to be way more explicit and tell actively what I expect as arguments, what I do care about and what not, just to make sure that people see right by looking at the interface what they may ignore and what they should probably put in as an argument. Actually, putting so many defaults in without documenting that in the interface is this new-fangled sloppiness that is perceived as cool-ness. May be I am getting old, but I dislike this and tend to tell much more in the interface. And not in the 35th iteration, but when writing the first public version. This is because I don't want to throw an interface at somebody, but to discuss and improve it, and for that I put comments in that invite to agree or create a better version. I have these style problems with several modules that I am reluctant to use, therefore. I know that I'm pretty alone with that. But my idea of a published module is such that it should try to motivate why it is doing things in which way, and why it thinks this is good to do. Doing that not and nothing instead is my definition of "sloppy". (interested people may get the actual module from me why this came up) cheers -- Chris -- Christian Tismer :^) mailto:tismer@stackless.com Software Consulting : Have a break! Take a ride on Python's Karl-Liebknecht-Str. 121 : *Starship* http://starship.python.net/ 14482 Potsdam : PGP key -> http://pgp.uni-mainz.de phone +49 173 24 18 776 fax +49 (30) 700143-0023 PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04 whom do you want to sponsor today? http://www.stackless.com/
On 12.04.14 01:55, Ethan Furman wrote:
On 04/11/2014 02:01 PM, Christian Tismer wrote:
I have these style problems with several modules that I am reluctant to use, therefore. I know that I'm pretty alone with that.
You are not alone in that.
Funny not to be alone in being alone in that :-) hugs -- Chris -- Christian Tismer :^) mailto:tismer@stackless.com Software Consulting : Have a break! Take a ride on Python's Karl-Liebknecht-Str. 121 : *Starship* http://starship.python.net/ 14482 Potsdam : PGP key -> http://pgp.uni-mainz.de phone +49 173 24 18 776 fax +49 (30) 700143-0023 PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04 whom do you want to sponsor today? http://www.stackless.com/
On 11/04/14 21:50, Chris Barker wrote:
On Thu, Apr 10, 2014 at 7:12 PM, Christian Tismer
wrote: def __init__(self, **kwargs): first_arg = kwargs.pop('option_1', somedefault) ... nth_arg = kwargs.pop('option_n', somedefault') ...
Is:
def __init__(self, option_1=some_default, option_n=some_default, **kwargs):
*that* much harder to write?
I've done this kind of thing (extracting arguments out of **kwds) in various places in PyGUI, but the situation is somewhat special. Most classes in PyGUI have an api that accepts *any* of the object's properties as keyword arguments to the constructor, implemented by code in the base class's __init__ method. It would be impractical to explicitly document all of them as constructor args, so I don't. I just say in one place in the docs that this is general behaviour to be expected of all PyGUI classes. Sometimes a class needs to treat the initial values of some of its properties in a special way, instead of just passing them on to the base __init__. But this is an implementation detail -- having those particular args appear explicitly in the signature, but not any of the others, would serve no purpose, and would just clutter up the function header. In that situation, I find the extract-from-kwds style is often easier to read. Also, often I just want to *read* the value of some arguments, without popping them. Putting all of those in as explicit keyword args would mean explicitly passing them on to the base __init__, further cluttering up the code. -- Greg
participants (6)
-
Chris Barker
-
Christian Tismer
-
Ethan Furman
-
Greg Ewing
-
Guido van Rossum
-
Tres Seaver