459,995 Members | 1,086 Online
Need help? Post your question and get tips & solutions from a community of 459,995 IT Pros & Developers. It's quick & easy.

# Argument Precedence (possible bug?)

 P: n/a Hello all, I am just learning Python and have come across something I feel might be a bug. Please enlightenment me... The following code presents a challenge. How in the world do you provide an argument for *arg4? ## ================================================== ========== def argPrecedence(par1, par2=0, par3=0, *par4, **par5): print 'par1 =', par1, ' # positional argument' print 'par2 =', par2, ' # keyword argument' print 'par3 =', par3, ' # keyword argument' print 'par4 =', par4, ' # argument converted to tuple' print 'par5 =', par5, ' # argument converted to dictionary' argPrecedence('arg1', arg3='arg3', arg2='arg2', arg5='arg5') # argPrecedence Results: par1 = arg1 # positional argument par2 = arg2 # keyword argument par3 = arg3 # keyword argument par4 = () # argument converted to tuple par5 = {'arg5': 'arg5'} # argument converted to dictionary ## ================================================== ========== The code above is verbose with comments because I am just learning Python and am creating my own examples and reference materials... Can you solve the problem? If so, can you share the solution? If not, is this a bug, limitation or is it a feature? Thank you for your time on this one! PS. I could be far off with this codes purpose; to try and create an example that will house all the different possible parameters (positional, keyword, * and **) in one single def statement. -- Best Regards Victor B. Gonzalez Mar 5 '06 #1
11 Replies

 P: n/a Please forgive my call() Change this: argPrecedence('arg1', arg3='arg3', arg2='arg2', arg5='arg5') to this: argPrecedence('arg1', par3='arg3', par2='arg2', arg5='arg5') Thanks! Mar 5 '06 #2

 P: n/a Hello all, I am just learning Python and have come across something I feel might be a bug. Please enlightenment me... The following code presents a challenge. How in the world do you provide an argument for *arg4? ## ================================================== ========== def argPrecedence(par1, par2=0, par3=0, *par4, **par5): print 'par1 =', par1, ' # positional argument' print 'par2 =', par2, ' # keyword argument' print 'par3 =', par3, ' # keyword argument' print 'par4 =', par4, ' # argument converted to tuple' print 'par5 =', par5, ' # argument converted to dictionary' argPrecedence('arg1', par3='arg3', par2='arg2', arg5='arg5') # argPrecedence Results: par1 = arg1 # positional argument par2 = arg2 # keyword argument par3 = arg3 # keyword argument par4 = () # argument converted to tuple par5 = {'arg5': 'arg5'} # argument converted to dictionary ## ================================================== ========== The code above is verbose with comments because I am just learning Python and am creating my own examples and reference materials... Can you solve the problem? If so, can you share the solution? If not, is this a bug, limitation or is it a feature? Thank you for your time on this one! PS. I could be far off with this codes purpose; to try and create an example that will house all the different possible parameters (positional, keyword, * and **) in one single def statement. -- Best Regards Victor B. Gonzalez Mar 5 '06 #3

 P: n/a "vbgunz" wrote: I am just learning Python and have come across something I feel might be a bug. Please enlightenment me... The following code presents a challenge. How in the world do you provide an argument for *arg4? ## ================================================== ========== def argPrecedence(par1, par2=0, par3=0, *par4, **par5): print 'par1 =', par1, ' # positional argument' print 'par2 =', par2, ' # keyword argument' print 'par3 =', par3, ' # keyword argument' print 'par4 =', par4, ' # argument converted to tuple' print 'par5 =', par5, ' # argument converted to dictionary' argPrecedence('arg1', arg3='arg3', arg2='arg2', arg5='arg5') # argPrecedence Results: par1 = arg1 # positional argument par2 = arg2 # keyword argument par3 = arg3 # keyword argument par4 = () # argument converted to tuple par5 = {'arg5': 'arg5'} # argument converted to dictionary ## ================================================== ========== The code above is verbose with comments because I am just learning Python and am creating my own examples and reference materials... Can you solve the problem? If so, can you share the solution? If not, is this a bug, limitation or is it a feature? not sure what you think the bug is, but I suspect that you've completely missed the point of what * and ** do, and how they're used in real code. Mar 5 '06 #4

 P: n/a vbgunz wrote: I am just learning Python and have come across something I feel might be a bug. Please enlightenment me... The following code presents a challenge. How in the world do you provide an argument for *arg4? Maybe by putting that in the list of arguments? ... You don't even *have* an arg4 in the list, so how are we supposed to know what you are asking without guessing? Please consider rephrasing the question. -Peter Mar 5 '06 #5

 P: n/a On Sun, 05 Mar 2006 04:45:05 -0800, vbgunz wrote: Hello all, I am just learning Python and have come across something I feel might be a bug. Please enlightenment me... The following code presents a challenge. How in the world do you provide an argument for *arg4? ## ================================================== ========== def argPrecedence(par1, par2=0, par3=0, *par4, **par5): print 'par1 =', par1, ' # positional argument' print 'par2 =', par2, ' # keyword argument' print 'par3 =', par3, ' # keyword argument' print 'par4 =', par4, ' # argument converted to tuple' print 'par5 =', par5, ' # argument converted to dictionary' No, you are confused about * and ** arguments. They don't convert arguments, they collect them. def f(a, b=0, *args, **kwargs): print "a =", a print "b =", b print "args =", args print "kwargs =", kwargs Now call the function different ways, and see which will work and which won't. f(1) f(1, 2) f(1, b=2) f(a=1, b=2) f(b=2, a=1) Notice that this does not work: f(b=3) Do you understand why not? Now look at these calls: f(1, 2, 3) f(1, 2, 3, 4, 5, 6) f(1, 2, 3, 4, 5, 6, foo=7, bar=8) In the function definition f(a, b=0, *args, **kwargs), a must be supplied, b has a default value so it can be left out. Both a and b can be provided as positional arguments like f(2, 3) or as keyword arguments. Any _extra_ positional arguments are collected into a tuple called args, and any _extra_ keyword arguments are collected into a dictionary called kwargs. Of course you can use any names you like. Hope this helps, -- Steven. Mar 5 '06 #6

 P: n/a On Sun, 05 Mar 2006 04:45:05 -0800, vbgunz wrote: PS. I could be far off with this codes purpose; to try and create an example that will house all the different possible parameters (positional, keyword, * and **) in one single def statement. This is usually a bad idea, not because Python can't cope with it, but because it is usually better to learn new things in small pieces, not one giant enormous piece. Try creating a number of small functions that do different things: def f(a, b, c): print "a =", a, "b =", b, "c =", c def g(a=0, b=0, c=0): print "a =", a, "b =", b, "c =", c Now call them different ways, and see what happens: f() f(1) f(1,2) f(1,2,3) f(b=2) Can you see a pattern? g() g(1) g(1,2) g(1,2,3) g(b=2) Then move on to argument collectors: def h(a, b, c=0, *d, **e): print "a =", a, "b =", b, "c =", c print "d =", d, "e =", e Also, remember that "positional arguments" and "keyword arguments" aren't defined differently, they are given when you call the function. For example, suppose you have this: def function(x, y): print x + y Now you call it with positional arguments: function(3, 7) Now you call it with keyword arguments: function(x=3, y=7) Now you call it with both: function(3, y=7) All from the same definition. An argument is positional or keyword according to how it is given, not how it is defined. -- Steven. Mar 5 '06 #7

 P: n/a I am sorry I hung you up on a typo Peter Hansen. On line 5 *arg4 should have been *par4. I hope it makes complete sense now. Sorry. Mar 5 '06 #8

 P: n/a Please allow me some time to look at your examples. I get hung up over the smallest details because in my mind, my approach should have just worked... I learned about these parameters reading "O'reilly Learning Python 2nd Edition". On page 217 of the paperback or Chapter 13.5.6 in the ebook, topic: 'Argument matching: The Gritty Details' mentions the following in verbatim... ....''' Moreover, Python internally carries out the following steps to match arguments before assignment: 1. Assign non-keyword arguments by position. 2. Assign keyword arguments by matching names. 3. Assign extra non-keyword arguments to *name tuple. 4. Assign extra keyword arguments to **name dictionary. 5. Assign default values to unassigned arguments in header. '''... As you can probably tell, I tried to follow the steps exactly, except step 5 got me lost so I tried not to question it. Anyhow, thank you very much for your time and examples. I will get back to you as soon as I am done putting your response to trial. Thank you! Mar 5 '06 #9

 P: n/a "vbgunz" wrote in message news:11*********************@i40g2000cwc.googlegro ups.com... Hello all, I am just learning Python and have come across something I feel might be a bug. I feel it is more likely that there in a bug in the communication process from the manual to you, on an admittedly somewhat confusing and complex subject. Please enlightenment me... OK. Here is my attempt at a summary. The parameters for a function are the local variable names listed in the definition header in ()s after the function name. There may be any number of regular parameters and up to two collection parameters. The arguments for a function call are the objects listed in ()s after the function object (usually but not necessarily indicated by name). Argument objects are the result of evaluating an expression. An argument is optionally named by prefixing the expression with 'name=', with the restriction that all args after a named arg must also be named. Regular paramenter names (and slots of collection parameters) are bound to argument objects by position or name according to the rules given in the manual. At the end of the process, all parameters and arguments must exactly one binding. (For parameters, the binding may be a default given in the definition.) If not, an exception is raised and the call aborted. How in the world do you provide an argument for *arg4? You obviously mean par4. See below for how it gets a non-empty value. def argPrecedence(par1, par2=0, par3=0, *par4, **par5): Par1, par2, and par3 are regular parameters. They can by matched either by position or name on a per-call basis. Mixing position and name matching in any one call is strongly discouraged as confusing and error prone. Par2 and par3 have default arg objects (calculated when the function is defined). That only means that they do not have to match any of the args in a particular call. It does not determine whether the binding to explicit args in a particular call will be by position or name. print 'par1 =', par1, ' # positional argument' print 'par2 =', par2, ' # keyword argument' print 'par3 =', par3, ' # keyword argument' So these comments are wrong except as they happen to be right for a particular call. Par4 and par5 are leftover-argument collection objects bound by default to () and {} respectively if there are, respectively, no leftover positional or named args. So the way to get a nonempty tuple bound to par4 is to have a leftover positional arg which requires that you give at least 4 (since the first three will be bound to the three regular parameters). print 'par4 =', par4, ' # argument converted to tuple' print 'par5 =', par5, ' # argument converted to dictionary' Wrong. Leftover args are placed in, not individually converted to, a tuple or dictionary. PS. I could be far off with this codes purpose; to try and create an example that will house all the different possible parameters (positional, keyword, * and **) in one single def statement. Again, regular parameters are both positional and named, and the matching on any particular call can be either (subject to the positional, then named args restriction). In any case, what I think you mean above is not possible. As stated previously, the *leftover_pos_arg tuple will be nonempty only if all regular params are positionally matched. Terry Jan Reedy Mar 5 '06 #10

 P: n/a Hello, Steven D'Aprano, Terry Jan Reedy! I would really like to extend my thanks to you guys. I hope I've got it right this time! def posKeyArgs(a, b=2, c=3): print a, b, c #posKeyArgs(b=20) # too few positional arguments. a needs an arg. #posKeyArgs(10, c=30, 20) # pos_args cannot follow any kw_args. def specialArgs(*args, **kwargs): # Can't call these keywords! print args print kwargs specialArgs(args='arg1', kwargs='kwargs') # Keywords match nothing. specialArgs('string') # not converted. Collected into: ('string',) The above can begin to explain why I had problems with my first example. *args and **kwargs cannot have arguments assigned to them by keyword. Once an argument is matched by keyword, all following arguments must also be matched by keyword. This would explain why using only positional arguments, my first example would have worked right out of the box. Because, all positionals would have matched first, then all left overs would have been collected into *args, then finally any keyword args would be collected into **kwargs. So far the general consensus also seems to be not to over complicate a function definition with such parameters. This makes sense and is why I chose Python over many other options. I love the idea of explicit over implicit :) Again though, my first example was only a personal reference. Fellas, thank you all so much for helping me understand this much better. You guys are my angels of Python for sure! Mar 6 '06 #11

 P: n/a "vbgunz" wrote in message news:11**********************@v46g2000cwv.googlegr oups.com... Hello, Steven D'Aprano, Terry Jan Reedy! I would really like to extend my thanks to you guys. I hope I've got it right this time! def posKeyArgs(a, b=2, c=3): print a, b, c #posKeyArgs(b=20) # too few positional arguments. a needs an arg. Yes, parameter a must get bound to something. Any of pKA(10, b=20), pKA(a=10,b=20), pKA(c=30,a=5), etc, should work. #posKeyArgs(10, c=30, 20) # pos_args cannot follow any kw_args. Yes, this is an extreme of flexibility not allowed. Terry Jan Reedy Mar 7 '06 #12

### This discussion thread is closed

Replies have been disabled for this discussion.