Why does passing tuple as arg WITHOUT scattering work?

A

Alf P. Steinbach

Hi all.

I'm just learning Python from scratch, on my own. Apologies if this question is
too newbie... Or perhaps answered in some FAQ (where?).

Here's my original code for simple starter program, using the ActivePython
implementation in Windows XP Prof, Python version is 2.6:


<code>
import Tkinter

window = Tkinter.Tk()
window.title( "A fixed size ellipse..." )
window.geometry( "350x200" ) # Client area size, not window size.
window.resizable( width = 0, height = 0 )

canvas = Tkinter.Canvas( window, bg = "white" )
bbox = 2, 2, 347, 197 # Pixel coors left, top, right, bottom
canvas.create_oval( bbox, fill = "PeachPuff" )
canvas.pack() # Fill the entire client area, please.

window.mainloop() # Process events until window is closed.
</code>


It worked nicely, and I thought this code was fairly perfect until I started
studying the language reference.

It seems that formally correct code should apply the scatter operator to the
tuple, like this:


canvas.create_oval( *bbox, fill = "PeachPuff" )


And this /also/ works nicely!

I think it's this latter that is correct, and that the former just worked by
accident, due to e.g. the way that some C function parses arguments or such?

But I'm unable to figure it out, so, what's correct (both? one?), and assuming
it's the latter that's correct, would the first version still work in practice
regardless of Python / Tkinter implementation?


Cheers,

- Alf (total newbie)
 
M

MRAB

Alf said:
Hi all.

I'm just learning Python from scratch, on my own. Apologies if this
question is too newbie... Or perhaps answered in some FAQ (where?).

Here's my original code for simple starter program, using the
ActivePython implementation in Windows XP Prof, Python version is 2.6:


<code>
import Tkinter

window = Tkinter.Tk()
window.title( "A fixed size ellipse..." )
window.geometry( "350x200" ) # Client area size, not window
size.
window.resizable( width = 0, height = 0 )

canvas = Tkinter.Canvas( window, bg = "white" )
bbox = 2, 2, 347, 197 # Pixel coors left, top, right,
bottom
canvas.create_oval( bbox, fill = "PeachPuff" )
canvas.pack() # Fill the entire client area,
please.

window.mainloop() # Process events until window is
closed.
</code>


It worked nicely, and I thought this code was fairly perfect until I
started studying the language reference.

It seems that formally correct code should apply the scatter operator to
the tuple, like this:


canvas.create_oval( *bbox, fill = "PeachPuff" )


And this /also/ works nicely!

I think it's this latter that is correct, and that the former just
worked by accident, due to e.g. the way that some C function parses
arguments or such?

But I'm unable to figure it out, so, what's correct (both? one?), and
assuming it's the latter that's correct, would the first version still
work in practice regardless of Python / Tkinter implementation?
I've looked at the source in Tkinter.py. The positional arguments are
collected and then flattened into a tuple (tuples and lists are
'scattered').
 
A

Alf P. Steinbach

* Rhodri James:
[snip]
canvas.create_oval( bbox, fill = "PeachPuff" )
[snip]

It worked nicely, and I thought this code was fairly perfect until I
started studying the language reference.

It seems that formally correct code should apply the scatter operator
to the tuple, like this:


canvas.create_oval( *bbox, fill = "PeachPuff" )


And this /also/ works nicely!

I think it's this latter that is correct, and that the former just
worked by accident, due to e.g. the way that some C function parses
arguments or such?

But I'm unable to figure it out, so, what's correct (both? one?), and
assuming it's the latter that's correct, would the first version still
work in practice regardless of Python / Tkinter implementation?

No. Tkinter.py goes to some lengths to make both of these work, unpacking
bbox if it happens to be a tuple (or packing if it wasn't; I admit I
didn't look at Tkinter.py very hard!)

Thanks, I now just found this code in [Tkinter.py]

<code>
def _create(self, itemType, args, kw): # Args: (val, val, ..., cnf={})
"""Internal function."""
args = _flatten(args)
cnf = args[-1]
if type(cnf) in (DictionaryType, TupleType):
args = args[:-1]
else:
cnf = {}
return getint(self.tk.call(
self._w, 'create', itemType,
*(args + self._options(cnf, kw))))
</code>

and although I don't understand it all it seems indeed to deal specially with a
dictionary or tuple as argument.

However I didn't find that documented anywhere.

I guess I'll find that sooner or later... :)

As to which of the approaches is correct, the answer is whichever one
works for a given function. It's rare to see the second version, partly
because it's rare to have a tuple in hand that you want to unpack like
that if you don't need to. However, generally speaking a function will
expect a tuple or not; libraries that can cope with either are the
exception rather than the rule.


Cheers & thanks again, that cleared it up,

- Alf
 
E

Ethan Furman

MRAB said:
I've looked at the source in Tkinter.py. The positional arguments are
collected and then flattened into a tuple (tuples and lists are
'scattered').

Huh. I would have said 'gathered'. Must be my FoxPro days sneaking up
on me. *Shudder*

~Ethan~
 
G

Gabriel Genellina

I'm just learning Python from scratch, on my own. Apologies if this
question is too newbie... Or perhaps answered in some FAQ (where?).

Welcome! I hope you'll enjoy the language. And no, it's not a trivial
question.
import Tkinter
canvas = Tkinter.Canvas( window, bg = "white" )
bbox = 2, 2, 347, 197
canvas.create_oval( bbox, fill = "PeachPuff" )
[...]
It worked nicely, and I thought this code was fairly perfect until I
started studying the language reference.

It seems that formally correct code should apply the scatter operator to
the tuple, like this:

canvas.create_oval( *bbox, fill = "PeachPuff" )

And this /also/ works nicely!

I think it's this latter that is correct, and that the former just
worked by accident, due to e.g. the way that some C function parses
arguments or such?
But I'm unable to figure it out, so, what's correct (both? one?), and
assuming it's the latter that's correct, would the first version still
work in practice regardless of Python / Tkinter implementation?

You can look for yourself, the high-level functions in Tkinter are written
in Python (there is a compiled C module too, _tkinter). Look into
Lib\lib-tk\Tkinter.py, Canvas.create_oval.

Warning: Tkinter is a very old library, and its code is not "pythonic"
enough by today's standards. In particular, Tkinter was originally written
_before_ the *args, **kw syntax was available. Several functions took a
_tuple_ of parameters and an explicit _dictionary_ for optional
parameters; support for variable number of arguments was added later.
The code is written in such a way to collect all positional arguments into
a tuple (flattening any inner list or tuple); if the last item in that
tuple is a dictionary, it is removed and used as the base options. Those
options are combined with an argument named "cnf" (which must be a
dictionary) and finally combined with any other named argument. That is,
it was (and still is) possible to write:

bbox = 2, 2, 347, 197
canvas.create_oval([bbox], {"fill" : "PeachPuff"})

instead of:

canvas.create_oval(bbox, fill="PeachPuff")

or even:

canvas.create_oval(2, 2, 347, 197, fill="PeachPuff")

In the end, the Tcl statement actually executed is like this:

..canvasname create oval 2 2 347 197 -fill PeachPuff

so it doesn't really matter how those parameters are passed.
But I'm unable to figure it out, so, what's correct (both? one?), and
assuming it's the latter that's correct, would the first version still
work in practice regardless of Python / Tkinter implementation?

I'd use the "modern" interface, that is, keyword arguments (instead of an
explicit dictionary). Although the documentation warns against the "old"
way, the fact is that Tkinter has changed very little in years, and the
cnf parameter is still there in version 3.1
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,284
Latest member
NicholeDum

Latest Threads

Top