10. Write Better Python (70m) Flashcards
What is PET in python?
Treehouse Notes:
PEP 8 is Python’s style guide. It’s a set of rules for how to format your Python code to maximize its readability. Writing code to a specification helps to make large code bases, with lots of writers, more uniform and predictable, too.
GPT Notes:
In Python, PET typically refers to the Python Execution Time (PET) library, which is used for measuring and analyzing the execution time of Python code. PET provides a convenient way to profile and optimize Python programs by collecting timing information about different parts of the code.
By using PET, you can identify bottlenecks and areas of your code that consume excessive processing time. It helps you understand which functions or sections of your code are taking the most time to execute. With this information, you can focus on optimizing those areas to improve the overall performance of your Python program.
PET allows you to measure the execution time of specific functions, methods, or blocks of code. It provides various features, such as:
- Function-level profiling: PET allows you to profile individual functions or methods in your code. It records the execution time, number of calls, and other relevant information for each function.
- Line-level profiling: PET can also profile at a more granular level by tracking the execution time of each line of code. This level of detail helps identify specific lines that contribute significantly to the overall execution time.
- Statistical analysis: PET provides statistical analysis capabilities to help you interpret the collected timing data. It calculates mean execution time, standard deviation, and other statistical metrics to give you a better understanding of the performance characteristics of your code.
- Visualization: PET can generate graphical representations of the profiling data, such as histograms and line plots. These visualizations help visualize the distribution of execution times and identify outliers or patterns.
To use PET in your Python program, you typically need to install the PET library, import it into your code, and instrument the sections of code you want to profile. By running your program with PET enabled, it collects timing information and generates a report that you can analyze to optimize your code.
It’s worth noting that there might be other uses or interpretations of the acronym “PET” in the context of Python, but the Python Execution Time library is a commonly known one.
What is PEP 8 and PEP 20?
PEP 8 and PEP 20 are both important documents in the Python community that provide guidelines and recommendations for writing Python code and promoting the principles of good coding style and readability.
- PEP 8: PEP 8 is the official style guide for Python code. “PEP” stands for Python Enhancement Proposal, and PEP 8 specifically addresses the coding conventions and style guidelines that Python developers should follow to write clean, readable, and consistent code. It covers topics such as naming conventions, indentation, line length, comments, and overall code structure. Adhering to PEP 8 helps improve code readability and maintainability, especially when collaborating with other developers. Following a consistent coding style also helps make code easier to understand for newcomers to Python.
- PEP 20: PEP 20 is titled “The Zen of Python” and serves as a guiding philosophy for Python developers. It outlines 19 principles that emphasize simplicity, readability, and practicality as core values in Python programming. These principles are often referred to as “Pythonic” and help shape the design and development of Python applications. PEP 20 is usually accessed by invoking the
import this
statement within a Python interpreter, which displays the Zen of Python’s principles. Some well-known principles from PEP 20 include “Readability counts,” “There should be one– and preferably only one –obvious way to do it,” and “Special cases aren’t special enough to break the rules.”
Both PEP 8 and PEP 20 are highly regarded within the Python community and are considered essential references for Python developers to write clean, readable, and Pythonic code. Adhering to these guidelines promotes consistency and improves the maintainability and collaboration of Python projects.
multiple imports
Re-arrange the following code using the rules of PEP8:
badpep8.py
~~~
def fooBar(arg1,arg2,arg3,arg4):
# way too much indentation
return arg1,arg2,arg3,arg4
def bar(*args):
# bad spacing
return 2+2
Bad class name, bad spacing, bad indentation
class treehouse:
def one(self):
return 1
def two(self):
return 2
import sys, random
bad identation and whitespace
a,b,c,d=fooBar ( “a long string”,”a longer string”,”yet another long string”,
“and other crazy string”
)
bad spacing
one = 1
three = 3
fourteen = 14 # make fourteen equal to 12
print( a )
print(fourteen)
print(treehouse().two())
~~~
Here’s the rearranged code following the rules of PEP 8:
```python
import sys
import random
def foo_bar(arg1, arg2, arg3, arg4):
return arg1, arg2, arg3, arg4
def bar(*args):
return 2 + 2
class Treehouse:
def one(self):
return 1
def two(self): return 2
a, b, c, d = foo_bar(“a long string”, “a longer string”, “yet another long string”,
“and other crazy string”)
one = 1
three = 3
fourteen = 14 # make fourteen equal to 12
print(a)
print(fourteen)
print(Treehouse().two())
~~~
In the rearranged code, the changes made are as follows:
- Imported
sys
andrandom
on separate lines. - Renamed the
fooBar
function tofoo_bar
following lowercase with underscores naming convention. - Removed excessive indentation in the
foo_bar
function. - Added appropriate spacing in the
bar
function. - Renamed the
treehouse
class toTreehouse
following the capitalized words convention for class names. - Corrected the indentation and spacing within the
Treehouse
class. - Moved the imports to the top of the file.
- Fixed indentation and whitespace around the assignment of
a, b, c, d
. - Adjusted the spacing around the assignment of
one
,three
, andfourteen
. - Changed the value of
fourteen
to be equal to12
as requested. - Added proper spacing around the
print
statements. - Invoked the
Treehouse
class using parentheses in theprint
statement.
By following the guidelines of PEP 8, the code becomes more readable, consistent, and adheres to the recommended Python coding style.
My Code:
~~~
import sys
import random
multiple imports
def foo_bar(arg1, arg2, arg3, arg4):
# way too much indentation
return arg1, arg2, arg3, arg4
def bar(*args):
# bad spacing
return 2 + 2
Bad class name, bad spacing, bad indentation
class Treehouse:
def one(self):
return 1
def two(self): return 2
bad identation and whitespace
alpha, beta, charlie, delta = foo_bar(
“a long string”,
“a longer string”,
“yet another long string”,
“and other crazy string”)
bad spacing
one = 1
three = 3
fourteen = 12 # make fourteen equal to 12
print(alpha)
print(fourteen)
print(Treehouse().two())
~~~
Sure! Here are the steps you took to modify the original code to follow the conventions of PEP 8:
- Imported
sys
andrandom
on separate lines. - Renamed the
fooBar
function tofoo_bar
following lowercase with underscores naming convention. - Removed excessive indentation in the
foo_bar
function. - Added appropriate spacing in the
bar
function. - Renamed the
treehouse
class toTreehouse
following the capitalized words convention for class names. - Corrected the indentation and spacing within the
Treehouse
class. - Moved the imports to the top of the file.
- Adjusted the indentation and spacing around the assignment of
alpha
,beta
,charlie
, anddelta
variables. - Fixed the spacing within the
foo_bar
function arguments by aligning them. - Adjusted the spacing around the assignment of
one
,three
, andfourteen
. - Changed the value of
fourteen
to be equal to12
as requested. - Added proper spacing around the
print
statements. - Invoked the
Treehouse
class using parentheses in theprint
statement.
By following these steps, you effectively modified the original code to adhere to the conventions and guidelines outlined in PEP 8.
what is the naming convention for classes and functions in Python as per PEP 8?
According to PEP 8, the official style guide for Python code, the naming conventions for classes and functions are as follows:
- Classes: Class names should use the CapWords (or CamelCase) convention, where each word within the name starts with an uppercase letter and there are no underscores between words. For example:
python class MyClass: pass
- Functions and Methods: Function and method names should be lowercase, with words separated by underscores. This convention is known as lowercase_with_underscores or snake_case. For example:```python
def my_function():
passdef calculate_average():
pass
```
It’s worth noting that PEP 8 also provides additional guidelines and recommendations for naming conventions related to variables, constants, and other elements in Python code. Adhering to these conventions helps to make the code more readable, consistent, and easily understandable for other developers.
Following these naming conventions contributes to the overall consistency of the Python codebase and makes it easier for developers to collaborate on projects.
Challenge 1: Add the correct amount of whitespace between the functions, between the functions and the class, and before the class method.
~~~
def first_function(arg1):
return ‘arg1 is {}’.format(arg1)
def second_function(arg1):
return ‘arg1 is {}’.format(arg1)
class MyClass:
args = [1, 2, 3]
def class_func(self):
return self.args
~~~
Challenge 2: Fix the spacing around commas, operators, and in function calls
~~~
numbers=[1,2,3,4,5]
def add(num1,num2):
return num1+num2
def subtract( num1,num2 ):
return num1-num2
def mathy(num1,num2):
up = add(5 + 7, 7 + 9)
down = subtract(6,3)
return add(up,down)
~~~
Challenge 3: Correct the import style
~~~
import os, sys, logging
from re import match, search
~~~
Challenge 1:
~~~
def first_function(arg1):
return ‘arg1 is {}’.format(arg1)
def second_function(arg1):
return ‘arg1 is {}’.format(arg1)
class MyClass:
args = [1, 2, 3]
def class_func(self): return self.args ~~~
Challenge 2:
~~~
numbers = [1, 2, 3, 4, 5]
def add(num1, num2):
return num1 + num2
def subtract(num1, num2):
return num1 - num2
def mathy(num1, num2):
up = add(5 + 7, 7 + 9)
down = subtract(6, 3)
return add(up, down)
~~~
Challenge 3:
~~~
import os
import sys
import logging
from re import match
from re import search
~~~
What does the following code will do in Python REPL:
python import this
The code import this
when executed in the Python REPL (Read-Eval-Print Loop) will display “The Zen of Python.” It is a famous poem or a set of guiding principles that embodies the philosophy of the Python language.
Here’s the output you would see after executing import this
in the Python REPL:
The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
“The Zen of Python” consists of 19 principles or aphorisms that highlight the core values and design principles of Python. These principles serve as a guide for Python developers, promoting simplicity, readability, and practicality in code development.
By invoking import this
, you can access and read the Zen of Python to gain insights into the mindset and philosophy that Python developers embrace when writing code.
What are docstrings in Python?
In Python, a docstring is a string literal used as a documentation string. It serves as a way to document and describe various elements in a Python program, such as modules, functions, classes, and methods. Docstrings provide important information about the purpose, usage, and behavior of the code to help other developers understand and utilize it effectively.
Here are some key points about docstrings:
- Format: Docstrings are enclosed in triple quotes (
'''
or"""
) to span multiple lines. They can be written as single-line or multi-line strings. - Purpose: Docstrings serve as a form of documentation for the code, providing details about the functionality, inputs, outputs, and usage of the associated element (module, function, class, method, etc.).
- Accessibility: Docstrings are accessible using the
\_\_doc\_\_
attribute of the corresponding element. For example,function_name.\_\_doc\_\_
retrieves the docstring of a function. - Style: Docstrings should follow a specific style guide, such as the conventions described in PEP 257. The most commonly used styles are the one-line summary docstring and the more detailed multi-line docstring.
- Tools: Various tools and libraries in the Python ecosystem utilize docstrings for generating documentation automatically. For instance, the
pydoc
module and documentation generators like Sphinx make use of docstrings to generate HTML or PDF documentation.
Here’s an example of a multi-line docstring for a function:
```python
def greet(name):
“””
Greet the person with the given name.
Args: name (str): The name of the person. Returns: str: A greeting message. Raises: ValueError: If the name is empty. """ if not name: raise ValueError("Name cannot be empty.") return f"Hello, {name}!" ~~~
In the example, the docstring provides information about the purpose of the function, the input argument, the return value, and any potential exceptions. It helps both developers and users of the code to understand how to use the function correctly and handle possible edge cases.
Using docstrings to document code promotes better collaboration, maintainability, and understanding of Python programs, making it easier for others (including your future self) to work with the codebase effectively.
Treehouse Notes:
Docstrings don’t require the triple-quotes, they just have to be a string, but the triple-quotes let you have newlines in the strings, a very useful formatting feature.
Add docstring to the following code:
docstrings.py:
~~~
def does_something(arg):
if isinstance(arg, (int, float)):
return arg + 10
elif isinstance(arg, str):
return str * 3
else:
raise TypeError(“does_something only takes ints, floats, and strings”)
~~~
And run Python REPL and import the above module and use help to show the docstring.
docstrings.py:
def does_something(arg): '''Takes one argument and does something based on type. If arg is a string, returns args * 3; If arg is an int or float, returns args + 10 ''' if isinstance(arg, (int, float)): return arg + 10 elif isinstance(arg, str): return str * 3 else: raise TypeError("does_something only takes ints, floats, and strings")
In Python REPL, if you run the following code you will see the docstring documentation:
~~~
python
import docstrings
help(docstrings.does_something)
~~~
it will return:
~~~
Help on function does_something in module docstrings:
does_something(arg)
Takes one argument and does something based on type.
If arg is a string, returns args * 3;
If arg is an int or float, returns args + 10
(END)
~~~
Challenge 1: Add a docstring to Treehouse.student. It should say “Gives a pleasant message about the student.”.
~~~
class Treehouse:
def student(self, name):
return ‘{} is a great Treehouse student!’.format(name)
~~~
Challenge 2: Add a docstring to Treehouse. Should be “Methods related to Treehouse and students.”.
Challenge 1:
~~~
class Treehouse:
def student(self, name):
“"”Gives a pleasant message about the student.
“””
return ‘{} is a great Treehouse student!’.format(name)
~~~
Challenge 2:
~~~
class Treehouse:
“"”Methods related to Treehouse and students.
“””
def student(self, name):
“"”Gives a pleasant message about the student.
“””
return ‘{} is a great Treehouse student!’.format(name)
~~~
Explain this DD game code in detail:
dd_game.py:
~~~
import random
player = {‘location’: None, ‘path’: []}
cells = [(0, 0), (0, 1), (0, 2),
(1, 0), (1, 1), (1, 2),
(2, 0), (2, 1), (2, 2)]
def get_locations():
monster = random.choice(cells)
door = random.choice(cells)
start = random.choice(cells)
if monster == door or monster == start or door == start: monster, door, start = get_locations() return monster, door, start
def get_moves(player):
moves = [‘LEFT’, ‘RIGHT’, ‘UP’, ‘DOWN’]
if player in [(0, 0), (1, 0), (2, 0)]:
moves.remove(‘LEFT’)
if player in [(0, 0), (0, 1), (0, 2)]:
moves.remove(‘UP’)
if player in [(0, 2), (1, 2), (2, 2)]:
moves.remove(‘RIGHT’)
if player in [(2, 0), (2, 1), (2, 2)]:
moves.remove(‘DOWN’)
return moves
def move_player(player, move):
x, y = player[‘location’]
player[‘path’].append((x, y))
if move == ‘LEFT’:
player[‘location’] = x, y - 1
elif move == ‘UP’:
player[‘location’] = x - 1, y
elif move == ‘RIGHT’:
player[‘location’] = x, y + 1
elif move == ‘DOWN’:
player[‘location’] = x + 1, y
return player
def draw_map():
print(‘ _ _ ’)
tile = ‘|{}’
for idx, cell in enumerate(cells):
if idx in [0, 1, 3, 4, 6, 7]:
if cell == player[‘location’]:
print(tile.format(‘X’), end=’’)
elif cell in player[‘path’]:
print(tile.format(‘.’), end=’’)
else:
print(tile.format(‘’), end=’’)
else:
if cell == player[‘location’]:
print(tile.format(‘X|’))
elif cell in player[‘path’]:
print(tile.format(‘.|’))
else:
print(tile.format(‘_|’))
monster, door, player[‘location’] = get_locations()
while True:
moves = get_moves(player[‘location’])
print(“Welcome to the dungeon!”)
print(“You’re currently in room {}”.format(player[‘location’]))
draw_map() print("\nYou can move {}".format(', '.join(moves))) print("Enter QUIT to quit") move = input("> ") move = move.upper() if move == 'QUIT': break if not move in moves: print("\n** Walls are hard! Stop running into them! **\n") continue player = move_player(player, move) if player['location'] == door: print("\n** You escaped! **\n") break elif player['location'] == monster: print("\n** You got eaten! **\n") break else: continue
~~~
Certainly! Let’s go through the code in more detail step by step:
- The code starts by importing the
random
module, which provides functions for generating random numbers. - The
player
dictionary is initialized with two keys:'location'
and'path'
. The'location'
key stores the current position of the player, which is initially set toNone
. The'path'
key stores a list of coordinates representing the path the player has taken in the dungeon. - The
cells
list defines the grid-based layout of the dungeon. It contains tuples representing the coordinates of each cell in the grid. Each cell is identified by its row and column position. - The
get_locations()
function is defined to randomly assign locations for the monster, door, and player’s starting position. It uses therandom.choice()
function to randomly select one cell from thecells
list for each entity. However, it also checks if the selected locations are the same (i.e., if the monster, door, and start positions overlap). If there is overlap, the function recursively calls itself until distinct locations are found for each entity. Once distinct locations are obtained, the function returns them as a tuple containing the monster’s location, door’s location, and player’s starting location. - The
get_moves(player)
function takes the player’s current location as input and returns a list of valid moves that the player can make from their current position. The function checks the player’s location against specific cells to determine the available moves. If the player is in the leftmost column, they cannot move left, so the'LEFT'
move is removed from the list. Similarly, if the player is in the top row, they cannot move up, so the'UP'
move is removed. Likewise, if the player is in the rightmost column, they cannot move right, so the'RIGHT'
move is removed. Finally, if the player is in the bottom row, they cannot move down, so the'DOWN'
move is removed. The function returns the updated list of valid moves. - The
move_player(player, move)
function takes the player dictionary and the chosen move as input and updates the player’s location accordingly. It extracts the current coordinates of the player’s location usingx, y = player['location']
. Then, it appends the current location to the player’s'path'
list to keep track of the path they have taken. Based on the chosen move, the function updates the player’s location coordinates. If the move is'LEFT'
, it subtracts 1 from they
coordinate, moving the player one cell to the left. If the move is'UP'
, it subtracts 1 from thex
coordinate, moving the player one cell up. If the move is'RIGHT'
, it adds 1 to they
coordinate, moving the player one cell to the right. If the move is'DOWN'
, it adds 1 to thex
coordinate, moving the player one cell down. Finally, the updated player dictionary is returned. - The
draw_map()
function is responsible for displaying the current state of the dungeon map. It uses a combination of loops and conditionals to print the appropriate characters for each cell. It starts by printing the top row separator, which consists of three underscore characters. Then, it enters a loop that iterates through each cell in thecells
list. Theenumerate()
function is used to retrieve both the index and value of each cell. Based on the index, the function determines whether to print a vertical separator or a new line character. For indices 0, 1, 3,
4, 6, and 7, it prints a vertical separator, which is a pipe character (|
). For all other indices, it prints a new line character to move to the next row. Inside the conditional statements, the function checks the current cell against the player’s location and path to determine what character to print. If the cell matches the player’s location, it prints an 'X'
to represent the player. If the cell is part of the player’s path, it prints a '.'
character. Otherwise, it prints an underscore ('_'
) to represent an unvisited cell. The map is displayed as a grid.
- The
monster
,door
, andplayer['location']
variables are assigned the values returned by theget_locations()
function. This randomly assigns distinct locations for the monster, door, and player’s starting position, setting up the initial state of the game. - The main game loop starts with a
while True
statement, which creates an infinite loop that continues until a game-ending condition is met or the player decides to quit. - Inside the loop, the available moves, the player’s current location, and the map are displayed to the player. The available moves are obtained by calling the
get_moves()
function with the player’s current location. - The player is prompted to enter a move. The input is obtained using the
input()
function and stored in themove
variable. To ensure case-insensitive comparisons, themove
variable is converted to uppercase using theupper()
method. - If the player enters
'QUIT'
, the game loop is terminated, and the game ends. - If the entered move is not in the list of available moves, an error message is displayed, indicating that the player has encountered a wall and cannot move in that direction. The loop continues to the next iteration, prompting the player to enter another move.
- If the entered move is valid (i.e., it is in the list of available moves), the player’s location is updated using the
move_player()
function, passing the player dictionary and the chosen move as arguments. Themove_player()
function updates the player’s location based on the chosen move and appends the current location to the player’s path. - If the player’s updated location matches the door’s location, it means the player has successfully reached the exit door. A victory message is displayed, indicating that the player has escaped the dungeon. The game loop is terminated using the
break
statement, and the game ends. - If the player’s updated location matches the monster’s location, it means the player has encountered the monster and has been eaten. A defeat message is displayed, indicating that the player has lost the game. The game loop is terminated using the
break
statement, and the game ends. - If neither the door nor the monster is encountered, the loop continues to the next iteration, and the player can make another move.
This provides a more detailed explanation of each part of the code and how it functions to create the dungeon game.
What does the following code does?
import logging logging.info("You won't see this") logging.warn("OH NO")
The provided code demonstrates the usage of the Python logging module to log messages with different levels of severity. Here’s what the code does:
- The code begins by importing the
logging
module, which is a built-in module in Python used for logging messages in applications. - The line
logging.info("You won't see this")
logs an informational message with the severity level set toINFO
. By default, the logging module is configured to log messages with a severity level ofWARNING
or higher, so this particular message won’t be displayed in the output. - The line
logging.warn("OH NO")
logs a warning message with the severity level set toWARNING
. Thewarn()
function is an alias forwarning()
in the logging module. This message will be displayed in the output, assuming the logging module is configured to display messages with a severity level ofWARNING
or higher.
The code demonstrates how to use the logging module to log messages with different severity levels. In this case, an informational message is logged but not displayed, while a warning message is logged and displayed if the logging configuration allows it. The actual behavior and display of log messages depend on the configuration of the logging module, which can be customized to suit specific needs.
What are the 6 log levels available?
The logging module in Python provides six built-in log levels that can be used to classify and categorize log messages based on their severity. Here are the six log levels available, listed in increasing order of severity:
-
DEBUG
: TheDEBUG
level is used for detailed and low-level information typically intended for debugging purposes. These messages provide the most detailed information about the program’s execution and are typically not displayed in a production environment. -
INFO
: TheINFO
level is used for general information messages that highlight the progress of the application. These messages provide informative details about the application’s execution, such as the start and completion of certain operations or milestones. They are typically used to give an overview of the application’s state. -
WARNING
: TheWARNING
level is used to indicate potential issues or unexpected situations that are not critical but may require attention. These messages highlight events or conditions that may cause problems in the future or affect the normal behavior of the application. They serve as warnings to indicate that something might be wrong or needs attention. -
ERROR
: TheERROR
level is used to report errors that occurred during the execution of the application. These messages indicate that something has gone wrong, but the application can still continue running. Errors typically represent more severe issues that should be addressed. -
CRITICAL
: TheCRITICAL
level is used to indicate critical errors or failures that may result in the termination or abnormal behavior of the application. These messages represent the highest severity level and typically indicate a severe problem that requires immediate attention. -
FATAL
(Deprecated): TheFATAL
level used to represent log messages of the highest severity, similar toCRITICAL
. However, in recent versions of the logging module,FATAL
has been deprecated, and it is recommended to useCRITICAL
instead.
These log levels allow developers to categorize and prioritize log messages based on their severity, making it easier to identify and troubleshoot issues during application development and debugging.
Explain logging in Python in detail?
Logging in Python is a built-in module that provides a flexible and configurable way to record log messages during the execution of a program. It allows developers to track and monitor the behavior of their application, aiding in debugging, troubleshooting, and analysis. The logging
module offers various features and components for controlling the logging process. Let’s explore the key aspects of logging in Python:
-
Loggers: Loggers are the main entry point for using the logging module. They are responsible for creating log records and sending them to appropriate destinations. Loggers are organized in a hierarchical structure based on their names, forming a logger hierarchy. Loggers can be obtained using the
logging.getLogger(name)
function, wherename
is a string representing the name of the logger. By default, there is a root logger that serves as the parent of all other loggers. -
Log Handlers: Log handlers determine what happens to log records once they are created by the loggers. Handlers are responsible for processing and sending log records to specific destinations, such as the console, files, network sockets, or external services. The
logging
module provides different types of handlers, includingStreamHandler
,FileHandler
,SocketHandler
,SMTPHandler
, and more. Handlers can be associated with loggers using thelogger.addHandler(handler)
method. -
Log Levels: Log levels indicate the severity or importance of a log message. The
logging
module defines six built-in log levels:DEBUG
,INFO
,WARNING
,ERROR
,CRITICAL
, andFATAL
(deprecated). Each log level has a numerical value associated with it, allowing log messages to be filtered and controlled based on their severity. Log levels are used to set the minimum severity level for log records that will be processed and displayed. For example, setting the log level toWARNING
will only display log messages with a severity level ofWARNING
,ERROR
, orCRITICAL
. -
Log Formatters: Log formatters define the structure and content of log messages. They determine how log records are formatted before they are passed to the log handlers. Formatters are responsible for converting log records into human-readable text or other formats. The
logging
module provides theFormatter
class, which can be customized to specify the desired log message format. Formatters are associated with log handlers using thehandler.setFormatter(formatter)
method. -
Logging Configuration: The logging module allows for configuration through various means. It supports both programmatic configuration and configuration via external files, such as INI files, JSON files, or YAML files. Programmatic configuration involves setting the logger hierarchy, handlers, log levels, and formatters explicitly in the code. External configuration files provide a more flexible approach by separating the logging configuration from the code. The
logging.config
module provides functions to load and apply configurations from external files. -
Logging Messages: To log a message, developers use the
logger.log(level, message)
method or one of the convenience methods such aslogger.debug(message)
,logger.info(message)
,logger.warning(message)
,logger.error(message)
, andlogger.critical(message)
. The log level determines the severity of the message. Log messages are processed by the logger and propagated up the logger hierarchy, where they are then handled by the associated handlers based on their log levels. -
Logging to Multiple Destinations: The logging module allows log records to be processed by multiple handlers simultaneously, enabling logging to different destinations. For example, log messages can be displayed on the console using a
StreamHandler
and simultaneously saved to a file using aFileHandler
. This provides flexibility in directing log messages to various outputs based on the application’s needs.
By leveraging
the logging module, developers can incorporate comprehensive logging capabilities into their Python applications. This facilitates better understanding, debugging, and monitoring of the application’s behavior, aiding in the identification and resolution of issues during development, testing, and production stages.
In the following DD game code:
dd_game.py:
~~~
import random
player = {‘location’: None, ‘path’: []}
cells = [(0, 0), (0, 1), (0, 2),
(1, 0), (1, 1), (1, 2),
(2, 0), (2, 1), (2, 2)]
def get_locations():
monster = random.choice(cells)
door = random.choice(cells)
start = random.choice(cells)
if monster == door or monster == start or door == start: monster, door, start = get_locations() return monster, door, start
def get_moves(player):
moves = [‘LEFT’, ‘RIGHT’, ‘UP’, ‘DOWN’]
if player in [(0, 0), (1, 0), (2, 0)]:
moves.remove(‘LEFT’)
if player in [(0, 0), (0, 1), (0, 2)]:
moves.remove(‘UP’)
if player in [(0, 2), (1, 2), (2, 2)]:
moves.remove(‘RIGHT’)
if player in [(2, 0), (2, 1), (2, 2)]:
moves.remove(‘DOWN’)
return moves
def move_player(player, move):
x, y = player[‘location’]
player[‘path’].append((x, y))
if move == ‘LEFT’:
player[‘location’] = x, y - 1
elif move == ‘UP’:
player[‘location’] = x - 1, y
elif move == ‘RIGHT’:
player[‘location’] = x, y + 1
elif move == ‘DOWN’:
player[‘location’] = x + 1, y
return player
def draw_map():
print(‘ _ _ ’)
tile = ‘|{}’
for idx, cell in enumerate(cells):
if idx in [0, 1, 3, 4, 6, 7]:
if cell == player[‘location’]:
print(tile.format(‘X’), end=’’)
elif cell in player[‘path’]:
print(tile.format(‘.’), end=’’)
else:
print(tile.format(‘’), end=’’)
else:
if cell == player[‘location’]:
print(tile.format(‘X|’))
elif cell in player[‘path’]:
print(tile.format(‘.|’))
else:
print(tile.format(‘_|’))
monster, door, player[‘location’] = get_locations()
while True:
moves = get_moves(player[‘location’])
print(“Welcome to the dungeon!”)
print(“You’re currently in room {}”.format(player[‘location’]))
draw_map() print("\nYou can move {}".format(', '.join(moves))) print("Enter QUIT to quit") move = input("> ") move = move.upper() if move == 'QUIT': break if not move in moves: print("\n** Walls are hard! Stop running into them! **\n") continue player = move_player(player, move) if player['location'] == door: print("\n** You escaped! **\n") break elif player['location'] == monster: print("\n** You got eaten! **\n") break else: continue
What does adding the following code to it will do?
import logging
logging.basicConfig(filename=’game.log’, level=logging.DEBUG)
logging.info(‘monster: {}; door: {}; player: {}’.format(monster, door, player[‘location’]))
~~~
The code after adding above will become like this:
dd_game.py:
~~~
import logging
import random
logging.basicConfig(filename=’game.log’, level=logging.DEBUG)
player = {‘location’: None, ‘path’: []}
cells = [(0, 0), (0, 1), (0, 2),
(1, 0), (1, 1), (1, 2),
(2, 0), (2, 1), (2, 2)]
def get_locations():
monster = random.choice(cells)
door = random.choice(cells)
start = random.choice(cells)
if monster == door or monster == start or door == start: monster, door, start = get_locations() return monster, door, start
def get_moves(player):
moves = [‘LEFT’, ‘RIGHT’, ‘UP’, ‘DOWN’]
if player in [(0, 0), (1, 0), (2, 0)]:
moves.remove(‘LEFT’)
if player in [(0, 0), (0, 1), (0, 2)]:
moves.remove(‘UP’)
if player in [(0, 2), (1, 2), (2, 2)]:
moves.remove(‘RIGHT’)
if player in [(2, 0), (2, 1), (2, 2)]:
moves.remove(‘DOWN’)
return moves
def move_player(player, move):
x, y = player[‘location’]
player[‘path’].append((x, y))
if move == ‘LEFT’:
player[‘location’] = x, y - 1
elif move == ‘UP’:
player[‘location’] = x - 1, y
elif move == ‘RIGHT’:
player[‘location’] = x, y + 1
elif move == ‘DOWN’:
player[‘location’] = x + 1, y
return player
def draw_map():
print(‘ _ _ ’)
tile = ‘|{}’
for idx, cell in enumerate(cells):
if idx in [0, 1, 3, 4, 6, 7]:
if cell == player[‘location’]:
print(tile.format(‘X’), end=’’)
elif cell in player[‘path’]:
print(tile.format(‘.’), end=’’)
else:
print(tile.format(‘’), end=’’)
else:
if cell == player[‘location’]:
print(tile.format(‘X|’))
elif cell in player[‘path’]:
print(tile.format(‘.|’))
else:
print(tile.format(‘_|’))
monster, door, player[‘location’] = get_locations()
logging.info(‘monster: {}; door: {}; player: {}’.format(monster, door, player[‘location’]))
while True:
moves = get_moves(player[‘location’])
print(“Welcome to the dungeon!”)
print(“You’re currently in room {}”.format(player[‘location’]))
draw_map() print("\nYou can move {}".format(', '.join(moves))) print("Enter QUIT to quit") move = input("> ") move = move.upper() if move == 'QUIT': break if not move in moves: print("\n** Walls are hard! Stop running into them! **\n") continue player = move_player(player, move) if player['location'] == door: print("\n** You escaped! **\n") break elif player['location'] == monster: print("\n** You got eaten! **\n") break else: continue # use ls to see game.log # then use cat game.log to printout game.log ~~~
Explain the use of logging in this code in detail now.
In the provided code, the logging
module is used to incorporate logging functionality into the game. Let’s break down the use of logging in detail:
- The line
import logging
imports the logging module, which provides the necessary classes and functions for logging. - The line
logging.basicConfig(filename='game.log', level=logging.DEBUG)
configures the logging system. It sets up the basic configuration for logging, including the output filename and the log level. Thefilename
parameter specifies the name of the log file to which the log records will be written. In this case, the log file is named ‘game.log’. Thelevel
parameter sets the minimum log level toDEBUG
, meaning all log records withDEBUG
level or higher will be logged. This line ensures that the log records are written to the ‘game.log’ file. - The line
logging.info('monster: {}; door: {}; player: {}'.format(monster, door, player['location']))
logs an initial information message. It uses thelogging.info()
method to log an informational message with the severity level set toINFO
. The message is formatted using theformat()
method and contains the values ofmonster
,door
, andplayer['location']
. This log record provides initial information about the game, including the locations of the monster, door, and player’s starting position. - Throughout the game loop, logging is not used explicitly. Instead, the code primarily relies on printing messages to the console using the
print()
function to provide feedback to the player. - At the end of the code, there are comments suggesting using commands like
ls
andcat
to view and print the contents of the ‘game.log’ file. These comments hint at the idea of inspecting the log file to examine the logged events and information related to the game.
In summary, the logging module is used to log an initial information message containing the locations of the monster, door, and player’s starting position. The log records are written to a file named ‘game.log’. However, beyond the initial information log, the code primarily relies on printing messages to the console for gameplay interaction. The logging functionality in this code is not extensively used but can be useful for capturing specific events or tracking game-related information for further analysis by inspecting the ‘game.log’ file.
Challenge 1: Log a message with a level of DEBUG. The message can say anything you want.
import logging logging.basicConfig(filename='cc.log', level=logging.DEBUG) Write your code below here
Challenge 2: Log “The French have the Grail” as a WARNING level message.
Challenge 1:
~~~
import logging
logging.basicConfig(filename=’cc.log’, level=logging.DEBUG)
Write your code below here
logging.debug(‘Hi, this is a debug message’)
~~~
Challenge 2:
~~~
import logging
logging.basicConfig(filename=’cc.log’, level=logging.DEBUG)
Write your code below here
logging.debug(‘Hi, this is a debug message’)
logging.warn(“The French have the Grail”)
~~~