Returning a tuple-struct

  • Thread starter groups.20.thebriguy
  • Start date
G

groups.20.thebriguy

I've noticed that there's a few functions that return what appears to
be a tuple, but that also has attributes for each item in the tuple.
For example, time.localtime() returns a time.time_struct, which looks
like a tuple but also like a struct. That is, I can do:
time.localtime() (2006, 1, 18, 21, 15, 11, 2, 18, 0)
time.localtime()[3] 21
time.localtime().tm_hour
21

Anyway, I guess there's a few of ways to do this. In the case above,
it would seem reasonable to override __getitem__() and other things to
get that result.

To my question... It seems like a useful but very simple way to
accomplish the above (that is, to have your return value accessible as
both a sequence and a struct) is to subclass tuple. Something like
this:

def foo():
class NewTuple(tuple): pass
x = NewTuple((1,2))
x.a, x.b = x
return x

And so I can do:

x = foo()
print x
print x.a
print x.b

And the result is:

(1, 2)
1
2

So, the question I have is just a style and/or pattern question...
Does anyone do this? Does is seem reasonably intuitive, or ugly? Is
there a better way? Thoughts?

-bri
 
J

James Stroud

I've noticed that there's a few functions that return what appears to
be a tuple, but that also has attributes for each item in the tuple.
For example, time.localtime() returns a time.time_struct, which looks
like a tuple but also like a struct. That is, I can do:


(2006, 1, 18, 21, 15, 11, 2, 18, 0)
time.localtime()[3]
21
time.localtime().tm_hour

21

Anyway, I guess there's a few of ways to do this. In the case above,
it would seem reasonable to override __getitem__() and other things to
get that result.

To my question... It seems like a useful but very simple way to
accomplish the above (that is, to have your return value accessible as
both a sequence and a struct) is to subclass tuple. Something like
this:

def foo():
class NewTuple(tuple): pass
x = NewTuple((1,2))
x.a, x.b = x
return x

And so I can do:

x = foo()
print x
print x.a
print x.b

And the result is:

(1, 2)
1
2

So, the question I have is just a style and/or pattern question...
Does anyone do this? Does is seem reasonably intuitive, or ugly? Is
there a better way? Thoughts?

-bri

I think stylistically better might be

class NewTuple(tuple):
def __new__(self, atup):
self.a, self.b = atup[:2]
return tuple.__new__(self, atup)

x = NewTuple((1, 2, 3, 4, 5))
print x
print x.a
print x.b
 
G

Giovanni Bajo

time.localtime() (2006, 1, 18, 21, 15, 11, 2, 18, 0)
time.localtime()[3] 21
time.localtime().tm_hour
21

Anyway, I guess there's a few of ways to do this. In the case above,
it would seem reasonable to override __getitem__() and other things to
get that result.


I have a generic solution for this (never submitted to the cookbook... should
I?)


import operator
def NamedTuple(*args, **kwargs):
class named_tuple_class(tuple):
pass

values = []
idx = 0
for arg in args:
for name in arg[:-1]:
setattr(named_tuple_class, name,
property(operator.itemgetter(idx)))
values.append(arg[-1])
idx += 1
for name,val in kwargs.iteritems():
setattr(named_tuple_class, name, property(operator.itemgetter(idx)))
values.append(val)
idx += 1

return named_tuple_class(values)



t = NamedTuple(("x", 12), ("y", 18))
t (12, 18)
t[0] 12
t.x 12
t[1] 18
t.y 18
t.z
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'named_tuple_class' object has no attribute 'z'

'foo'
 
T

Tom Anderson

Is there a better way? Thoughts?

I was thinking along these lines:

class NamedTuple(tuple):
def __init__(self, indices, values):
"indices should be a map from name to index"
tuple.__init__(self, values)
self.indices = indices
def __getattr__(self, name):
return self[self.indices[name]]

colourNames = {"red": 0, "green": 1, "blue":2}
plum = NamedTuple(colourNames, (219, 55, 121))

The idea is that it's a tuple, but it has some metadata alongside (shared
with other similarly-shaped tuples) which allows it to resolve names to
indices - thus avoiding having two references to everything.

However, if i try that, i get:

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: tuple() takes at most 1 argument (2 given)

As far as i can tell, inheriting from tuple is forcing my constructor to
only take one argument. Is that the case? If so, anyone got any idea why?

If i rewrite it like this:

class NamedTuple(tuple):
def __init__(self, values):
tuple.__init__(self, values)
def __getattr__(self, name):
return self[self.indices[name]]

class ColourTuple(NamedTuple):
indices = {"red": 0, "green": 1, "blue":2}

plum = ColourTuple((219, 55, 121))

Then it works. This is even an arguably better style. Changing the
constructor to take *values rather than values, and to validate the length
of the value tuple against the length of the index tuple, would be good,
but, since i'm lazy, is left as an exercise to the reader.

tom
 
T

Tim Roberts

I've noticed that there's a few functions that return what appears to
be a tuple, but that also has attributes for each item in the tuple.
For example, time.localtime() returns a time.time_struct, which looks
like a tuple but also like a struct. That is, I can do:
time.localtime() (2006, 1, 18, 21, 15, 11, 2, 18, 0)
time.localtime()[3] 21
time.localtime().tm_hour
21

Ah, but it ISN'T really a tuple:
'(2006, 1, 21, 22, 49, 32, 5, 21, 0)'

It's a class object. The __repr__ method returns a string that LOOKS the
same as a tuple.
 
J

James Stroud

Tom said:
Is there a better way? Thoughts?


I was thinking along these lines:

class NamedTuple(tuple):
def __init__(self, indices, values):
"indices should be a map from name to index"
tuple.__init__(self, values)
self.indices = indices
def __getattr__(self, name):
return self[self.indices[name]]

colourNames = {"red": 0, "green": 1, "blue":2}
plum = NamedTuple(colourNames, (219, 55, 121))

The idea is that it's a tuple, but it has some metadata alongside
(shared with other similarly-shaped tuples) which allows it to resolve
names to indices - thus avoiding having two references to everything.

However, if i try that, i get:

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: tuple() takes at most 1 argument (2 given)

As far as i can tell, inheriting from tuple is forcing my constructor to
only take one argument. Is that the case? If so, anyone got any idea why?

This error message is not coming from __init__, but from __new__. See

http://www.python.org/2.2/descrintro.html#__new__

James
 

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

Forum statistics

Threads
474,282
Messages
2,571,404
Members
48,096
Latest member
Kenkian2628

Latest Threads

Top