Tuple question

A

Arthur

No one is saying you must use a list then. Do whatever you want!
But no one is forcing you to use a tuple, either (in this hypothetical
application), and if you need an index function, you know where to get
it.

Which to me, is what it boils down to. What are you trying to do, and
where's the fit. At a very practical level. I continue to think any
general discussion of homogeneity/heterogenuity is misdirecting. Python
relies on lists to be able to do duty in many different kinds of
circumstances, as oppose to acccessing specialized containers, as in other
languages. Clearly, in some of those circumstances homogeneity, in some
sense or other, is to the essence. In others it clearly is not. It's the
append method one is after, for example. In a dynamic app, append and
ordered access solves a set of problems that may or may not be reasonablely
conceptualized as related to homogeneity. So any attempt to describe
anything about lists vs. tuples in terms of its data content always in the
end seems unnecessarily reductionist, IMO - if that's the right word.

Art
 
D

Donn Cave

Quoth "Arthur" <[email protected]>:
| ... In a dynamic app, append and
| ordered access solves a set of problems that may or may not be reasonablely
| conceptualized as related to homogeneity. So any attempt to describe
| anything about lists vs. tuples in terms of its data content always in the
| end seems unnecessarily reductionist, IMO - if that's the right word.

Say, have we been here before? Remember, it really isn't about the
data content considered separately, rather the synthesis of structure
and data.

Donn Cave, (e-mail address removed)
 
A

Alex Martelli

Donn Cave said:
On the other hand, we normally use tuples for data that
is meaningful only when it's intact. The (key, value)

So by this argument len(t) should not work if t is a tuple...

I've never accepted the BDFL's explanations on what tuples are for; like
Python beginners I use them as immutable lists (to index into a
dictionary or be set members) and curse their lack of useful methods.
pair that comes back from dict.items(), for example. Each
value may very well be a string, but the sequence is not
homogeneous in the sense we're talking about, and index()
is not useful.

Even for a pair I sometimes like to know if 42 is the key, the value,
or neither. index is handy for that... but not if the pair is a tuple,
only if it's a list. Rationalize as you will, it's still a Python wart.

Pseudotuples with NAMED (as well as indexed) arguments, as modules stat
and time now return, may be a different issue. Not sure why we never
made declaring such pseudotuples as usertypes as easy as it should be, a
custom metaclass in some stdlib module shd be enough. But tuples whose
items can't be named, just indexed or sliced, just are not a good fit
for the kind of use case you and Guido use to justify tuple's lack of
methods, IMHO.


Alex
 
A

Alex Martelli

Peter Hansen said:
Consider, for example, that one actually has to build the
tuple in the first place... how can you do that without
having the info in a list to begin with? (I'm sure there
are ways if one is ingenious, but I think the answers
would just go to prove the point I was making.)

tuple(somegenerator(blah)) will work excellently well. In 2.4, you can
even often code that 'somegenerator' inline as a generator
comprehension. So this 'having the info in a list' argument sounds just
totally bogus to me.

Say I want to work with some primes and I have a primes generator.
Primes aren't going to change, so a tuple is a natural. I start with,
e.g.,

ps = tuple(itertools.islice(primes(), 999999))

....and then I'm stumped because I can't index into ps to find, say, the
progressive number of some given prime N by ps.index(N). How silly,
having to keep ps a list, i.e. mutable (when it intrinsically isn't)
just to be able to index into it! [I can usefully exploit ps's
sortedness via module bisect... ignoring the latter's specs and docs
that keep screamign LISTS, bisect.bisect DOES work on tuples... but I
wouldn't feel comfy about that surviving, given said docs and
specs...:)


Alex
 
A

Andrew Durdin

Pseudotuples with NAMED (as well as indexed) arguments, as modules stat
and time now return, may be a different issue. Not sure why we never
made declaring such pseudotuples as usertypes as easy as it should be, a
custom metaclass in some stdlib module shd be enough. But tuples whose
items can't be named, just indexed or sliced, just are not a good fit
for the kind of use case you and Guido use to justify tuple's lack of
methods, IMHO.

Such "pseudotuples" are easy enough to implement. Since I'm not all
that crash hot with metaclasses, I just made a tuple subclass (see
below). Would a metaclass implementation offer any significant
benefits over a subclass?

class NamedTuple(tuple):
"""Builds a tuple with elements named and indexed.

A NamedTuple is constructed with a sequence of (name, value) pairs;
the values can then be obtained by looking up the name or the value.
"""

def __new__(cls, seq):
return tuple.__new__(cls, [val for name,val in seq])

def __init__(self, seq):
tuple.__init__(self)
tuple.__setattr__(self, "_names", dict(zip([name for name,val
in seq], range(len(seq)))))

def __getattr__(self, name):
try:
return tuple.__getitem__(self, self.__dict__["_names"][name])
except KeyError:
raise AttributeError, "object has no attribute named '%s'" % name

def __setattr__(self, name, value):
if self._names.has_key(name):
raise TypeError, "object doesn't support item assignment"
else:
tuple.__setattr__(self, name, value)

# Example
if __name__ == "__main__":
names = ("name", "age", "height")
person1 = NamedTuple(zip(names, ["James", "26", "185"]))
person2 = NamedTuple(zip(names, ["Sarah", "24", "170"]))

print person1.name
for i,name in enumerate(names):
print name, ":", person2


(Submitted to the Cookbook: recipe #303439)
 
A

Arthur

Donn Cave said:
Quoth "Arthur" <[email protected]>:
| ... In a dynamic app, append and
| ordered access solves a set of problems that may or may not be reasonablely
| conceptualized as related to homogeneity. So any attempt to describe
| anything about lists vs. tuples in terms of its data content always in the
| end seems unnecessarily reductionist, IMO - if that's the right word.

Say, have we been here before?

Have we? ;)
Remember, it really isn't about the
data content considered separately, rather the synthesis of structure
and data.

Yes. I slipped. Continuing to discuss the issue in terms of homogenuity ane
hetereogenuity (in any sense) seems unnecessarily reductionist, IMO - if
that's the right word.

That Guido conceptualizes in some hard to define way related to these
concepts may in fact explain why things are as they are..

And I guess some of the questions that lead into to these discussions are
more of the "why are things as they are", rahter than anything related to
the practical use of lists and tuples. And in the contgext of the question
of "why things are as they are" it is hard to avoid discussion of
homogenuity and hetereogenuity - which is really mostly an attempt to psyche
out Guido's reasoning.

I guess I don't do PEPs, becuase I am a humble user - more interested in
picking things up once they are, and as they are - and accomplishing what I
need to accomplish

I certainly *don't* think the concepts of homogenuity and hetereogenuity
help a twit.

A clue about perfromance issues arounds tuples vs. lists is *much* more
interesting to me - for example.

Even a 20%-er.

Art
 
A

Alex Martelli

Andrew Durdin said:
...
Such "pseudotuples" are easy enough to implement. Since I'm not all
that crash hot with metaclasses, I just made a tuple subclass (see
below). Would a metaclass implementation offer any significant
benefits over a subclass?

I think of a tuple with a given sequence of names for its fields as a
type (a subclass of tuple, sure). For example, the name->index
correspondence for all the pseudotuples-with-9-items returned by module
time is just the same one -- why would I want to carry around that
dictionary for each INSTANCE of a time-pseudotuple, rather than having
it once and for all in the type? So I'd have, say:

example_type = tuple_with_names('foo', 'bar', 'baz')
assert issubclass(example_type, tuple)

and then I could make as many instances of this subclass of tuple as
needed, with a call like either example_type(1,2,3) or
example_type(baz=3,foo=1,bar=2) [the first form would accept 3
positional arguments, the second one 3 named arguments -- they're all
needed of course, but passing them as named will often result in clearer
and more readable application-level code].

You prefer to specify the names every time you make an instance and
build and carry the needed name->index dict along with each instance.
Ah well, I guess that's OK, but I don't really see the advantage
compared to the custom-metaclass approach.
(Submitted to the Cookbook: recipe #303439)

Great, thanks -- I should get the snapshot tomorrow (or whenever they do
start working again over in Vancouver, since I get it from
ActiveState:) and I'll be happy to consider presenting your approach
(and a custom metaclass for contrast;-).


Alex
 
D

Donn Cave

Quoth (e-mail address removed) (Alex Martelli):
| ...
|> On the other hand, we normally use tuples for data that
|> is meaningful only when it's intact. The (key, value)
|
| So by this argument len(t) should not work if t is a tuple...

I expect it's used relatively infrequently, and for different
reasons. "if len(info) == 5", for example - just from that
line from a relatively popular Python application, would you
guess info is a list, or a tuple?

| I've never accepted the BDFL's explanations on what tuples are for; like
| Python beginners I use them as immutable lists (to index into a
| dictionary or be set members) and curse their lack of useful methods.
|
| > pair that comes back from dict.items(), for example. Each
| > value may very well be a string, but the sequence is not
| > homogeneous in the sense we're talking about, and index()
| > is not useful.
|
| Even for a pair I sometimes like to know if 42 is the key, the value,
| or neither. index is handy for that... but not if the pair is a tuple,
| only if it's a list. Rationalize as you will, it's still a Python wart.

Maybe the problem is that tuples have too many features already.
It's sort of silly that they're indexed by number, and if that
weren't allowed, we would find fewer people trying to make lists
of them.

| Pseudotuples with NAMED (as well as indexed) arguments, as modules stat
| and time now return, may be a different issue. Not sure why we never
| made declaring such pseudotuples as usertypes as easy as it should be, a
| custom metaclass in some stdlib module shd be enough. But tuples whose
| items can't be named, just indexed or sliced, just are not a good fit
| for the kind of use case you and Guido use to justify tuple's lack of
| methods, IMHO.

There you go, they shouldn't be indexed or sliced, that's right!
Named attributes would be nice, but otherwise you use pattern
matching (to the extent support in Python -- key, value = item.)
Makes for more readable code.

Donn Cave, (e-mail address removed)
 
B

Bengt Richter

Quoth (e-mail address removed) (Alex Martelli):
| ...
|> On the other hand, we normally use tuples for data that
|> is meaningful only when it's intact. The (key, value)
|
| So by this argument len(t) should not work if t is a tuple...

I expect it's used relatively infrequently, and for different
reasons. "if len(info) == 5", for example - just from that
line from a relatively popular Python application, would you
guess info is a list, or a tuple?

| I've never accepted the BDFL's explanations on what tuples are for; like
| Python beginners I use them as immutable lists (to index into a
| dictionary or be set members) and curse their lack of useful methods.
|
| > pair that comes back from dict.items(), for example. Each
| > value may very well be a string, but the sequence is not
| > homogeneous in the sense we're talking about, and index()
| > is not useful.
|
| Even for a pair I sometimes like to know if 42 is the key, the value,
| or neither. index is handy for that... but not if the pair is a tuple,
| only if it's a list. Rationalize as you will, it's still a Python wart.

Maybe the problem is that tuples have too many features already.
It's sort of silly that they're indexed by number, and if that
weren't allowed, we would find fewer people trying to make lists
of them.

| Pseudotuples with NAMED (as well as indexed) arguments, as modules stat
| and time now return, may be a different issue. Not sure why we never
| made declaring such pseudotuples as usertypes as easy as it should be, a
| custom metaclass in some stdlib module shd be enough. But tuples whose
| items can't be named, just indexed or sliced, just are not a good fit
| for the kind of use case you and Guido use to justify tuple's lack of
| methods, IMHO.

There you go, they shouldn't be indexed or sliced, that's right!
Named attributes would be nice, but otherwise you use pattern
matching (to the extent support in Python -- key, value = item.)
Makes for more readable code.

How about just named read-only but redefinable views? E.g.,
... """tuple view"""
... _views = {}
... def __getattr__(self, name):
... try: return tuple.__getitem__(self, self.__class__._views[name])
... except KeyError: raise AttributeError, '%s is not a tuple view.' %name
... def __setattr__(self, name, ix):
... self.__class__._views[name] = ix
... def __delattr__(self, name): del self.__class__._views[name]
... Traceback (most recent call last):
File "<stdin>", line 1, in ?
Traceback (most recent call last):
File "<stdin>", line 1, in ?
(9, 8, 7, 6, 5, 4, 3)

Of course, with the views in the class's _views dict, further instances share
previous definitions:
('e', 'f')

You could generalize further with properties defining whatever viewing
function you want, of course.

Regards,
Bengt Richter
 
A

Andrew Durdin

I think of a tuple with a given sequence of names for its fields as a
type (a subclass of tuple, sure). For example, the name->index
correspondence for all the pseudotuples-with-9-items returned by module
time is just the same one -- why would I want to carry around that
dictionary for each INSTANCE of a time-pseudotuple, rather than having
it once and for all in the type?
You prefer to specify the names every time you make an instance and
build and carry the needed name->index dict along with each instance.
Ah well, I guess that's OK, but I don't really see the advantage
compared to the custom-metaclass approach.

Ah. This is one reason why a non-metaclass version is not so good.
There really is no advantage to my inheritance-based implementation --
I tried to make a metaclass version but ran into some issues. However,
on my second try I succeeded -- see below.
Great, thanks -- I should get the snapshot tomorrow (or whenever they do
start working again over in Vancouver, since I get it from
ActiveState:) and I'll be happy to consider presenting your approach
(and a custom metaclass for contrast;-).

Below is a better (=easier to use) implementation using metaclasses;
I've submitted it to the Cookbook anyway (recipe #303481) despite
being past the deadling. The NamedTuple function is for convenience
(although the example doesn't use it for the sake of explicitness).
NamedTuples accepts a single argument: a sequence -- as for tuple() --
or a dictionary with (at least) the names that the NamedTuple expects.


class NamedTupleMetaclass(type):
"""Metaclass for a tuple with elements named and indexed.

NamedTupleMetaclass instances must set the 'names' class attribute
with a list of strings of valid identifiers, being the names for the
elements. The elements can then be obtained by looking up the name
or the index.
"""

def __init__(cls, classname, bases, classdict):
super(NamedTupleMetaclass, cls).__init__(cls, classname,
bases, classdict)

# Must derive from tuple
if not tuple in bases:
raise ValueError, "'%s' must derive from tuple type." % classname

# Create a dictionary to keep track of name->index correspondence
cls._nameindices = dict(zip(classdict['names'],
range(len(classdict['names']))))


def instance_getattr(self, name):
"""Look up a named element."""
try:
return self[self.__class__._nameindices[name]]
except KeyError:
raise AttributeError, "object has no attribute named
'%s'" % name

cls.__getattr__ = instance_getattr


def instance_setattr(self, name, value):
raise TypeError, "'%s' object has only read-only
attributes (assign to .%s)" % (self.__class__.__name__, name)

cls.__setattr__ = instance_setattr


def instance_new(cls, seq_or_dict):
"""Accept either a sequence of values or a dict as parameters."""
if isinstance(seq_or_dict, dict):
seq = []
for name in cls.names:
try:
seq.append(seq_or_dict[name])
except KeyError:
raise KeyError, "'%s' element of '%s' not
given" % (name, cls.__name__)
else:
seq = seq_or_dict
return tuple.__new__(cls, seq)

cls.__new__ = staticmethod(instance_new)


def NamedTuple(*namelist):
"""Class factory function for creating named tuples."""
class _NamedTuple(tuple):
__metaclass__ = NamedTupleMetaclass
names = list(namelist)

return _NamedTuple


# Example follows
if __name__ == "__main__":
class PersonTuple(tuple):
__metaclass__ = NamedTupleMetaclass
names = ["name", "age", "height"]

person1 = PersonTuple(["James", 26, 185])
person2 = PersonTuple(["Sarah", 24, 170])
person3 = PersonTuple(dict(name="Tony", age=53, height=192))

print person1
for i, name in enumerate(PersonTuple.names):
print name, ":", person2
print "%s is %s years old and %s cm tall." % person3

person3.name = "this will fail"
 
R

Roy Smith

Donn Cave said:
I expect it's used relatively infrequently, and for different
reasons. "if len(info) == 5", for example - just from that
line from a relatively popular Python application, would you
guess info is a list, or a tuple?

I'd guess it was something which had a __len__ method.
Maybe the problem is that tuples have too many features already.
It's sort of silly that they're indexed by number, and if that
weren't allowed, we would find fewer people trying to make lists
of them.

If tuples weren't indexed, the only way you'd be able to access the
elements would be to unpack them, which would be rather inconvenient.
Unless of course you had an alternate way to name the elements, but if
you're going to allow named element access, and forbid indexed access,
then you might as well just create a normal class instance.

The more I look into this, the more I realize just how inconsistent the
whole thing is.

For example, tuples can be used as dictionary keys because they are
immutable. Or so it's commonly said. But, that's not true. The real
reason they can be used as keys is because they're hashable. If you try
to use a list as a key, it doesn't complain that it's immutable, it
complains that it's unhashable:
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: list objects are unhashable

Furthermore, regular objects can be used as keys, even though they *are*
mutable. You can do this:

class data:
pass

key = data ()
key.x = 1
key.y = 2

d = {}
d[key] = None

dictKey = d.keys()[0]
print dictKey.x, dictKey.y

key.x = 42
dictKey = d.keys()[0]
print dictKey.x, dictKey.y

If a mutable class instance object can be used as a dictionary key, then
I don't really see any reason a list shouldn't be usable as a key. How
is a class instance's mutability any less of disqualifier for key-ness
than a list's mutability?

And, once you allow lists to be keys, then pretty much the whole raison
d'etre for tuples goes away. And if we didn't have tuples, then we
wouldn't have to worry about silly syntax warts like t = (1,) to make a
1-tuple :)
 
B

Benjamin Niemann

Roy said:
Donn Cave said:
I expect it's used relatively infrequently, and for different
reasons. "if len(info) == 5", for example - just from that
line from a relatively popular Python application, would you
guess info is a list, or a tuple?


I'd guess it was something which had a __len__ method.

Maybe the problem is that tuples have too many features already.
It's sort of silly that they're indexed by number, and if that
weren't allowed, we would find fewer people trying to make lists
of them.


If tuples weren't indexed, the only way you'd be able to access the
elements would be to unpack them, which would be rather inconvenient.
Unless of course you had an alternate way to name the elements, but if
you're going to allow named element access, and forbid indexed access,
then you might as well just create a normal class instance.

The more I look into this, the more I realize just how inconsistent the
whole thing is.

For example, tuples can be used as dictionary keys because they are
immutable. Or so it's commonly said. But, that's not true. The real
reason they can be used as keys is because they're hashable. If you try
to use a list as a key, it doesn't complain that it's immutable, it
complains that it's unhashable:

d = {}
d[[1, 2]] = 3

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: list objects are unhashable

Furthermore, regular objects can be used as keys, even though they *are*
mutable. You can do this:

class data:
pass

key = data ()
key.x = 1
key.y = 2

d = {}
d[key] = None

dictKey = d.keys()[0]
print dictKey.x, dictKey.y

key.x = 42
dictKey = d.keys()[0]
print dictKey.x, dictKey.y

If a mutable class instance object can be used as a dictionary key, then
I don't really see any reason a list shouldn't be usable as a key. How
is a class instance's mutability any less of disqualifier for key-ness
than a list's mutability?

And, once you allow lists to be keys, then pretty much the whole raison
d'etre for tuples goes away. And if we didn't have tuples, then we
wouldn't have to worry about silly syntax warts like t = (1,) to make a
1-tuple :)

A very handy feature of lists is:

a = [1, 2, 3]
b = [1, 2, 3]
if a == b:
print "List equality is based on content"

while:

a = data()
a.x = 42
b = date()
b.x = 42
if a != b:
print "Other objects have an identity that is independent of\
content"

This special behaviour of lists is implemented by implementing the
__eq__ method. Objects with non-standard __eq__ usually don't have the
expected behaviour when used as keys:

a = [1, 2, 3]
b = [1, 2, 4]
d = {}
d[a] = "first"
d = "second"
a[2] = 4
b[2] = 3
print d[[1, 2, 3]]

Which result would you expect here?
 
R

Roy Smith

I asked:
How is a class instance's mutability any less of disqualifier for
key-ness than a list's mutability?

Benjamin Niemann said:
a = [1, 2, 3]
b = [1, 2, 3]
if a == b:
print "List equality is based on content"

Tuple (and string) equality is based on content too. So what? I can
give my data class an __eq__ method, and then my class instance equality
would also based on content.

So, to restate my original question, why should my mutable,
content-based-eqality class instance be a valid dictionary key, when a
list is not? Which part of a list's behavior makes it inherently
unusable as a key? I'm not asking about design philosophy, I'm asking
about observable behavior.
 
B

Benjamin Niemann

Roy said:
I asked:
How is a class instance's mutability any less of disqualifier for
key-ness than a list's mutability?


a = [1, 2, 3]
b = [1, 2, 3]
if a == b:
print "List equality is based on content"


Tuple (and string) equality is based on content too. So what?
tuples and strings are immutable.

I can
give my data class an __eq__ method, and then my class instance equality
would also based on content.

So, to restate my original question, why should my mutable,
content-based-eqality class instance be a valid dictionary key, when a
list is not? Which part of a list's behavior makes it inherently
unusable as a key? I'm not asking about design philosophy, I'm asking
about observable behavior.
The example I provides should have shown this: when you modify the
objects which is used as a dictionary key, the dictionary is also
modified. This is an usually undesired side effect.
Python won't prevent you from doing such things with your own class that
implements __eq__. But it does not do such things for its built-in classes.
 
B

Bengt Richter

I asked:
How is a class instance's mutability any less of disqualifier for
key-ness than a list's mutability?

Benjamin Niemann said:
a = [1, 2, 3]
b = [1, 2, 3]
if a == b:
print "List equality is based on content"

Tuple (and string) equality is based on content too. So what? I can
give my data class an __eq__ method, and then my class instance equality
would also based on content.

So, to restate my original question, why should my mutable,
content-based-eqality class instance be a valid dictionary key, when a
list is not? Which part of a list's behavior makes it inherently
unusable as a key? I'm not asking about design philosophy, I'm asking
about observable behavior.

I don't think a list is _inherently_ unusable, but an immutable sequence
is usable in a different way because of what can be assumed.
I suspect it has something to do with optimizing lookup. For immutables,
equal id should mean equal hash and equal value. If id's are not equal,
hashes only need to be computed once for an immutable, since they can
be cached in the immutables's internal representation (trading a little
space for computation time). If hashes are equal but id's are not, you
either have duplicate tuples/immutables or a rare collision. If you are
forced to compare values in the rare-collision case, I guess you are down
to comparing vectors of pointers, and comparison would then be similar
for tuples and lists. Different lengths would be early out non-equal. Etc.

It does seem like you could allow lists as keys, but it would mean
a performance hit when using them, even if you managed to get type-dependent
dispatching in the internal logic for free. You could still cache a list hash
internally, but you would have to invalidate it on list mutation, which would
add cost to mutation. Optimization tradeoffs ripple in surprising ways, and only
pay off if they are good in real usage patterns. Not easy to get right.

As it is, you could take a big hit and subclass dict to fake it, or you could
sublass list to provide tuple-like hashing and comparing ;-)

Regards,
Bengt Richter
 
B

Bryan Olson

Peter said:
> Consider, for example, that one actually has to build the
> tuple in the first place... how can you do that without
> having the info in a list to begin with? (I'm sure there
> are ways if one is ingenious, but I think the answers
> would just go to prove the point I was making.)

x = (1, 2)
y = (3, 4)
print x + y
 
B

Bryan Olson

Donn said:
> Maybe the problem is that tuples have too many features already.
> It's sort of silly that they're indexed by number, and if that
> weren't allowed, we would find fewer people trying to make lists
> of them.

Plus lists have the mis-feature that we can mix types.

The talk of how lists and tuples are supposed to be used
suggests that we want lists of any type, but only on type in
each list. Tuples should be indexed by statically know values.

We should treat each kind of tuple as a distinct type, that we
should not mix in a list, so should not be able to justify

[(5, 4.23), ("Hi", [])]

as a list of one type, simply because the type is 'tuple'. Since
the structure of tuples would be statically known, we can dump
the (item,) notation and just make any item the same thing as
the one-tuple holding that item.


Alas, that's ML, not Python. Were that Python's designers'
intent, why isn't it part of Python's design? Why would we want
to live within the confines of static typing, but without the
safety and efficiency advantages of a type-checking compiler?

In Python, tuples are immutable, hashable lists. Deal with it.
 
A

Alex Martelli

Andrew Durdin said:
Below is a better (=easier to use) implementation using metaclasses;
I've submitted it to the Cookbook anyway (recipe #303481) despite
being past the deadling. The NamedTuple function is for convenience
(although the example doesn't use it for the sake of explicitness).

Actually I haven't received a snapshot from ActiveState yet, so I
suspect anything posted to them until they open for business on Monday
(assuming Canada doesn't rest on Labor Day) should get in, anyway.
So, thanks!

Remember that a comment on an existing recipe is as good as a whole new
recipe from my POV (better, if it means I don't have to work hard to
merge multiple recipes into one...;-) -- anybody whose material we use
in the printed cookbook gets credited as an author, whether the material
came as a recipe or as a comment!-)


Alex
 
A

Alex Martelli

Donn Cave said:
Quoth (e-mail address removed) (Alex Martelli):
| ...
|> On the other hand, we normally use tuples for data that
|> is meaningful only when it's intact. The (key, value)
|
| So by this argument len(t) should not work if t is a tuple...

I expect it's used relatively infrequently, and for different
reasons. "if len(info) == 5", for example - just from that
line from a relatively popular Python application, would you
guess info is a list, or a tuple?

No idea -- could just as well be a dict, an array.array, whatever.
That's the very point and the beauty of polymorphism - I don't CARE what
kind of container 'info' is, I know by this snippet that we're testing
if it has exactly 5 items, or not.

| Even for a pair I sometimes like to know if 42 is the key, the value,
| or neither. index is handy for that... but not if the pair is a tuple,
| only if it's a list. Rationalize as you will, it's still a Python wart.

Maybe the problem is that tuples have too many features already.
It's sort of silly that they're indexed by number, and if that
weren't allowed, we would find fewer people trying to make lists
of them.

Hmmm, how would you access a specific item (since tuples currently don't
support item access by name) if not by indexing?

| Pseudotuples with NAMED (as well as indexed) arguments, as modules stat
| and time now return, may be a different issue. Not sure why we never
| made declaring such pseudotuples as usertypes as easy as it should be, a
| custom metaclass in some stdlib module shd be enough. But tuples whose
| items can't be named, just indexed or sliced, just are not a good fit
| for the kind of use case you and Guido use to justify tuple's lack of
| methods, IMHO.

There you go, they shouldn't be indexed or sliced, that's right!
Named attributes would be nice, but otherwise you use pattern
matching (to the extent support in Python -- key, value = item.)
Makes for more readable code.

Not for sufficiently long tuples. Take the 9-item tuples that the time
module used to use before they grew names: having to unpack the tuple to
access a single item of it would be exceedingly tedious and heavily
boilerplatey to boot.

Besides, I _do_ need to have immutable sequences that are suitable as
dict keys. Today, x=tuple(mylist) performs that role admirably. So,
say I have a dict d, indexed by such tuples -- of different lengths --
and I want all the keys into d that have 23 as their first item. Today
this is a trivial task -- but if I couldn't index tuples I WOULD have a
problem... basically I would need "frozen lists" (and "frozen dicts"
where I today use tuple(d.iteritems())...). Today, tuples serve all of
these roles -- poor man's structs (lacking names), immutable 'frozen'
lists for dict-keying roles, etc. We need at least two new builtin
types to take their place if we want to remove tuple indexing...


Alex
 
A

Alex Martelli

Roy Smith said:
I asked:
How is a class instance's mutability any less of disqualifier for
key-ness than a list's mutability?

Benjamin Niemann said:
a = [1, 2, 3]
b = [1, 2, 3]
if a == b:
print "List equality is based on content"

Tuple (and string) equality is based on content too. So what? I can
give my data class an __eq__ method, and then my class instance equality
would also based on content.

And your class's instances wouldn't then be hashable any more unless
they defined a __hash__ method -- have you tried?

So, to restate my original question, why should my mutable,
content-based-eqality class instance be a valid dictionary key, when a
list is not? Which part of a list's behavior makes it inherently
unusable as a key? I'm not asking about design philosophy, I'm asking
about observable behavior.

This was discussed in detail in another thread about 2 days ago, I
believe. That thread started by somebody asking why modules were
hashable (could be keys in a dictionary).


Alex
 

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
473,754
Messages
2,569,521
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top