Перейти к содержимому

Как сделать варианты ответа в python

  • автор:

Выбор варианта из перечня возможных ответов в Python, как использовать if и elif

Изучаю азы Python, пытаюсь написать простенький текстовый квест. Намертво застрял в следующем моменте: допустим, есть реплика персонажа и тебе нужно выбрать один из нескольких вариантов ответа. В зависимости от твоего выбора, персонаж выдаст тебе следующую реплику.

Пример: "Вася, как твои дела"?

Варианты: a) Нормально б) Хорошо в) Плохо

Выбрали ответ "Нормально", нажав на кнопку ‘a’ — будет ответ "ну понятно" Выбрали "Хорошо", нажав на ‘b’ — будет ответ "ну и замечательно" Ну и далее по той же логике.

Я понимаю, что это реализуется через операторы выбора, но у меня не выходит. Кусок из кода:

Пробовал после if поставить else, но это прокатит только в том случае, если есть не больше одного альтернативного варианта.

Как сделать варианты ответа в Python

В этой статье вы узнаете, как создать варианты ответа в Python с использованием структур данных и условий.

Использование списков

Списки — это упорядоченные коллекции объектов. Мы можем использовать списки для хранения вариантов ответа.

Использование словарей

Словари — это коллекции пар ключ-значение. Мы можем использовать словари для хранения вариантов ответа и соответствующих им действий.

Использование оператора if

Оператор if позволяет сравнивать значения и выполнять различные действия в зависимости от исхода сравнения.

Build a Quiz Application With Python

In this tutorial, you’ll build a Python quiz application for the terminal. The word quiz was first used in 1781 to mean eccentric person. Nowadays, it’s mostly used to describe short tests of trivia or expert knowledge with questions like the following:

When was the first known use of the word quiz?

By following along in this step-by-step project, you’ll build an application that can test a person’s expertise on a range of topics. You can use this project to strengthen your own knowledge or to challenge your friends to a fun battle of wits.

In this tutorial, you’ll learn how to:

  • Interact with the user in the terminal
  • Improve the usability of your application
  • Refactor your application to continuously improve it
  • Store data in dedicated data files

The quiz application is a comprehensive project for anyone comfortable with the basics of Python. Throughout the tutorial, you’ll get all the code you need in separate, bite-size steps. You can also find the full source code of the application by clicking on the link below:

Get Source Code: Click here to get access to the source code that you’ll use to build your quiz application.

Whether you’re an eccentric person or not, read on to learn how to create your own quiz.

Demo: Your Python Quiz Application

In this step-by-step project, you’ll build a terminal application that can quiz you and your friends on a range of topics:

You first choose a topic for your questions. Then, for each question, you’ll choose an answer from a set of alternatives. Some questions may have multiple correct answers. You can access a hint to help you along the way. After answering a question, you’ll read an explanation that can provide more context for the answer.

Project Overview

You’ll start by creating a basic Python quiz application that’s only capable of asking a question, collecting an answer, and checking whether the answer is correct. From there, you’ll add more and more features in order to make your app more interesting, user-friendly, and fun.

You’ll build the quiz application iteratively by going through the following steps:

  1. Create a basic application that can ask multiple-choice questions.
  2. Make the app more user-friendly by improving how it looks and how it handles user errors.
  3. Refactor the code to use functions.
  4. Separate question data from source code by storing questions in a dedicated data file.
  5. Expand the app to handle multiple correct answers, give hints, and provide explanations.
  6. Add interest by supporting different quiz topics to choose from.

As you follow along, you’ll gain experience in starting with a small script and expanding it. This is an important skill in and of itself. Your favorite program, app, or game probably started as a small proof of concept that later grew into what it is today.

Prerequisites

In this tutorial, you’ll build a quiz application using Python’s basic building blocks. While working through the steps, it’s helpful if you’re comfortable with the following concepts:

    from the user at the terminal
  • Organizing data in structures like lists, tuples, and dictionaries
  • Using if statements to check different conditions
  • Repeating actions with for and while loops
  • Encapsulating code with functions

If you’re not confident in your knowledge of these prerequisites, then that’s okay too! In fact, going through this tutorial will help you practice these concepts. You can always stop and review the resources linked above if you get stuck.

Step 1: Ask Questions

In this step, you’ll learn how to create a program that can ask questions and check answers. This will be the foundation of your quiz application, which you’ll improve upon in the rest of the tutorial. At the end of this step, your program will look like this:

Your program will be able to ask questions and check answers. This version includes the basic functionality that you need, but you’ll add more functionality in later steps. If you prefer, then you can download the source code as it’ll look when you’re done with this step by clicking the link below and entering the source_code_step_1 directory:

Get Source Code: Click here to get access to the source code that you’ll use to build your quiz application.

Get User Information With input()

One of Python’s built-in functions is input() . You can use it to get information from the user. For a first example, run the following in a Python REPL:

input() takes an optional prompt that’s displayed to the user before the user enters information. In the example above, the prompt is shown in the highlighted line, and the user enters Geir Arne before hitting Enter . Whatever the user enters is returned from input() . This is seen in the REPL example, as the string ‘Geir Arne’ has been assigned to name .

You can use input() to have Python ask you questions and check your answers. Try the following:

This example shows one thing that you need to be aware of: input() always returns a text string, even if that string contains only digits. As you’ll soon see, this won’t be an issue for the quiz application. However, if you wanted to use the result of input() for mathematical calculations, then you’d need to convert it first.

Time to start building your quiz application. Open your editor and create the file quiz.py with the following content:

This code is very similar to what you experimented with in the REPL above. You can run your application to check your knowledge:

If you happen to give the wrong answer, then you’ll be gently corrected so that you’ll hopefully do better next time.

Note: The f before your quoted string literal inside the else clause indicates that the string is a formatted string, usually called an f-string. Python evaluates expressions inside curly braces ( <> ) within f-strings and inserts them into the string. You can optionally add different format specifiers.

For example, !r indicates that answer should be inserted based on its repr() representation. In practice, this means that strings are shown surrounded by single quotes, like ‘1871’ .

A quiz with only one question isn’t very exciting! You can ask another question by repeating your code:

You’ve added a question by copying and pasting the previous code then changing the question text and the correct answer. Again, you can test this by running the script:

It works! However, copying and pasting code like this isn’t great. There’s a programming principle called Don’t Repeat Yourself (DRY), which says that you should usually avoid repeated code because it gets hard to maintain.

Next, you’ll start improving your code to make it easier to work with.

Use Lists and Tuples to Avoid Repetitive Code

Python provides several flexible and powerful data structures. You can usually replace repeated code with a tuple, a list, or a dictionary in combination with a for loop or a while loop.

Instead of repeating code, you’ll treat your questions and answers as data and move them into a data structure that your code can loop over. The immediate—and often challenging—question then becomes how you should structure your data.

There’s never one uniquely perfect data structure. You’ll usually choose between several alternatives. Throughout this tutorial, you’ll revisit your choice of data structure several times as your application grows.

For now, choose a fairly simple data structure:

  • A list will hold several question elements.
  • Each question element will be a two-tuple consisting of the question text and the answer.

You can then store your questions as follows:

This fits nicely with how you want to use your data. You’ll loop over each question, and for each question, you want access to both the question and answer.

Change your quiz.py file so that you store your questions and answers in the QUESTIONS data structure:

When you run this code, it shouldn’t look any different from how it did earlier. In fact, you haven’t added any new functionality. Instead, you’ve refactored your code so that it’ll be easier to add more questions to your application.

In the previous version of your code, you needed to add five new lines of code for each question that you added. Now, the for loop takes care of running those five lines for each question. To add a new question, you only need to add one line spelling out the question and the corresponding answer.

Note: You’re learning about quizzes in this tutorial, so questions and answers are important. Each code example will introduce a new question. To keep the code listings in this tutorial at a manageable size, some of the older questions may be removed. However, feel free to keep all questions in your code, or to even replace these with your own questions and answers.

The questions you’ll see in the examples are related to the tutorial, even though you won’t find all the answers in the text. Feel free to search online if you’re curious about more details about a question or an answer.

Next, you’ll make your quiz application easier to use by adding answer alternatives for each question.

Provide Multiple Choices

Using input() is a great way to read input from your user. However, the way you’re currently using it can end up being frustrating. For example, someone may answer one of your questions like this:

Should they really be marked wrong because they included the parentheses to indicate that the function is callable? You can take away a lot of guesswork for the users by giving them alternatives. For example:

Here, the alternatives show that you expect the answer to be entered without parentheses. In the example, the alternatives are listed before the question. This is a bit counterintuitive, but it’s easier to implement into your current code. You’ll improve this in the next step.

In order to implement answer alternatives, you need your data structure to be able to record three pieces of information for each question:

  1. The question text
  2. The correct answer
  3. Answer alternatives

It’s time to revisit QUESTIONS for the first—but not the last—time and make some changes to it. It makes sense to store the answer alternatives in a list, as there can be any number of them and you just want to display them to the screen. Furthermore, you can treat the correct answer as one of the answer alternatives and include it in the list, as long as you’re able to retrieve it later.

You decide to change QUESTIONS to a dictionary where the keys are your questions and the values are the lists of answer alternatives. You consistently put the correct answer as the first item in the list of alternatives so that you can identify it.

Note: You could continue to use a list of two-tuples to hold your questions. In fact, you’re only iterating over the questions and answers, not looking up the answers by using a question as a key. Therefore, you could argue that the list of tuples is a better data structure for your use case than a dictionary.

However, you use a dictionary because it looks better visually in your code, and the roles of questions and answer alternatives are more distinct.

You update your code to loop over each item in your newly minted dictionary. For each question, you pick out the correct answer from the alternatives, and you print out all the alternatives before asking the question:

If you always showed the correct answer as the first alternative, then your users would soon catch on and be able to guess the correct answer every time. Instead, you change the order of the alternatives by sorting them. Test your application:

The last question reveals another experience that can be frustrating for the user. In this example, they’ve chosen the correct alternative. However, as they were typing it, a typo snuck in. Can you make your application more forgiving?

You know that the user will answer with one of the alternatives, so you just need a way for them to communicate which alternative they choose. You can add a label to each alternative and only ask the user to enter the label.

Update the application to use enumerate() to print the index of each answer alternative:

You store the reordered alternatives as sorted_alternatives so that you can look up the full answer based on the answer label that the user enters. Recall that input() always returns a string, so you need to convert it to an integer before you treat it as a list index.

Now, it’s more convenient to answer the questions:

Great! You’ve created quite a capable quiz application! In the next step, you won’t add any more functionality. Instead, you’ll make your application more user-friendly.

Step 2: Make Your Application User-Friendly

In this second step, you’ll improve on your quiz application to make it easier to use. In particular, you’ll improve the following:

  • How the application looks and feels
  • How you summarize the user’s results
  • What happens if your user enters a nonexistent alternative
  • Which order you present the questions and alternatives in

At the end of this step, your application will work as follows:

Your program will still work similarly to now, but it’ll be more robust and attractive. You can find the source code as it’ll look at the end of this step in the source_code_step_2 directory by clicking below:

Get Source Code: Click here to get access to the source code that you’ll use to build your quiz application.

Format the Output More Nicely

Look back at how your quiz application is currently presented. It’s not very attractive. There are no blank lines that tell you where a new question starts, and the alternatives are listed above the question, which is a bit confusing. Furthermore, the numbering of the different choices start at 0 instead of 1 , which would be more natural.

In your next update to quiz.py , you’ll number the questions themselves and present the question text above the answer alternatives. Additionally, you’ll use lowercase letters instead of numbers to identify answers:

You use string.ascii_lowercase to get letters that label your answer alternatives. You combine letters and alternatives with zip() and store them in a dictionary as follows:

You use these labeled alternatives when you display the options to the user and when you look up the user’s answer based on the label that they entered. Note the use of the special escape string «\n» . This is interpreted as a newline and adds a blank line on the screen. This is a simple way to add some organization to your output:

Your output is still mostly monochrome in the terminal, but it’s more visually pleasing, and it’s easier to read.

Keep Score

Now that you’re numbering the questions, it would also be nice to keep track of how many questions the user answers correctly. You can add a variable, num_correct , to take care of this:

You increase num_correct for each correct answer. The num loop variable already counts the total number of questions, so you can use that to report the user’s result.

Handle User Errors

So far, you haven’t worried too much about what happens if the user enters an answer that’s not valid. In the different versions of your app, this oversight could result in the program raising an error or—less dramatically—registering a user’s invalid answer as wrong.

You can handle user errors in a better way by allowing the user to re-enter their answer when they enter something invalid. One way to do this is to wrap input() in a while loop:

The condition (text := input()) != «quit» does a few things at once. It uses an assigment expression ( := ), often called the walrus operator, to store the user input as text and compare it to the string «quit» . The while loop will run until you type quit at the prompt. See The Walrus Operator: Python 3.8 Assignment Expressions for more examples.

Note: If you’re using an older version of Python than 3.8, then the assignment expression will cause a syntax error. You can rewrite the code to avoid using the walrus operator. There’s a version of the quiz application that runs on Python 3.7 in the source code that you downloaded earlier.

In your quiz application, you use a similar construct to loop until the user gives a valid answer:

If you enter an invalid choice at the prompt, then you’ll be reminded about your valid choices:

Note that once the while loops exits, you’re guaranteed that answer_label is one of the keys in labeled_alternatives , so it’s safe to look up answer directly. Next, you’ll add one more improvement by injecting some randomness into your quiz.

Add Variety to Your Quiz

Currently, when you run your quiz application, you’re always asking the questions in the same order as they’re listed in your source code. Additionally, the answer alternatives for a given question also come in a fixed order that never changes.

You can add some variety to your quiz by changing things up a little. You can randomize both the order of the questions and the order of the answer alternatives for each question:

You use random.sample() to randomize the order of your questions and the order of the answer alternatives. Usually, random.sample() picks out a few random samples from a collection. However, if you ask for as many samples as there are items in the sequence, then you’re effectively randomly reordering the whole sequence:

Additionally, you cap the number of questions in the quiz to NUM_QUESTIONS_PER_QUIZ which is initially set to five. If you include more than five questions in your application, then this also adds some variety as to which questions get asked in addition to the order in which they’re asked.

Note: You can also use random.shuffle() to shuffle your questions and alternatives. The difference is that shuffle() reorders sequences in place, which means that it changes your underlying QUESTIONS data structure. sample() creates new lists of questions and alternatives, instead.

In your current code, using shuffle() wouldn’t be a problem because QUESTIONS is reset every time you run your quiz application. It could become a problem down the line, for example if you implement the possibility of asking the same question several times. Your code is usually simpler to reason about if you don’t change or mutate your underlying data structure.

Throughout this step, you’ve improved on your quiz application. It’s now time to take a step back and consider the code itself. In the next section, you’ll reorganize the code so that you keep it modular and ready for further development.

Step 3: Organize Your Code With Functions

In this step, you’ll refactor your code. Refactoring means that you’ll change your code, but your application’s behavior and your user’s experience will stay as they are. This may not sound very exciting, but it’ll be tremendously useful down the line, as good refactorings make it more convenient to maintain and expand your code.

Note: If you’d like to see two Real Python team members work through refactoring some code, then check out Refactoring: Prepare Your Code to Get Help. You’ll also learn how to ask clear, concise programming questions.

Currently, your code isn’t particularly organized. All your statements are fairly low level. You’ll define functions to improve your code. A few of their advantages are the following:

  • Functions name higher-level operations that can help you get an overview of your code.
  • Functions can be reused.

To see how the code will look after you’ve refactored it, click below and check out the source_code_step_3 folder:

Get Source Code: Click here to get access to the source code that you’ll use to build your quiz application.

Prepare Data

Many games and applications follow a common life cycle:

  1. Preprocess: Prepare initial data.
  2. Process: Run main loop.
  3. Postprocess: Clean up and close application.

In your quiz application, you first read the available questions, then you ask each of the questions, before finally reporting the final score. If you look back at your current code, then you’ll see these three steps in the code. But the organization is still a bit hidden within all the details.

You can make the main functionality clearer by encapsulating it in a function. You don’t need to update your quiz.py file yet, but note that you can translate the previous paragraph into code that looks like this:

This code won’t run as it is. The functions prepare_questions() and ask_question() haven’t been defined, and there are some other details missing. Still, run_quiz() encapsulates the functionality of your application at a high level.

Writing down your application flow at a high level like this can be a great start to uncover which functions are natural building blocks in your code. In the rest of this section, you’ll fill in the missing details:

  • Implement prepare_questions() .
  • Implement ask_question() .
  • Revisit run_quiz() .

You’re now going to make quite substantial changes to the code of your quiz application as you’re refactoring it to use functions. Before doing so, it’s a good idea to make sure you can revert to the current state, which you know works. You can do this either by saving a copy of your code with a different filename or by making a commit if you’re using a version control system.

Once you’ve safely stored your current code, start with a new quiz.py that only contains your imports and global variables. You can copy these from your previous version:

Remember that you’re only reorganizing your code. You’re not adding new functionality, so you won’t need to import any new libraries.

Next, you’ll implement the necessary preprocessing. In this case, this means that you’ll prepare the QUESTIONS data structure so that it’s ready to be used in your main loop. For now, you’ll potentially limit the number of questions and make sure they’re listed in a random order:

Note that prepare_questions() deals with general questions and num_questions parameters. Afterward, you’ll pass in your specific QUESTIONS and NUM_QUESTIONS_PER_QUIZ as arguments. This means that you don’t depend on global variables in your function. With this decoupling, your function is more general, and you can later more readily replace the source of your questions.

Ask Questions

Look back on your sketch for the run_quiz() function and remember that it contains your main loop. For each question, you’ll call ask_question() . Your next task is to implement that helper function.

Think about what ask_question() needs to do:

  1. Pick out the correct answer from the list of alternatives
  2. Shuffle the alternatives
  3. Print the question to the screen
  4. Print all alternatives to the screen
  5. Get the answer from the user
  6. Check that the user’s answer is valid
  7. Check whether the user answered correctly or not
  8. Add 1 to the count of correct answers if the answer is correct

These are a lot of small things to do in one function, and you could consider whether there’s potential for further modularization. For example, items 3 to 6 in the list above are all about interacting with the user, and you can pull them into yet another helper function.

To achieve this modularization, add the following get_answer() helper function to your source code:

This function accepts a question text and a list of alternatives. You then use the same techniques as earlier to label the alternatives and ask the user to enter a valid label. Finally, you return the user’s answer.

Using get_answer() simplifies your implementation of ask_question() , as you no longer need to handle the user interaction. You can do something like the following:

You first randomly reorder the answer alternatives using random.shuffle() , as you did earlier. Next, you call get_answer() , which handles all details about getting an answer from the user. You can therefore finish up ask_question() by checking the correctness of the answer. Observe that you return 1 or 0 , which indicates to the calling function whether the answer was correct or not.

Note: You could replace the return values with Booleans. Instead of 1 , you could return True , and instead of 0 , you could return False . This would work because Python treats Booleans as integers in calculations:

In some cases, your code reads more naturally when you use True and False . In this case, you’re counting correct answers, so it seems more intuitive to use numbers.

You’re now ready to implement run_quiz() properly. One thing you’ve learned while implementing prepare_questions() and ask_question() is which arguments you need to pass on:

As earlier, you use enumerate() to keep a counter that numbers the questions you ask. You can increment num_correct based on the return value of ask_question() . Observe that run_quiz() is your only function that directly interacts with QUESTIONS and NUM_QUESTIONS_PER_QUIZ .

Your refactoring is now complete, except for one thing. If you run quiz.py now, then it’ll seem like nothing happens. In fact, Python will read your global variables and define your functions. However, you’re not calling any of those functions. You therefore need to add a function call that starts your application:

You call run_quiz() at the end of quiz.py , outside of any function. It’s good practice to protect such a call to your main function with an if __name__ == «__main__» test. This special incantation is a Python convention that means that run_quiz() is called when you run quiz.py as a script, but it’s not called when you import quiz as a module.

That’s it! You’ve refactored your code into several functions. This will help you in keeping track of the functionality of your application. It’ll also be useful in this tutorial, as you can consider changes to individual functions instead of changing the whole script.

For the rest of the tutorial, you’ll see your full code listed in collapsible boxes like the one below. Expand these to see the current state and get an overview of your full application:

quiz.py source code Show/Hide

The full source code of your quiz application is listed below:

Run your application with python quiz.py .

Through this step, you’ve refactored your code to make it more convenient to work with. You separated your commands into well-organized functions that you can continue to develop. In the next step, you’ll take advantage of this by improving how you read questions into your application.

Step 4: Separate Data Into Its Own File

You’ll continue your refactoring journey in this step. Your focus will now be how you provide questions to your application.

So far, you’ve stored the questions directly in your source code in the QUESTIONS data structure. It’s usually better to separate your data from your code. This separation can make your code more readable, but more importantly, you can take advantage of systems designed for handling data if it’s not hidden inside your code.

In this section, you’ll learn how to store your questions in a separate data file formatted according to the TOML standard. Other options—that you won’t cover in this tutorial—are storing the questions in a different file format like JSON or YAML, or storing them in a database, either a traditional relational one or a NoSQL database.

To peek at how you’ll improve your code in this step, click below and go to the source_code_step_4 directory:

Get Source Code: Click here to get access to the source code that you’ll use to build your quiz application.

Move Questions to a TOML File

TOML is branded as “a config file format for humans” (Source). It’s designed to be readable by humans and uncomplicated to parse by computers. Information is represented in key-value pairs that can be mapped to a hash table data structure, like a Python dictionary.

TOML supports several data types, including strings, integers, floating-point numbers, Booleans, and dates. Additionally, data can be structured in arrays and tables, which are similar to Python’s lists and dictionaries, respectively. TOML has been gaining popularity over the last years, and the format is stable after version 1.0.0 of the format specification was released in January 2021.

Create a new text file that you’ll call questions.toml , and add the following content:

While there are differences between TOML syntax and Python syntax, you’ll recognize elements like using quotation marks ( » ) for text and square brackets ( [] ) for lists of elements.

To work with TOML files in Python, you need a library that parses them. In this tutorial, you’ll use tomli . This will be the only package you use in this project that’s not part of Python’s standard library.

Note: TOML support is added to Python’s standard library in Python 3.11. If you’re already using Python 3.11, then you can skip the instructions below to create a virtual environment and install tomli . Instead, you can immediately start coding by replacing any mentions of tomli in your code with the compatible tomllib .

Later in this section, you’ll learn how to write code that can use tomllib if it’s available and fall back to tomli if necessary.

Before installing tomli , you should create and activate a virtual environment:

4. More Control Flow Tools¶

As well as the while statement just introduced, Python uses a few more that we will encounter in this chapter.

4.1. if Statements¶

Perhaps the most well-known statement type is the if statement. For example:

There can be zero or more elif parts, and the else part is optional. The keyword ‘ elif ’ is short for ‘else if’, and is useful to avoid excessive indentation. An if … elif … elif … sequence is a substitute for the switch or case statements found in other languages.

If you’re comparing the same value to several constants, or checking for specific types or attributes, you may also find the match statement useful. For more details see match Statements .

4.2. for Statements¶

The for statement in Python differs a bit from what you may be used to in C or Pascal. Rather than always iterating over an arithmetic progression of numbers (like in Pascal), or giving the user the ability to define both the iteration step and halting condition (as C), Python’s for statement iterates over the items of any sequence (a list or a string), in the order that they appear in the sequence. For example (no pun intended):

Code that modifies a collection while iterating over that same collection can be tricky to get right. Instead, it is usually more straight-forward to loop over a copy of the collection or to create a new collection:

4.3. The range() Function¶

If you do need to iterate over a sequence of numbers, the built-in function range() comes in handy. It generates arithmetic progressions:

The given end point is never part of the generated sequence; range(10) generates 10 values, the legal indices for items of a sequence of length 10. It is possible to let the range start at another number, or to specify a different increment (even negative; sometimes this is called the ‘step’):

To iterate over the indices of a sequence, you can combine range() and len() as follows:

In most such cases, however, it is convenient to use the enumerate() function, see Looping Techniques .

A strange thing happens if you just print a range:

In many ways the object returned by range() behaves as if it is a list, but in fact it isn’t. It is an object which returns the successive items of the desired sequence when you iterate over it, but it doesn’t really make the list, thus saving space.

We say such an object is iterable , that is, suitable as a target for functions and constructs that expect something from which they can obtain successive items until the supply is exhausted. We have seen that the for statement is such a construct, while an example of a function that takes an iterable is sum() :

Later we will see more functions that return iterables and take iterables as arguments. In chapter Data Structures , we will discuss in more detail about list() .

4.4. break and continue Statements, and else Clauses on Loops¶

The break statement breaks out of the innermost enclosing for or while loop.

A for or while loop can include an else clause.

In a for loop, the else clause is executed after the loop reaches its final iteration.

In a while loop, it’s executed after the loop’s condition becomes false.

In either kind of loop, the else clause is not executed if the loop was terminated by a break .

This is exemplified in the following for loop, which searches for prime numbers:

(Yes, this is the correct code. Look closely: the else clause belongs to the for loop, not the if statement.)

When used with a loop, the else clause has more in common with the else clause of a try statement than it does with that of if statements: a try statement’s else clause runs when no exception occurs, and a loop’s else clause runs when no break occurs. For more on the try statement and exceptions, see Handling Exceptions .

The continue statement, also borrowed from C, continues with the next iteration of the loop:

4.5. pass Statements¶

The pass statement does nothing. It can be used when a statement is required syntactically but the program requires no action. For example:

This is commonly used for creating minimal classes:

Another place pass can be used is as a place-holder for a function or conditional body when you are working on new code, allowing you to keep thinking at a more abstract level. The pass is silently ignored:

4.6. match Statements¶

A match statement takes an expression and compares its value to successive patterns given as one or more case blocks. This is superficially similar to a switch statement in C, Java or JavaScript (and many other languages), but it’s more similar to pattern matching in languages like Rust or Haskell. Only the first pattern that matches gets executed and it can also extract components (sequence elements or object attributes) from the value into variables.

The simplest form compares a subject value against one or more literals:

Note the last block: the “variable name” _ acts as a wildcard and never fails to match. If no case matches, none of the branches is executed.

You can combine several literals in a single pattern using | (“or”):

Patterns can look like unpacking assignments, and can be used to bind variables:

Study that one carefully! The first pattern has two literals, and can be thought of as an extension of the literal pattern shown above. But the next two patterns combine a literal and a variable, and the variable binds a value from the subject ( point ). The fourth pattern captures two values, which makes it conceptually similar to the unpacking assignment (x, y) = point .

If you are using classes to structure your data you can use the class name followed by an argument list resembling a constructor, but with the ability to capture attributes into variables:

You can use positional parameters with some builtin classes that provide an ordering for their attributes (e.g. dataclasses). You can also define a specific position for attributes in patterns by setting the __match_args__ special attribute in your classes. If it’s set to (“x”, “y”), the following patterns are all equivalent (and all bind the y attribute to the var variable):

A recommended way to read patterns is to look at them as an extended form of what you would put on the left of an assignment, to understand which variables would be set to what. Only the standalone names (like var above) are assigned to by a match statement. Dotted names (like foo.bar ), attribute names (the x= and y= above) or class names (recognized by the “(…)” next to them like Point above) are never assigned to.

Patterns can be arbitrarily nested. For example, if we have a short list of Points, with __match_args__ added, we could match it like this:

We can add an if clause to a pattern, known as a “guard”. If the guard is false, match goes on to try the next case block. Note that value capture happens before the guard is evaluated:

Several other key features of this statement:

Like unpacking assignments, tuple and list patterns have exactly the same meaning and actually match arbitrary sequences. An important exception is that they don’t match iterators or strings.

Sequence patterns support extended unpacking: [x, y, *rest] and (x, y, *rest) work similar to unpacking assignments. The name after * may also be _ , so (x, y, *_) matches a sequence of at least two items without binding the remaining items.

Mapping patterns: <"bandwidth": b, "latency": l>captures the "bandwidth" and "latency" values from a dictionary. Unlike sequence patterns, extra keys are ignored. An unpacking like **rest is also supported. (But **_ would be redundant, so it is not allowed.)

Subpatterns may be captured using the as keyword:

will capture the second element of the input as p2 (as long as the input is a sequence of two points)

Most literals are compared by equality, however the singletons True , False and None are compared by identity.

Patterns may use named constants. These must be dotted names to prevent them from being interpreted as capture variable:

For a more detailed explanation and additional examples, you can look into PEP 636 which is written in a tutorial format.

4.7. Defining Functions¶

We can create a function that writes the Fibonacci series to an arbitrary boundary:

The keyword def introduces a function definition. It must be followed by the function name and the parenthesized list of formal parameters. The statements that form the body of the function start at the next line, and must be indented.

The first statement of the function body can optionally be a string literal; this string literal is the function’s documentation string, or docstring. (More about docstrings can be found in the section Documentation Strings .) There are tools which use docstrings to automatically produce online or printed documentation, or to let the user interactively browse through code; it’s good practice to include docstrings in code that you write, so make a habit of it.

The execution of a function introduces a new symbol table used for the local variables of the function. More precisely, all variable assignments in a function store the value in the local symbol table; whereas variable references first look in the local symbol table, then in the local symbol tables of enclosing functions, then in the global symbol table, and finally in the table of built-in names. Thus, global variables and variables of enclosing functions cannot be directly assigned a value within a function (unless, for global variables, named in a global statement, or, for variables of enclosing functions, named in a nonlocal statement), although they may be referenced.

The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). 1 When a function calls another function, or calls itself recursively, a new local symbol table is created for that call.

A function definition associates the function name with the function object in the current symbol table. The interpreter recognizes the object pointed to by that name as a user-defined function. Other names can also point to that same function object and can also be used to access the function:

Coming from other languages, you might object that fib is not a function but a procedure since it doesn’t return a value. In fact, even functions without a return statement do return a value, albeit a rather boring one. This value is called None (it’s a built-in name). Writing the value None is normally suppressed by the interpreter if it would be the only value written. You can see it if you really want to using print() :

It is simple to write a function that returns a list of the numbers of the Fibonacci series, instead of printing it:

This example, as usual, demonstrates some new Python features:

The return statement returns with a value from a function. return without an expression argument returns None . Falling off the end of a function also returns None .

The statement result.append(a) calls a method of the list object result . A method is a function that ‘belongs’ to an object and is named obj.methodname , where obj is some object (this may be an expression), and methodname is the name of a method that is defined by the object’s type. Different types define different methods. Methods of different types may have the same name without causing ambiguity. (It is possible to define your own object types and methods, using classes, see Classes ) The method append() shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent to result = result + [a] , but more efficient.

4.8. More on Defining Functions¶

It is also possible to define functions with a variable number of arguments. There are three forms, which can be combined.

4.8.1. Default Argument Values¶

The most useful form is to specify a default value for one or more arguments. This creates a function that can be called with fewer arguments than it is defined to allow. For example:

This function can be called in several ways:

giving only the mandatory argument: ask_ok(‘Do you really want to quit?’)

giving one of the optional arguments: ask_ok(‘OK to overwrite the file?’, 2)

or even giving all arguments: ask_ok(‘OK to overwrite the file?’, 2, ‘Come on, only yes or no!’)

This example also introduces the in keyword. This tests whether or not a sequence contains a certain value.

The default values are evaluated at the point of function definition in the defining scope, so that

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:

This will print

If you don’t want the default to be shared between subsequent calls, you can write the function like this instead:

4.8.2. Keyword Arguments¶

Functions can also be called using keyword arguments of the form kwarg=value . For instance, the following function:

accepts one required argument ( voltage ) and three optional arguments ( state , action , and type ). This function can be called in any of the following ways:

but all the following calls would be invalid:

In a function call, keyword arguments must follow positional arguments. All the keyword arguments passed must match one of the arguments accepted by the function (e.g. actor is not a valid argument for the parrot function), and their order is not important. This also includes non-optional arguments (e.g. parrot(voltage=1000) is valid too). No argument may receive a value more than once. Here’s an example that fails due to this restriction:

When a final formal parameter of the form **name is present, it receives a dictionary (see Mapping Types — dict ) containing all keyword arguments except for those corresponding to a formal parameter. This may be combined with a formal parameter of the form *name (described in the next subsection) which receives a tuple containing the positional arguments beyond the formal parameter list. ( *name must occur before **name .) For example, if we define a function like this:

It could be called like this:

and of course it would print:

Note that the order in which the keyword arguments are printed is guaranteed to match the order in which they were provided in the function call.

4.8.3. Special parameters¶

By default, arguments may be passed to a Python function either by position or explicitly by keyword. For readability and performance, it makes sense to restrict the way arguments can be passed so that a developer need only look at the function definition to determine if items are passed by position, by position or keyword, or by keyword.

A function definition may look like:

where / and * are optional. If used, these symbols indicate the kind of parameter by how the arguments may be passed to the function: positional-only, positional-or-keyword, and keyword-only. Keyword parameters are also referred to as named parameters.

4.8.3.1. Positional-or-Keyword Arguments¶

If / and * are not present in the function definition, arguments may be passed to a function by position or by keyword.

4.8.3.2. Positional-Only Parameters¶

Looking at this in a bit more detail, it is possible to mark certain parameters as positional-only. If positional-only, the parameters’ order matters, and the parameters cannot be passed by keyword. Positional-only parameters are placed before a / (forward-slash). The / is used to logically separate the positional-only parameters from the rest of the parameters. If there is no / in the function definition, there are no positional-only parameters.

Parameters following the / may be positional-or-keyword or keyword-only.

4.8.3.3. Keyword-Only Arguments¶

To mark parameters as keyword-only, indicating the parameters must be passed by keyword argument, place an * in the arguments list just before the first keyword-only parameter.

4.8.3.4. Function Examples¶

Consider the following example function definitions paying close attention to the markers / and * :

The first function definition, standard_arg , the most familiar form, places no restrictions on the calling convention and arguments may be passed by position or keyword:

The second function pos_only_arg is restricted to only use positional parameters as there is a / in the function definition:

The third function kwd_only_args only allows keyword arguments as indicated by a * in the function definition:

And the last uses all three calling conventions in the same function definition:

Finally, consider this function definition which has a potential collision between the positional argument name and **kwds which has name as a key:

There is no possible call that will make it return True as the keyword ‘name’ will always bind to the first parameter. For example:

But using / (positional only arguments), it is possible since it allows name as a positional argument and ‘name’ as a key in the keyword arguments:

In other words, the names of positional-only parameters can be used in **kwds without ambiguity.

4.8.3.5. Recap¶

The use case will determine which parameters to use in the function definition:

Use positional-only if you want the name of the parameters to not be available to the user. This is useful when parameter names have no real meaning, if you want to enforce the order of the arguments when the function is called or if you need to take some positional parameters and arbitrary keywords.

Use keyword-only when names have meaning and the function definition is more understandable by being explicit with names or you want to prevent users relying on the position of the argument being passed.

For an API, use positional-only to prevent breaking API changes if the parameter’s name is modified in the future.

4.8.4. Arbitrary Argument Lists¶

Finally, the least frequently used option is to specify that a function can be called with an arbitrary number of arguments. These arguments will be wrapped up in a tuple (see Tuples and Sequences ). Before the variable number of arguments, zero or more normal arguments may occur.

Normally, these variadic arguments will be last in the list of formal parameters, because they scoop up all remaining input arguments that are passed to the function. Any formal parameters which occur after the *args parameter are ‘keyword-only’ arguments, meaning that they can only be used as keywords rather than positional arguments.

4.8.5. Unpacking Argument Lists¶

The reverse situation occurs when the arguments are already in a list or tuple but need to be unpacked for a function call requiring separate positional arguments. For instance, the built-in range() function expects separate start and stop arguments. If they are not available separately, write the function call with the * -operator to unpack the arguments out of a list or tuple:

In the same fashion, dictionaries can deliver keyword arguments with the ** -operator:

4.8.6. Lambda Expressions¶

Small anonymous functions can be created with the lambda keyword. This function returns the sum of its two arguments: lambda a, b: a+b . Lambda functions can be used wherever function objects are required. They are syntactically restricted to a single expression. Semantically, they are just syntactic sugar for a normal function definition. Like nested function definitions, lambda functions can reference variables from the containing scope:

The above example uses a lambda expression to return a function. Another use is to pass a small function as an argument:

4.8.7. Documentation Strings¶

Here are some conventions about the content and formatting of documentation strings.

The first line should always be a short, concise summary of the object’s purpose. For brevity, it should not explicitly state the object’s name or type, since these are available by other means (except if the name happens to be a verb describing a function’s operation). This line should begin with a capital letter and end with a period.

If there are more lines in the documentation string, the second line should be blank, visually separating the summary from the rest of the description. The following lines should be one or more paragraphs describing the object’s calling conventions, its side effects, etc.

The Python parser does not strip indentation from multi-line string literals in Python, so tools that process documentation have to strip indentation if desired. This is done using the following convention. The first non-blank line after the first line of the string determines the amount of indentation for the entire documentation string. (We can’t use the first line since it is generally adjacent to the string’s opening quotes so its indentation is not apparent in the string literal.) Whitespace “equivalent” to this indentation is then stripped from the start of all lines of the string. Lines that are indented less should not occur, but if they occur all their leading whitespace should be stripped. Equivalence of whitespace should be tested after expansion of tabs (to 8 spaces, normally).

Here is an example of a multi-line docstring:

4.8.8. Function Annotations¶

Function annotations are completely optional metadata information about the types used by user-defined functions (see PEP 3107 and PEP 484 for more information).

Annotations are stored in the __annotations__ attribute of the function as a dictionary and have no effect on any other part of the function. Parameter annotations are defined by a colon after the parameter name, followed by an expression evaluating to the value of the annotation. Return annotations are defined by a literal -> , followed by an expression, between the parameter list and the colon denoting the end of the def statement. The following example has a required argument, an optional argument, and the return value annotated:

4.9. Intermezzo: Coding Style¶

Now that you are about to write longer, more complex pieces of Python, it is a good time to talk about coding style. Most languages can be written (or more concise, formatted) in different styles; some are more readable than others. Making it easy for others to read your code is always a good idea, and adopting a nice coding style helps tremendously for that.

For Python, PEP 8 has emerged as the style guide that most projects adhere to; it promotes a very readable and eye-pleasing coding style. Every Python developer should read it at some point; here are the most important points extracted for you:

Use 4-space indentation, and no tabs.

4 spaces are a good compromise between small indentation (allows greater nesting depth) and large indentation (easier to read). Tabs introduce confusion, and are best left out.

Wrap lines so that they don’t exceed 79 characters.

This helps users with small displays and makes it possible to have several code files side-by-side on larger displays.

Use blank lines to separate functions and classes, and larger blocks of code inside functions.

When possible, put comments on a line of their own.

Use spaces around operators and after commas, but not directly inside bracketing constructs: a = f(1, 2) + g(3, 4) .

Name your classes and functions consistently; the convention is to use UpperCamelCase for classes and lowercase_with_underscores for functions and methods. Always use self as the name for the first method argument (see A First Look at Classes for more on classes and methods).

Don’t use fancy encodings if your code is meant to be used in international environments. Python’s default, UTF-8, or even plain ASCII work best in any case.

Likewise, don’t use non-ASCII characters in identifiers if there is only the slightest chance people speaking a different language will read or maintain the code.

Actually, call by object reference would be a better description, since if a mutable object is passed, the caller will see any changes the callee makes to it (items inserted into a list).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *