From: Frank Millman on
Hi all

I needed something similar to, but not quite the same as,
collections.namedtuple.

The differences are that namedtuple requires the 'names' to be provided at
creation time, and then lends itself to creating multiple instances of
itself. I wanted a more generic class where I could supply the 'names' and
'values' at instantiation time.

I came up with a simple solution that seems to work -

>>> class MyTuple(tuple):
.... def __new__(cls, names, values):
.... for name, value in zip(names, values):
.... setattr(cls, name, value)
.... return tuple.__new__(cls, values)
....
>>> names = ['A', 'B', 'C']
>>> values = ['a', 'b', 'c']
>>>
>>> tup = MyTuple(names, values)
>>>
>>> print tup
('a', 'b', 'c')
>>>
>>> print tup[0]
a
>>>
>>> print tup.B
b
>>>

Then I had a need to add elements after the tuple had been created. As
tuples are immutable, I thought it would be easy to change it to a list.
However, it does not work -

>>> class MyList(list):
.... def __new__(cls, names, values):
.... for name, value in zip(names, values):
.... setattr(cls, name, value)
.... return list.__new__(cls, values)
....
>>> names = ['A', 'B', 'C']
>>> values = ['a', 'b', 'c']
>>>
>>> lst = MyList(names, values)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list() takes at most 1 argument (2 given)
>>>

I can find a workaround, but I would be interested to know the reason why it
does not work.

Version is 2.6.2.

Thanks

Frank Millman


From: lbolla on
On Mar 31, 7:49 am, "Frank Millman" <fr...(a)chagford.com> wrote:
> Hi all
>
> I needed something similar to, but not quite the same as,
> collections.namedtuple.
>
> The differences are that namedtuple requires the 'names' to be provided at
> creation time, and then lends itself to creating multiple instances of
> itself. I wanted a more generic class where I could supply the 'names' and
> 'values' at instantiation time.
>
> I came up with a simple solution that seems to work -
>
> >>> class MyTuple(tuple):
>
> ...   def __new__(cls, names, values):
> ...     for name, value in zip(names, values):
> ...       setattr(cls, name, value)
> ...     return tuple.__new__(cls, values)
> ...
>
> >>> names = ['A', 'B', 'C']
> >>> values = ['a', 'b', 'c']
>
> >>> tup = MyTuple(names, values)
>
> >>> print tup
> ('a', 'b', 'c')
>
> >>> print tup[0]
> a
>
> >>> print tup.B
> b
>
> Then I had a need to add elements after the tuple had been created. As
> tuples are immutable, I thought it would be easy to change it to a list.
> However, it does not work -
>
> >>> class MyList(list):
>
> ...   def __new__(cls, names, values):
> ...     for name, value in zip(names, values):
> ...       setattr(cls, name, value)
> ...     return list.__new__(cls, values)
> ...>>> names = ['A', 'B', 'C']
> >>> values = ['a', 'b', 'c']
>
> >>> lst = MyList(names, values)
>
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> TypeError: list() takes at most 1 argument (2 given)
>
>
>
> I can find a workaround, but I would be interested to know the reason why it
> does not work.
>
> Version is 2.6.2.
>
> Thanks
>
> Frank Millman

When subclassing immutable types, you need to override __new__;
otherwise you need to override __init__.
Here is an in-depth explanation: http://www.python.org/download/releases/2.2/descrintro/#metaclasses

Here is some code:

class MyTuple(tuple):
def __new__(cls, names, values):
for name, value in zip(names, values):
setattr(cls, name, value)
return tuple.__new__(cls, values)

class MyList(list):
def __init__(self, names, values):
list.__init__(self, values)
for name, value in zip(names, values):
setattr(self, name, value)

names = ['A', 'B', 'C']
values = ['a', 'b', 'c']

tup = MyTuple(names, values)
print tup
print tup[0]
print tup.B

lst = MyList(names, values)
print lst
print lst[0]
print lst.B


L.
From: Frank Millman on

"lbolla" <lbolla(a)gmail.com> wrote in message
news:f8011c0b-0b1b-4a4f-94ff-304c16ef9a5b(a)q16g2000yqq.googlegroups.com...
On Mar 31, 7:49 am, "Frank Millman" <fr...(a)chagford.com> wrote:
>> Hi all
>>

> When subclassing immutable types, you need to override __new__;
> otherwise you need to override __init__.

Perfect. Thanks very much.

Frank


From: Bruno Desthuilliers on
lbolla a �crit :
>
> class MyList(list):
> def __init__(self, names, values):
> list.__init__(self, values)
> for name, value in zip(names, values):
> setattr(self, name, value)
>
> names = ['A', 'B', 'C']
> values = ['a', 'b', 'c']
>
> lst = MyList(names, values)
> print lst
> print lst[0]
> print lst.B
>

>>> lst[0] = "foobar"
>>> lst.A
'a'>>> lst.B = 42
>>> lst[1]
'b'
>>> lst.D="duh"
>>> lst[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range

From: Rob Williscroft on
Frank Millman wrote in news:mailman.1360.1270018159.23598.python-
list(a)python.org in comp.lang.python:

> I came up with a simple solution that seems to work -
>
>>>> class MyTuple(tuple):
> ... def __new__(cls, names, values):
> ... for name, value in zip(names, values):
> ... setattr(cls, name, value)
> ... return tuple.__new__(cls, values)
> ...
>>>> names = ['A', 'B', 'C']
>>>> values = ['a', 'b', 'c']
>>>>
>>>> tup = MyTuple(names, values)
>>>>

Are you aware you are adding attributes to the class here, IOW:

MyTuple.C == 'c'

If you want to add attibutes to the instance:

class MyTuple(tuple):
def __new__(cls, names, values):
r = tuple.__new__(cls, values)
for name, value in zip(names, values):
setattr(r, name, value)
return r

names = ['A', 'B', 'C']
values = ['a', 'b', 'c']

tup = MyTuple(names, values)

assert tup[0] == 'a'
assert tup.B == 'b'

try:
MyTuple.C
except AttributeError:
pass
else:
assert False