[Tutor] Need a solution.

Alan Gauld alan.gauld at btinternet.com
Sat Jun 13 10:20:36 CEST 2009


"David" <david at abbottdavid.com> wrote 

>> class GuessError(Exception): pass
>> class GuessedNumber:
>>     def __init__(self,tries=None,limits=None):...
>>     def generate(self, limits=None):...
>>     def __cmp__(self, aNumber):
>>           if self.count >= self.tries: raise GuessError
> Thanks always for the feedback, i came up with this, not sure if it is 
> much better but I am learning as I prod along :)

The big problem from an OO point of view is that you are not 
making the objects do any work. You are extracting the data 
from inside them and you are doing all the work in your main 
function. In OOP we should be trying to make the objects do 
everything and the main function just does some high level 
coordination.

If you make your GuessedNumber class have comparison 
methods you can avoid much of that. And if you put the counting 
code inside the comparison that could save a lot of effort in main 
too. Remember that in theory you shold not know what data is 
inside the object. You should only interact with objects via their 
published operations.

> #!/usr/bin/python
> from random import randrange
> from sys import exit
> 
> class GuessedNumber:
>     def __init__(self, attempts=None):
>         self.attempts = attempts
>         self.number = randrange(1,99)

Any time you have a class that just has an __init__ it means 
its not doing anything. And that's a bad sign. Classes are there 
to *do* things not just store data. We can use a tuple or 
dictionary to do that.

> class Counter:
>     def __init__(self):
>         self.value = 0
>     def step(self):
>         self.value += 1
>     def current(self):
>         return self.value

Whilst this is OK,, because it does something, its a lot of 
code to wrap an integer. I personally wouldn't bother. 
But at least it is doing things :-)

> def play():
>     c = Counter()
>     guessit = GuessedNumber(attempts=5)
>     target_number = guessit.number
>     attempts = guessit.attempts

See, here is the problem, you create an object then 
immediately extract all the data and then just ignore 
the object. You might as well just assign the values 
to variables


>     guess = int(raw_input('Guess-> '))
>     c.step()
>     while c.current() < attempts:

So why not 

         while c.current() < guessit.attempts

use the object, thats why its there

>         try:
>             if guess == target_number:

Whereas this could have been 

               if guess == guessIt

>                 print "Well Done"
>                 play_again()
>             elif guess < target_number:

and         elif guess < guessit

>                 print 'Higher ... '
>                 guess = int(raw_input('Guess Again-> '))
>                 c.step()
>             elif guess > target_number:
>                 print 'Lower ... '
>                 guess = int(raw_input('Guess Again-> '))
>                 c.step()
> 
>         except ValueError:
>             print 'You must enter a number'
>             pass
> 
>     print 'Too many attempts, the number was', target_number
>     play_again()
> def play_again():
>     answer = raw_input('Do you want to try again? y/n ')
>     answer = answer.lower()
>     if answer == 'y':
>         play()
>     else:
>         exit()

This recursive approach will work most of the time but 
remember that Python does limit recursion to 1000 levels 
so if your player was very keen you could run out of levels. 
A normal loop would be safer.

HTH,

Alan G.



More information about the Tutor mailing list