From: Carl Banks on
On Mar 31, 2:02 am, Rob Williscroft <r...(a)rtw.me.uk> wrote:
> Frank Millman wrote in news:mailman.1360.1270018159.23598.python-
> l...(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

Careful, this adds the new attributes as to the object's __dict__, not
to the tuple item slots. Which works ok if you don't care if that
someone can mutate the attributes, and if they do the attributes no
longer match the items. Personally I do mind.

tup = MyTuple(names,values)
assert tup.A == 'a'
assert tup[0] == 'a'
tup.A = 'd' # doesn't fail but should
assert tup.A == 'd'
assert tup[0] == 'd' # fails but, granted you allow mutabilty,
shouldn't



The best way to do what the OP wanted (originally) is this, no
subclassing necessary:

def my_named_tuple(names,values):
return namedtuple('ad_hoc_named_tuple',names)(*values)


As for the OP's second problem, to append named items, I'd first
consider whether I'm thinking about the problem correctly, and if so,
go with a subclass of list and overriding __getattr__. Probably one
of the rare cases I would override __getattr__ other than a proxy
class.


Carl Banks
From: Hrvoje Niksic on
"Frank Millman" <frank(a)chagford.com> writes:

>>>> class MyList(list):
> ... def __new__(cls, names, values):
> ... for name, value in zip(names, values):
> ... setattr(cls, name, value)
> ... return list.__new__(cls, values)

Did you really mean to setattr the class here? If I'm guessing
your intentions correctly, it should be:

def __new__(cls, names, values):
self = list.__new__(cls, values)
for name, value in zip(names, values):
setattr(self, name, value)
return self

> 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.

Because you didn't define __init__, so MyList inherited the one from
list, and it has a different signature to what you're calling it with.
Simply add an __init__ with the proper signature, and the proper upcall
to list.__init__:

def __init__(self, names, values):
list.__init__(self, values)

But if you're doing that, you don't need __new__ at all. Simply
override __init__ and place your setattr loop there:

>>> class MyList(list):
.... def __init__(self, names, values):
.... for name, value in zip(names, values):
.... setattr(self, name, value)
.... list.__init__(self, values)
....
>>> MyList(['a'], [1])
[1]
>>> _.a
1
From: Frank Millman on
On Mar 31, 8:49 am, "Frank Millman" <fr...(a)chagford.com> wrote:
> Hi all

Thanks to all for the helpful replies.

Rob, you are correct, I had not realised I was adding attributes to the
class instead of the instance. Your alternative does work correctly. Thanks.

Carl, I understand your concern about modifying attributes. In my particular
case, this is not a problem, as the class is under my control, and an
instance will not be modified once it is set up, but I agree one must be
careful not to mis-use it.

My use-case is that I want to create a number of objects, I want to store
them in a tuple/list so that I can retrieve them sequentially, and I also
want to retrieve them individually by name. Normally I would create a tuple
and a dictionary to serve the two purposes, but I thought this might be a
convenient way to get both behaviours from the same structure.

Regarding adding elements after instantiation, I would subclass 'list', as
suggested by others, and then add an 'add' method, like this -

def add(self, name, value):
setattr(self, name, value)
self.append(value)

I tested this and it behaves as I want.

Having said all of this, I have realised that what I probably want is an
ordered dict. I will play with the one in PyPi, hopefully it will render
this entire discussion redundant. It was interesting, though, and I have
learned a lot.

Thanks again

Frank