By the end of this lab, you will be able to:
- Write correct
importstatements using both absolute and relative import syntax - Refactor code into separate modules while maintaining proper import relationships between files
- Organize Python code using packages with init.py files
- Use init.py files to control package initialization and automatic imports
- Apply relative imports within package hierarchies
- Diagnose and resolve common import errors including
NameError,AttributeError, andModuleNotFoundError
- Follow these setup instructions, then return here to get started
- Use the integrated terminal to navigate to the
import-warmup/directory if you're not already there - Verify that your Codespace is set up correctly by running
python3.14 --version. The output should bePython 3.14.0.
This exercise introduces the types of errors you'll see when your imports are missing or incorrect, as well as providing practice on fixing them.
Run the run_exercises.py file:
python3.14 run_exercises.pyYou'll see the following NameError:
File "/workspaces/import-warmup/run-exercises.py", line 1, in <module>
my_portfolio = portfolio.data.create_portfolio("Retirement")
^^^^^^^^^
NameError: name 'portfolio' is not defined
Add import statement(s) to the run-exercises.py file so that the above NameError is resolved. Do not change any other code in the file.
If you've succeeded, the NameError: name 'portfolio' is not defined error is replaced with a new error: NameError: name 'make_asset' is not defined
- What's the difference between a
NameErrorand anAttributeErrorin the context of imports? - Why does Python require explicit import statements rather than automatically finding all available code?
The error message tells you that make_asset is not defined. Find where make_asset is defined and add the necessary import statement(s) to the file(s) that use it. Do not modify any of the existing code.
You'll know you succeeded when the above NameError is replaced with the following error:
Traceback (most recent call last):
File "/workspaces/import-warmup/run-exercises.py", line 4, in <module>
portfolio.report.print_report(my_portfolio)
^^^^^^^^^^^^^^^^
AttributeError: module 'portfolio' has no attribute 'report'
Add import statement(s) to run-exercises.py so that the above AttributeError is resolved. Do not modify any of the existing code.
You'll know you succeeded when running python3.14 run_exercises.py results in no errors and you have a report printed to the screen.
In this exercise, you'll see how refactoring code can break existing import statements, and how to refactor import statements to suit the changes.
Review the code in portfolio/assets.py and portfolio/report.py. Notice the following:
- The
calculate_asset_value()andcalculate_portfolio_value()functions are found in both files. - A recent code review suggested that neither function belongs in
portfolio/assets.pybecause they act upon an asset rather than being part of an asset. They also don't belong inportfolio/report.pybecause they're not about a report, either.
Create a new module called portfolio/metrics.py. Refactor the two functions into this file and import them back into the original file(s). Do not change any other code in the original file(s) (i.e., make the import statements work with the existing code rather than changing the existing code to make your import statements work.)
You'll know you succeeded if:
- The
calculate_asset_value()andcalculate_portfolio_value()functions appear only inmetrics.py run-exercises.pyruns without errors and prints the portfolio report.
- Why is it a good idea to move repeated code into modules?
- What would happen if the module got moved into another subfolder? How would you correct these errors?
Our code is looking good, but having all of the modules lumped into the portfolio folder makes it hard to understand how each module is linked to the others. Packages are one way to provide organization to your codebase, but the imports you use change when this structure changes.
This exercise will give you practice with using import statements within packages, specifically with using relative imports.
Within the portfolio/ directory, add a subdirectory called core/. Add an empty __init__.py file to core/.
Move assets.py and metrics.py into core/. Your directory structure should look like this when you're finished:
portfolio/
├── core/
│ ├── __init__.py
│ ├── assets.py
│ └── metrics.py
├── data.py
└── report.py
At this point, executing run-exercises.py will result in a ModuleNotFoundError.
- Is
core/a regular package or a namespace package? Does it matter which one it is? Why or why not? What benefits does one type have over the other?
When working with packages, you can import using:
- Absolute imports: specify the entire package structure in the import statement. They're generally preferred because they're unambiguous.
- Relative imports: use dots (
.) to indicate the current package level. They're sometimes used within packages because they remain valid if the package is renamed.
Update your imports in all files so that all errors are resolved, and the report prints as expected. Use relative imports rather than absolute imports wherever possible (i.e., whenever a file containing an import statement is in a subpackage).
You'll know you succeeded when the report prints as expected, with no errors thrown.
While the program runs as expected, it seems strange to have to explicitly import report.py and data.py into the main program since we'll likely always want to print a report, and we'd need a portfolio created first. Instead, the relevant files should be always imported when importing the enclosing package, without explicitly stating it in every file. This is a job for __init__.py!
Modify the project so that you no longer have to explicitly import the report.py and data.py files into run-exercises.py.
You'll know you succeeded when:
reportanddataare no longer explicitly imported intorun-exercises.py- The report prints as expected
- What are the trade-offs of using
__init__.pyfor automatic imports versus requiring explicit imports in every file? - When might automatic imports be problematic? (Consider: circular dependencies, namespace pollution, import performance)