From: Carl Banks on 31 Mar 2010 05:38 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 31 Mar 2010 07:07 "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 31 Mar 2010 07:51
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 |