[Tutor] access class through indexing?

Alex Hall mehgcap at gmail.com
Wed Aug 4 23:28:03 CEST 2010


On 8/4/10, Evert Rol <evert.rol at 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)
mehgcap at gmail.com; http://www.facebook.com/mehgcap


More information about the Tutor mailing list