f---ing typechecking

J

James Stroud

Steven said:
The user's expected behaviour for [1] + (1,) might be to return a list, or
it might be to return a tuple. Since there is no obviously correct
behaviour, the right thing to do is to refuse to guess.
I guess we differ on what is obvious. This seems obvious to me:

[1] + (1,) => [1, 1]
(1,) + [1] => (1, 1)

simply becuase the operand on the left should take precendence because
its "__add__" is called and its "__add__" returns a list.

But that's data dependent. When you call

[1] + MyTuple(1)

your MyTuple.__radd__ will be called first, not the list's __add__.

OK. With this you are beginning to convince me. Yes, I would want my
__radd__ called in this case (which is a form of mind-reading, but
necessary and convenient)--so your point is how might python read one's
mind given list and tuple. You got me there.

But you must agree that I am not an easy turn.
 
J

James Stroud

Paul said:
Since tuples are immutable, I think of them as fixed data objects with
some simple sequential structure, as opposed to lists which are much
more dynamically accessible/updateable data containers. Back in my
relational database design days, I sometimes had to create a primary
key for a table by combining values stored in two or more columns -
neither column value alone was unique, but the combination of them
was, and so made a good retrieval index. In Python, such data pairs
would be ideally represented with tuples, in support of in-memory data
cacheing or tree indexing - for a given record, the values don't
change, so the immutability of their tupleness doesn't get in the way.

In similar vein, I've used tuples internally in my Python code as
cache keys for function memoizing. They are WORM structures - write
once, read many - built to represent the cache value, but never
updated.
With this idea of tuples as a data structure, I could reasonably
interpret this:

(1,"abc",3) + [1]

to result in (1,"abc",3,[1]) just as well as (1,"abc",3,1). But
instead of just picking one, Python complains about this, and so
forces me to explicitly use

(1,"abc",3) + tuple([1])

or

(1,"abc",3) + ([1],)

I don't think tuples are just an academic curiosity, as your post
seems to suggest.

-- Paul

Were lists implemented as efficiently as tuples in terms of memory and
speed, there would be no difference except the academic one--which is
perhaps important.

Actually, I must admit that I use them and their implicit meanings all
the time in my code (as proof, notice in the "any way to create a
table-like object?" thread how I specify column headers--its for a
reason I use tuple in the example because the resulting data structures
will be ordered according to the tuple passed--of course, I refuse to
enforce this via "ugly and boring" type checking). For the most part, I
ignore the implementation differences and draw the distinction at their
semantics.

Also, risking getting a reputation for being overly contentious, I think
your argument falls apart in the latter half of your post because "+",
as far as I can tell with python sequences, is used to mean catenate
exclusively and not append, which is what you suggest as a possibility.

Anyway, D'Arpano made an excellent point with a good mind-reading
example and so I understand the design decision regarding catenation of
tuples and lists more clearly now. I'm still not sure I like it, but
there is no accounting for taste.

James
 
M

Marc

I guess we differ on what is obvious. This seems obvious to me:

[1] + (1,) => [1, 1]
(1,) + [1] => (1, 1)

simply becuase the operand on the left should take precendence because
its "__add__" is called and its "__add__" returns a list. In essence, as
we know the obviously correct behavior for __add__ and __radd__, then it
would be the obviously correct behavior that the above would follow.

Given those obviouses, the following seems to me:

[1] + (1,) => [1, (1,)]

That's the trouble with obvious -- my obvious may not be so obvious to
you (and vice versa). That's why the Zen of Python says "In the face
of ambiguity, refuse the temptation to guess." (Although it also says
"Flat is better than nested", but I'll ignore that for now.)

Basically -- if you want Perl, you know where to find it ;-)

Marc
 
S

skip

Sergey> posix.stat_result is CLASS, not regular tuple.

I believe it morphed from being a tuple though. I wasn't suggesting that
there is some other way to approximate records. I was trying to demonstrate
the use of a tuple as a record. It eventually became so compelling that a
new struct_time type was created. It works both like a tuple as a class.

The notion of tuples as records in Python is not new:

http://mail.python.org/pipermail/python-list/1999-December/thread.html

Search for "super tuples".

Skip
 
N

Neil Cerutti

Since tuples are immutable, I think of them as fixed data objects with
some simple sequential structure, as opposed to lists which are much
more dynamically accessible/updateable data containers.

Me, too. There are plenty of things that aren't strictly a
sequence, but which you sometimes want to iterate over anyhow.
If I had a set of fast, overpriced luxury cars in my garage, I
would sometimes want to do something to each one (like wash
them), even though they really aren't in any sequence.

Dictionary keys are the same sort of thing. Even though they
aren't in any sequence, it's still useful to iterate over them.
 
N

Neil Cerutti

Szabolcs said:
L=[1]
L.extend((1,))
L
[1, 1]

Are list.extend() and list concatenation supposed to behave
differently? I always thought concatenation was just shorthand
for calling extend().

They are different. list.extend() mutates the list, returning
None, while the + operator returns a new, concatenated list.

+= on the other hand works very similarly to list.extend().
However:
[1, 2].extend([3])
[1, 2] += [3]
SyntaxError: augmented assign to list literal or comprehension not possible
It seems like the '+' operator for lists should accept any
iterable for the right side argument to be consistent with
extend() and the '+=' operator.

+= will only perform the operation in place "whenever possible",
so there are objects for which a conversion to .extend wouldn't
work, and you'd get an actual call of the + operation followed by
the assignment operation.
 
D

Dennis Lee Bieber

[1] + (1,) => [1, (1,)]
Heck, let's go to the perverse absurdity...

[1] + (1,) => {"list": [1], "tuple" (1,)} + (2,) => {"list": [1],
"tuple": (1, 2)}

Which, sort of, treats the mixed pair like a complex number: list <>
real part, tuple <> imaginary part

<G>
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
B

Ben Finney

James Stroud said:
I increasingly come to the decision to avoid tuples altogether
because, eventually, you end up turning them into lists anyway

I don't. I end up extracting them to separate variables.

If they are eventually extracted to lists, then it generally makes no
sense for them ever to begin as a tuple.
 
S

skip

Sergey> posix.stat_result is CLASS, not regular tuple.

Sergey> It it morphed, the tuple nature of it is just history now.

No, it is still full of tuple-fu:
(33188, 92111L, 26738688L, 1, 0, 1, 355L, 1171570459, 1164401316,
1171087243)
>>> type(s)
said:
>>> s.st_mtime 1164401316
>>> s[0:3] (33188, 92111L, 26738688L)
>>> s[4:] + s[0:3] (0, 1, 355L, 1171570459, 1164401316, 1171087243, 33188, 92111L, 26738688L)
>>> type(s[4:] + s[0:3])
said:
>> The notion of tuples as records in Python is not new:
>> http://mail.python.org/pipermail/python-list/1999-December/thread.html
>> Search for "super tuples".

Sergey> Did it go beyond just talking?

Raymond Hettinger just proposed adding a pure Python implementation to
Python 2.6. The version he's been using for about a year is here:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/500261

Sergey> We can think about tuples anything, but are they something other
Sergey> than freezed lists? We can index them, slice, iterate,
Sergey> map/redice/filter, can cast (fuckin C!) to lists and back -
Sergey> what the difference?? "Tuples is similar to records" - I think
Sergey> this is just topological artefact, generated when python was
Sergey> compared with some other languague :)

The fact that tuples and lists share so many implementation details makes
people think of tuples as immutable lists. That view has its uses (allowing
you to pretend that you can use lists as dictionary keys or take advantage
of the compile-time allocation of tuples of constants, for example), but I
don't think that makes the view of tuples as records a "topological
artifact". I'm not sure where you believe Python was compared to some other
language. My original comment was that tuples could be thought of more like
C structs or Pascal records. That was an analogy, not a language
comparison.

Sergey> classes have sintactically and functionally overcame
Sergey> records/structs - why to drag to the same role tuples, that have
Sergey> not initially any feature of record?

They have a number of record-like features. They are relatively compact
(unlike class instances), once instantiated they can't be extended (unlike
lists or class instances in the common case). They just don't yet have
named attributes.

Skip
 
D

Donn Cave

Paul Rubin said:
Should f(*args) receive a list rather than a tuple arg?

No, clearly not. Function parameters are good example
of exactly what tuples are about, with some extra struct/dict
stuff for emphasis.

If t is a valid argument tuple for function f, then can t[1:]
also be a valia argument tuple for function f?

For ordinary functions without special argument handling, no.
We know that without having to know anything about t, and not
much about f. This is characteristic of tuple applications.

Donn Cave, (e-mail address removed)
 
P

Paul Rubin

Donn Cave said:
If t is a valid argument tuple for function f, then can t[1:]
also be a valid argument tuple for function f?

For ordinary functions without special argument handling, no.
We know that without having to know anything about t, and not
much about f. This is characteristic of tuple applications.

I'm not sure what you're saying. The current situation is if I say

def f(*args):
print args

f (1,2,3,4,5,6,7)

f receives a 7-element tuple, but if I say

f (8, 9, 10)

f receives a 3-element tuple.

I'm asking whether f should receive a list instead. I think that is
more in keeping with the notion of a tuple being like a structure
datatype. How can there be a structure datatype with an unpredictable
number of members?

It might have come across as a different question-sorry for any
confusion.
 
P

Paul McGuire

How can there be a structure datatype with an unpredictable
number of members?

It might have come across as a different question-sorry for any
confusion.

This may be where the "tuple is like a struct" analogy isn't so good,
and "tuple is like a list but immutable" is better.

In both of your examples, the caller of f() sent a fixed list of
arguments: (1,2,3,4,5,6,7) in the first case and (8,9,10) in the
second. Internally I bet the compiler wants to clean up that same
list of those same args when f() is finished.

Just as a thought experiment, do this for yourself. Implement f as:

def f(*args):
args = list(args)
print args

Now what is it you want to do with args that you can't do with it as a
tuple? Do you want some form of communication back to the caller to
occur, for example? Maybe some kind of pass-by-ref instead of pass-by-
value?

-- Paul
 
P

Paul Rubin

Paul McGuire said:
Now what is it you want to do with args that you can't do with it as a
tuple?

I'm ok with it being a tuple, but I'm not so wed to the notion that
tuples are record structures. I think it would be lame to not be able
to iterat through the arg list, for example.
 
C

Chris Mellon

This may be where the "tuple is like a struct" analogy isn't so good,
and "tuple is like a list but immutable" is better.

In both of your examples, the caller of f() sent a fixed list of
arguments: (1,2,3,4,5,6,7) in the first case and (8,9,10) in the
second. Internally I bet the compiler wants to clean up that same
list of those same args when f() is finished.

Just as a thought experiment, do this for yourself. Implement f as:

def f(*args):
args = list(args)
print args

Now what is it you want to do with args that you can't do with it as a
tuple? Do you want some form of communication back to the caller to
occur, for example? Maybe some kind of pass-by-ref instead of pass-by-
value?

I wrote code that did exactly this just yesterday. I needed a list
because I wanted to pop arguments off as I consumed them.
 
D

Donn Cave

Paul Rubin said:
Donn Cave said:
If t is a valid argument tuple for function f, then can t[1:]
also be a valid argument tuple for function f?

For ordinary functions without special argument handling, no.
We know that without having to know anything about t, and not
much about f. This is characteristic of tuple applications.

I'm not sure what you're saying. The current situation is if I say

def f(*args):
print args

f (1,2,3,4,5,6,7)

f receives a 7-element tuple, but if I say

f (8, 9, 10)

f receives a 3-element tuple.

OK, and of course

- f has some implementation which assigns meaning to each
parameter, according to its position.

- it does this by assigning names to those positions (this isn't
essential to my point, but reinforces the struct analogy)

For any basic, no-tricks implementation of f, the second application
is going to have problems:

- fourth and following positions have no actual parameters

- if you meant to portray (8, 9, 10) as t[4:], where t is
(_, _, _, _, 8, 9, 10), following my proposition above,
then it should be clear that inasmuch as t[4] for example
has some meaning in terms of f, one cannot preserve this
meaning in a smaller slice of t. Please think about it,
because I do not intend to try to explain this again.

So the parameters of f form a sort of type -- an ordered sequence
of values, where each item has a different meaning according to
its absolute position in the sequence, and where a name is assigned
(by at least f and optionally by its caller) to each position.
This type can be represented by many possible values, but we can
observe that just as a matter of principle, for any value of this
type, a smaller slice is not also of this type.

Of course the word "type" above is about something that is not
formally implemented in Python, so you can obtusely quibble about
whether it's a type or not, but the constraints are real.
I'm asking whether f should receive a list instead. I think that is
more in keeping with the notion of a tuple being like a structure
datatype. How can there be a structure datatype with an unpredictable
number of members?

Unpredictable? How do you manage to write functions in this case?
Are all your formal parameter lists like (*a), with logic to deal
with the variable lengths? But even that wouldn't by itself make
your point. Any experienced C programmer is likely to have "subtyped"
a struct, adding one or more values to the end of the parent struct,
so the value can be passed to functions that expect the parent type.
Variable length isn't strictly foreign to struct types.

Donn Cave, (e-mail address removed)
 
P

Paul Rubin

Donn Cave said:
Unpredictable? How do you manage to write functions in this case?
Are all your formal parameter lists like (*a), with logic to deal
with the variable lengths?

I'm thinking of functions like printf, which take a variable number of
args and don't assign them into variables by position.
 
D

Donn Cave

Paul Rubin said:
I'm thinking of functions like printf, which take a variable number of
args and don't assign them into variables by position.

I don't really see why you're thinking of them, but if
you look at how they do it, you'll see that they use some
run time magic to work as if they were written as conventional
functions. This invariably involves some accessory data, like
printf's format string, that in effect tells the function its
parameter implementation at run time - including number of
parameters, since you can't tell even that from standard args
data as far as I know. What this proves is that you can implement
an argument list at run time, but it by no means changes the
nature of the argument list as a sequence.

Donn Cave, (e-mail address removed)
 
P

Paul Rubin

Donn Cave said:
What this proves is that you can implement
an argument list at run time, but it by no means changes the
nature of the argument list as a sequence.

Right, it's treated as a sequence rather than a record structure.
So is that consistent with the "tuples are record structures" view,
as opposed to the "tuples are immutable lists" view?
 
P

Paul Rubin

Paul McGuire said:
This may be where the "tuple is like a struct" analogy isn't so good,
and "tuple is like a list but immutable" is better.

Right, that's what I'm getting at.
 

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

Members online

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top