+=, -=, *=, /=
random.shuffle() Function
sort() List Method
join() List Method
%s
In this chapter you will learn a few new methods and functions that come with Python. You will also learn about
Bagels is a simple game you can play with a friend. Your friend thinks up a random 3-digit number with no repeating digits, and you try to
You can also get multiple clues after each
I am thinking of a 3-digit number. Try to guess what it is. Here are some clues: When I say: That means: Pico One digit is correct but in the wrong position. Fermi One digit is correct and in the right position. Bagels No digit is correct. I have thought up a number. You have 10 guesses to get it. Guess #1:
123
Fermi Guess #2:
453
Pico Guess #3:
425
Fermi Guess #4:
326
Bagels Guess #5:
489
Bagels Guess #6:
075
Fermi Fermi Guess #7:
015
Fermi Pico Guess #8:
175
You got it! Do you want to play again? (yes or no)
no
bagels.py
This code can be downloaded from http://inventwithpyt.hon.com/bagels.py
If you get errors after typing this code in, compare it to the book's code with the online
diff tool at http://inventwithpython.com/diff or email the author at
al@inventwithpython.com
1. import random
2. def getSecretNum(numDigits):
3. # Returns a string that is numDigits long, made up of unique random digits.
4. numbers = list(range(10))
5. random.shuffle(numbers)
6. secretNum = ''
7. for i in range(numDigits):
8. secretNum += str(numbers[i])
9. return secretNum
10.
11. def getClues(guess, secretNum):
12. # Returns a string with the pico, fermi, bagels clues to the user.
13. if guess == secretNum:
14. return 'You got it!'
15.
16. clue = []
17.
18. for i in range(len(guess)):
19. if guess[i] == secretNum[i]:
20. clue.append('Fermi')
21. elif guess[i] in secretNum:
22. clue.append('Pico')
23. if len(clue) == 0:
24. return 'Bagels'
25.
26. clue.sort()
27. return ' '.join(clue)
28.
29. def isOnlyDigits(num):
30. # Returns True if num is a string made up only of digits. Otherwise returns False.
31. if num == '':
32. return False
33.
34. for i in num:
35. if i not in '0 1 2 3 4 5 6 7 8 9'.split():
36. return False
37.
38. return True
39.
40. def playAgain():
41. # This function returns True if the player wants to play again, otherwise it returns False.
42. print('Do you want to play again? (yes or no)')
43. return input().lower().startswith('y')
44.
45. NUMDIGITS = 3
46. MAXGUESS = 10
47.
48. print('I am thinking of a %s-digit number. Try to guess what it is.' % (NUMDIGITS))
49. print('Here are some clues:')
50. print('When I say: That means:')
51. print(' Pico One digit is correct but in the wrong position.')
52. print(' Fermi One digit is correct and in the right position.')
53. print (' Bagels No digit is correct.')
54 .
55. while True:
56. secretNum = getSecretNum(NUMDIGITS)
57. print('I have thought up a number. You have %s guesses to get it.' % (MAXGUESS))
58 .
59. numGuesses = 1
60. while numGuesses <= MAXGUESS:
61. guess = ''
62. while len(guess) != NUMDIGITS or not isOnlyDigits (guess):
63. print('Guess #%s: ' % (numGuesses))
64. guess = input()
65 .
66. clue = getClues(guess, secretNum)
67. print(clue)
68. numGuesses += 1
69 .
70. if guess == secretNum:
71. break
72. if numGuesses > MAXGUESS:
73. print('You ran out of guesses. The answer was %s.' % (secretNum))
74 .
75. if not playAgain() :
76. break
Here is a flow chart for this program. The flow chart describes the basic events of what happens in this game, and in what order they can happen:
(рис 11.1) Flow chart for the Bagels game.
And here is the source code for our game. Start a new file and type the code in, and then save the file as bagels.py. We will design our game so that it is very easy to change the size of the secret number. It can be 3 digits or 5 digits or 10 digits. We will do this by using a NUMDIGITS instead of hard-coding the integer 3 into our source code.
Hard-coding means writing a program in a way that it changing the behavior of the program requires changing a lot of the source code. For example, we could hard-code a name into a print() function call like: print('Hello, Albert'). Or we could use this line: print('Hello, ' + name) which would let us change the name that is printed by changing the name variable while the program is running.
At the start of the program we import the random module and also create a function for generating a random secret number for the player to
1. import random
This game imports the random module so we can use the module's
2. def getSecretNum(numDigits): 3. # Returns a string that is numDigits long, made up of unique random digits. 4. numbers = list(range(10)) 5. random.shuffle(numbers)
Our first function is named getSecretNum(), which will generate the random secret number. Instead of having the code only produce 3-digit numbers, we use a parameter named numDigits to tell us how many digits the secret number should have. (This way, we can make the game produce secret numbers with four or six digits, for example, just by passing 4 or 6 as numDigits.)
You may have noticed that the return value of our call to range() was in turn passed to a function called list(). The list() function returns a list value of the value passed to it, much like the str() function returns a string form or the int() function returns an integer form. The reason we do this is because the range() function technically does not return a list but something called an iterator object. Iterators are a topic that you don't need to know at this point, so they aren't covered in this book.
Just about every time we use the range() function it is in a for for loops (just like lists are), but if we ever want to store a list of integers in a variable, be sure to convert the return value of range() to a list with the list() function first. (Just like we do on line 4.)
First, we create a list of integers 0 to 9 by calling list(range(10)) and store a reference to this list in numbers. Then we call a function in the random module named shuffle(). The only parameter to random.shuffle() is a reference to a list. The shuffle() function will randomly change the order of all the items in the list.
Notice that random.shuffle() does not return a value. It changes the list you pass it "in place" (just like our makeMove() function in the numbers = random.shuffle(numbers).
Try experimenting with the random.shuffle() function by entering the following code into the interactive shell:
>>> import random >>> spam = list(range(10)) >>> print(spam) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> random.shuffle(spam) >>> print(spam) [1, 2, 5, 9, 4, 7, 0, 3, 6, 8] >>> random.shuffle(spam) >>> print(spam) [3, 0, 5, 9, 6, 8, 2, 4, 1, 7] >>> random.shuffle(spam) >>> print(spam) [9, 8, 3, 5, 4, 7, 1, 2, 0, 6] >>>
Every time you pass a list reference to random.shuffle(), the list it references has all the same items but in a different order. The reason we do this is because we want the secret number to have unique values. The Bagels game is much more fun if you don't have duplicate numbers in the secret number, such as '244' or '333'.
6. secretNum = '' 7. for i in range(numDigits): 8. secretNum += str(numbers[i]) 9. return secretNum
The secret number will be a string of the first three digits (because we'll pass 3 for the numDigits parameter) of the shuffled list of integers. For example, if the shuffled list is [9, 8, 3, 5, 4, 7, 1, 2, 0, 6] then we want the string returned by getSecretNum() to be '983'.
The secretNum variable starts out as a blank string. We then loop a number of times equal to the integer value in numDigits. On each iteration through the loop, a new integer is pulled from the shuffled list, converted to a string, and concatenated to the end of secretNum. So if numDigits is 3, the loop will iterate three times and three random digits will be concatenated as strings.
For example, if numbers refers to the list [9, 8, 3, 5, 4, 7, 1, 2, 0, 6], then on the first iteration, numbers[0] (that is, 9) will be passed to str(), which in turn returns '9' which is concatenated to the end of secretNum. On the second iteration, the same happens with numbers[1] (that is, 8) and on the third iteration the same happens with numbers[2] (that is, 3). The final value of secretNum that is returned is '983'.
You may notice that secretNum in this function is a string, not an integer. This may seem odd, but remember that our secret number could be something like '012'. If we stored this as an integer, it would be 12 (without the leading zero) which would make it harder to work with in our program.
The += operator on line 8 is new. This is one of the
spam = 42 spam = spam + 10 cheese = 'Hello ' cheese = cheese + 'world!'
After running the above code, spam would have the value 52 and cheese would have the value 'Hello world!'. The
spam = 42 spam += 10 # Same as spam = spam + 10 cheese = 'Hello ' cheese += 'world!' # Same as cheese = cheese + 'world!'
There are other -= will subtract a value from an integer. *= will multiply the variable by a value. /= will divide a variable by a value. Notice that these -, *, and / operators.
We also need a way of figuring out which clues to show to the player.
11. def getClues(guess, secretNum) : 12. # Returns a string with the pico, fermi, bagels cluesto the user. 13. if guess == secretNum: 14. return 'You got it!'
The getClues() function will return a string with the fermi, pico, and bagels clues, depending on what it is passed for the and secretNum parameters. The most obvious and easiest step is to check if the 'You got it!'.
16 . clue = []
17 .
18. for i in range(len(guess)):
19. if guess[i] == secretNum[i]:
20. clue.append('Fermi')
21. elif guess[i] in secretNum:
22. clue.append('Pico')
If the clue, which we will add the strings 'Fermi' and 'Pico' as needed. We will combine the strings in this list into a single string to return.
We do this by looping through each possible index in and secretNum (they both are the same size). We will assume that and secretNum are the same size. As the value of i changes from 0 to 1 to 2, and so on, the if statement checks if the first, second, third, etc. letter of is the same as the number in the same position in secretNum. If so, we will add a string 'Fermi' to clue.
If that condition is False we will check if the number at the ith position in exists anywhere in secretNum. If this condition is True we know that the number is somewhere in the secret number but not in the same position. This is why we add the 'Pico' to clue.
23. if len(clue) == 0: 24. return 'Bagels'
If we go through the entire for loop above and never add anything to the clue list, then we know that there are no correct digits at all in . In this case, we should just return the string 'Bagels' as our only clue.
26. clue.sort()
Lists have a method named sort() that
>>> spam = [5, 3, 4, 1, 2] >>> spam.sort() >>> spam [1, 2, 3, 4, 5]
Notice that the sort() method does not return a sorted list, but rather just sorts the list it is called on "in place". This is much like how the reverse() method works. You would never want to use this line of code: return spam.sort() because that would return the value None (which is what sort() returns). Instead you would want a separate line spam.sort() and then the line return spam.
The reason we want to sort the clue list is because we might return extra clues that we did not intend based on the order of the clues. If clue referenced the list ['Pico', 'Fermi', 'Pico'], then that would tell us that the center digit of our
27. return ' '.join(clue)
The join() string method returns a string of each item in the list argument joined together. The string that the method is called on (on line 27, this is a single space, ' ') appears in between each item in the list. So the string that is returned on line 27 is each string in clue combined together with a single space in between each string.
For an example, enter the following into the interactive shell:
>>> 'x'.join(['hello', 'world']) 'helloxworld' >>> 'ABCDEF'.join(['x', 'y', 'z']) 'xABCDEFyABCDEFz' >>> ' '.join(['My', 'name', 'is', 'Zophie']) 'My name is Zophie'
We need a couple more functions for our game to use. The first is a function that will tell us if the input() function returns a string of whatever the player typed in. If the player enters in anything but numbers for their
The second function is something we've seen before in previous games. We want a function that will ask the player if they want to play the game again and from the player's response, figure out if it was a Yes or No answer.
29. def isOnlyDigits(num): 30. # Returns True if num is a string made up only of digits. Otherwise returns False. 31. if num == '': 32. return False
The isOnlyDigits() is a small function that will help us determine if the player entered a num and make sure it is a number.
Line 31 does a quick check to see if we were sent the blank string, and if so, we return False.
34. for i in num: 35. if i not in '0 1 2 3 4 5 6 7 8 9' .split() : 36. return False 37 . 38. return True
We use a for loop on the string num. The value of i will have a single character from the num string on each iteration. Inside the for-block, we check if i does not exist in the list returned by '0 1 2 3 4 5 6 7 8 9'.split(). If it doesn't, we know that there is a character in num that is something besides a number. In that case, we should return the value False.
If execution continues past the for loop, then we know that every character in num is a number because we did not return out of the function. In that case, we return the value True.
40. def playAgain() :
41. # This function returns True if the player wants to play again, otherwise it returns False.
42. print('Do you want to play again? (yes or no)')
43. return input() .lower() .startswith('y')
The playAgain() function is the same one we used in Hangman and True or False. The return value from the call to the input() function is a string that has its lower() lower() method returns another string (the lowercase string) and that string has its startswith() 'y'.
45. NUMDIGITS = 3
46. MAXGUESS = 10
47.
48. print('I am thinking of a %s-digit number. Try to guess what it is.' % (NUMDIGITS))
49. print('Here are some clues:')
50. print('When I say: That means:')
51. print(' Pico One digit is correct but in the wrong position.')
52. print(' Fermi One digit is correct and in the right position.')
53. print (' Bagels No digit is correct.')
This is the actual start of the program. Instead of hard-coding three digits as the size of the secret number, we will use the NUMDIGITS. And instead of hard-coding a maximum of ten guesses that the player can make, we will use the MAXGUESS. (This is because if we increase the number of digits the secret number has, we also might want to give the player more guesses. We put the variable names in all capitals to show they are meant to be constant.)
The print() function call will tell the player the rules of the game and what the Pico, Fermi, and Bagels clues mean. Line 48's print() call has % (NUMDIGITS) added to the end and %s inside the string. This is a technique know as string
String +
>>> name = 'Alice'
>>> event = 'party'
>>> where = 'the pool'
>>> when = 'Saturday'
>>> time = '6:00pm'
>>> print('Hello, ' + name + '. Will you go to the
' + event + ' at ' + where + ' this ' + when + '
at ' + time + '?')
Hello, Alice. Will you go to the party at the pool
this Saturday at 6:00pm?
>>>
As you can see, it can be very hard to type a line that concatenates several strings together. Instead, you can use string interpolation, which lets you put placeholders like %s (these placeholders are called conversion specifiers), and then put all the variable names at the end. Each %s is replaced with the value in the variable at the end of the line. For example, the following code does the same thing as the above code:
name = 'Alice'
event = 'party'
where = 'the pool'
when = 'Saturday'
time = '6:00pm'
print('Hello, %s. Will you go to the %s at %s this
%s at %s?' % (name, event, where, when, time))
Hello, Alice. Will you go to the party at the pool
this Saturday at 6:00pm?
>>>
String + concatenation operators.
The final line has the print() call with a string with conversion specifiers, followed by the % sign, followed by a set of name will be used for the first %s, the second variable with the second %s and so on. The Python interpreter will give you an error if you do not have the same number of %s conversion specifiers as you have variables.
Another benefit of using string %s stands for.) If you typed this code into the shell, you'd get an error:
>>> spam = 42
>>> print('Spam == ' + spam)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str
implicitly
>>>
You get this error because spam is an integer. You would have to remember to put str(spam) in there instead. But with string
>>> spam = 42
>>> print('Spam == %s' % (spam))
Spam == 42
>>>
As you can see, using string print() function calls.
String
Now that the program has displayed the rules to Bagels to the player, the program will randomly create a secret number and then enter a loop where it repeatedly asks for the player's guesses until she has either correctly guessed the secret number, or has run out of guesses. After that, we will ask the player if she wants to play again.
55. while True:
56. secretNum = getSecretNum(NUMDIGITS)
57. print('I have thought up a number. You have %s guesses to get it.' % (MAXGUESS))
58.
59. numGuesses = 1
60. while numGuesses <= MAXGUESS:
We start with a while loop that has a condition of True, meaning it will loop forever until we execute a break statement. Inside the infinite loop, we get a secret number from our getSecretNum() function (passing it NUMDIGITS to tell how many digits we want the secret number to have) and assign it to secretNum. Remember that secretNum is a string, not an integer.
We tell the player how many digits is in our secret number by using string numGuesses to 1, to denote that this is the first while loop which will keep looping as long as numGuesses is less than or equal to MAXGUESS.
Notice that this second while loop on line 60 is inside another while loop that started on line 55. Whenever we have these loops-inside-loops, we call them nested loops. You should know that any break or continue statements will only break or continue out of the innermost loop, and not any of the outer loops.
61. guess = ''
62. while len(guess) != NUMDIGITS or not isOnlyDigits (guess) :
63. print('Guess #%s: ' % (numGuesses))
64. guess = input ()
The variable will hold the player's while loop that starts on line 62 is for. We set while loop's condition is False the first time, ensuring that we enter the loop at least once.
66. clue = getClues(guess, secretNum) 67. print(clue) 68. numGuesses += 1
After execution gets past the while loop on line 62, we know that contains a valid secretNum to our getClues() function. It returns a string that contains our clues, which we will display to the player. We then increment numGuesses by 1 using the
70. if guess == secretNum:
71. break
72. if numGuesses > MAXGUESS:
73. print('You ran out of guesses. The answer was %s.' % (secretNum))
If is the same value as secretNum, then we know the player has correctly guessed the secret number and we can break out of this loop (the while loop that was started on line 60). If not, then execution continues to line 72, where we check to see if the player ran out of guesses. If so, then we tell the player that they have lost and what the secret number was. We know that the condition for the while loop on line 55 will be False, so there is no need for a break statement.
At this point, execution jumps back to the while loop on line 60 where we let the player have another break statement on line 71), then execution would proceed to line 75.
75. if not playAgain() : 76. break
After leaving the while loop on line 60, we ask the player if want to play again by calling our playAgain() function. If playAgain() returns False, then we should break out of the while loop that was started on line 55. Since there is no more code after this loop, the program terminates.
If playAgain() returned True, then we would not execute the break statement and execution would jump back to line 55. A new secret number would be generated so that the player can play a new game.
Bagels is a fairly simple game to program but can be difficult to win at. But if you keep playing, you will eventually discover better ways to
This chapter introduced a few new functions and methods (random.shuffle(), sort (), and join()), along with a couple handy shortcuts. Using the spam = spam + 1, which can be shortend to spam += 1). String %s (called a conversion specifier) inside the string instead of using many
The join() string method is passed a list of strings that will be concatenated together, with the original associated string in between them. For example, 'X'.join ( ['hello', 'world', 'yay'] ) will evaluate to the string, 'helloXworldXyay'.
The sort() list method will rearrange the items in the list to be in
The append() list method will add a value to the end of the associated list. If spam contains the list ['a', 'b', 'c'], then calling spam.append('d') will change the list in spam to be ['a', 'b', 'c', 'd'].
The next chapter is not about programming directly, but will be necessary for the games we want to create in the later chapters of this book. We will learn about the math concepts of Cartesian coordinates and negative numbers. These will be used in the Sonar, Reversi, and Dodger games, but Cartesian coordinates and negative numbers are used in almost all games (especially graphical games). If you already know about these concepts, give the next chapter a brief reading anyway just to freshen up. Let's dive in!
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.