On 8/4/10, Evert Rol <evert....@gmail.com> wrote: >>>> Further to my questions about overriding builtin methods earlier, how >>>> would I make a class able to be accessed and changed using index >>>> notation? For example, take the following: >>>> deck=CardPile(52) #creates a new deck of cards >>>> print(len(deck)) #prints 52, thanks to my __len__ function >>>> for c in deck: print c #also works thanks to __iter__ >>>> print(deck[4]) #fails with a list index out of range error >>>> How would I get the last one working? I tried __getattr__(self, i), >>>> but it did not work. I want to be able to get an arbitrary item from >>>> the "pile" of cards (which can be a deck, a hand, whatever), and/or >>>> set an element. A "pile" is just a list of Card objects, so I would >>>> only need to use sequence indexing, not mapping functions. >>>> >>> >>> Implement __getitem__(self, key) (See >>> http://docs.python.org/reference/datamodel.html#emulating-container-types >>> ) >>> >>> If you want to support slicing (access like deck[0:10]), you'll need to >>> handle getting a slice object as the key in addition to accepting an >>> integer >>> key. >>> >>> If a pile is really "just" a list of cards, you may want to look into >>> inheriting from list instead of re-implementing all of the functionality >>> on >>> your own. >> I tried this first, by typing >> class Pile(list): >> Doing this does not seem to work, though, since creating a pile of >> size 52 results in a list of size 0, unless I include the __len__ >> function. I thought putting (list) in my class definition would >> automatically give me the functions of a list as well as anything I >> wanted to implement, but that does not seem to be the case. > > That depends how you create the Pile of 52 cards: list(52) also doesn't > generate 52 (random) items. > If you override __init__ to accept an integer that generates the cards for > you, this should work. Here is my init, not half as pretty as yours, but it should work. Maybe this will explain the problem.
def __init__(self, size, cards=None, fill=False): #creates a pile of cards. If "fill"==true, it will auto-fill the pile starting from the Ace of Clubs up through the King of Spades, stopping if it exceeds the size arg. #if the cards arg is not null, it will populate the pile with the cards in the list. self=[] if fill: #auto-fill, useful to generate a new, unshuffled deck for i in range(1, 14): for j in range(1, 5): self.append(Card(i, j)) #end for #end for self=self[:size] #keep only the amount specified elif cards is not None: #fill the pile with the cards for c in cards: self.append(c) #end for #end if #end def __init__ > Something like: > > class Pile(list): > def __init__(self, *args, **kwargs): > if len(args) == 1 and isinstance(args[0], (int, long)): > args = ([Card(i) for i in xrange(args[0])],) > super(Pile, self).__init__(*args, **kwargs) Why call super here, if it is already my own __init__? > > allows things like Pile(52), Pile([card1, card2, card3]), Pile(12)[0:3] etc. Again, my init is not nearly so fancy, and I will have to look hard at what you did to understand it, but they are the same in terms of class structure/inheriting list attributes as far as I can see, except the call to the super.__init__ method. > > Cheers, > > Evert > > -- Have a great day, Alex (msg sent from GMail website) mehg...@gmail.com; http://www.facebook.com/mehgcap _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor