Skip to content

Commit f791129

Browse files
authored
Add Type Hints section to More Language Features lecture (#488)
* Add Type Hints section to More Language Features lecture Adds a compact section on Python type hints to the python_advanced_features lecture, covering: - Basic syntax (function parameters, return types, variable annotations) - Common built-in types and container type syntax - Demonstration that hints are not enforced at runtime - Why type hints matter (readability, IDE support, error checking, LLM code) - Connection to scientific Python and JIT compilation Closes #343 * Minor: join note text into single paragraph line * Fix section heading capitalization per qe-writing-006 Lowercase section headings to capitalize only the first word and proper nouns (e.g., Python), per QuantEcon style rules. * Fix heading capitalization lecture-wide per qe-writing-006 Apply sentence case to all section headings (##, ###, ####) across the entire lecture, not just the new Type Hints section. Only the first word and proper nouns (e.g., Python) are capitalized. * Fix bold/italic usage per qe-writing-005 style rule Change bold to italic for emphasis (not definitions): - 'skip it on first pass' (pre-existing) - 'ignored by the Python interpreter at runtime' - 'not enforced' - 'clarity and tooling support' * Address Copilot review: fix version text and type mismatch example
1 parent ed4ba79 commit f791129

File tree

1 file changed

+118
-16
lines changed

1 file changed

+118
-16
lines changed

lectures/python_advanced_features.md

Lines changed: 118 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ kernelspec:
2222

2323
## Overview
2424

25-
With this last lecture, our advice is to **skip it on first pass**, unless you have a burning desire to read it.
25+
With this last lecture, our advice is to *skip it on first pass*, unless you have a burning desire to read it.
2626

2727
It's here
2828

2929
1. as a reference, so we can link back to it when required, and
3030
1. for those who have worked through a number of applications, and now want to learn more about the Python language
3131

32-
A variety of topics are treated in the lecture, including iterators, decorators and descriptors, and generators.
32+
A variety of topics are treated in the lecture, including iterators, type hints, decorators and descriptors, and generators.
3333

34-
## Iterables and Iterators
34+
## Iterables and iterators
3535

3636
```{index} single: Python; Iteration
3737
```
@@ -130,7 +130,7 @@ next(nikkei_data)
130130
next(nikkei_data)
131131
```
132132

133-
### Iterators in For Loops
133+
### Iterators in for loops
134134

135135
```{index} single: Python; Iterators
136136
```
@@ -286,14 +286,14 @@ tags: [raises-exception]
286286
max(y)
287287
```
288288

289-
## `*` and `**` Operators
289+
## `*` and `**` operators
290290

291291
`*` and `**` are convenient and widely used tools to unpack lists and tuples and to allow users to define functions that take arbitrarily many arguments as input.
292292

293293
In this section, we will explore how to use them and distinguish their use cases.
294294

295295

296-
### Unpacking Arguments
296+
### Unpacking arguments
297297

298298
When we operate on a list of parameters, we often need to extract the content of the list as individual arguments instead of a collection when passing them into functions.
299299

@@ -400,7 +400,7 @@ To summarize, when `*list`/`*tuple` and `**dictionary` are passed into *function
400400

401401
The difference is that `*` will unpack lists and tuples into *positional arguments*, while `**` will unpack dictionaries into *keyword arguments*.
402402

403-
### Arbitrary Arguments
403+
### Arbitrary arguments
404404

405405
When we *define* functions, it is sometimes desirable to allow users to put as many arguments as they want into a function.
406406

@@ -459,7 +459,109 @@ Overall, `*args` and `**kargs` are used when *defining a function*; they enable
459459

460460
The difference is that functions with `*args` will be able to take *positional arguments* with an arbitrary size, while `**kargs` will allow functions to take arbitrarily many *keyword arguments*.
461461

462-
## Decorators and Descriptors
462+
## Type hints
463+
464+
```{index} single: Python; Type Hints
465+
```
466+
467+
Python is a *dynamically typed* language, meaning you don't need to declare the types of variables.
468+
469+
(See our {doc}`earlier discussion <need_for_speed>` of dynamic versus static types.)
470+
471+
However, Python supports optional **type hints** (also called type annotations) that allow you to indicate the expected types of variables, function parameters, and return values.
472+
473+
Type hints were introduced starting in Python 3.5 and have evolved in subsequent versions.
474+
All of the syntax shown here works in Python 3.9 and later.
475+
476+
```{note}
477+
Type hints are *ignored by the Python interpreter at runtime* --- they do not affect how your code executes. They are purely informational and serve as documentation for humans and tools.
478+
```
479+
480+
### Basic syntax
481+
482+
Type hints use the colon `:` to annotate variables and parameters, and the arrow `->` to annotate return types.
483+
484+
Here is a simple example:
485+
486+
```{code-cell} python3
487+
def greet(name: str, times: int) -> str:
488+
return (name + '! ') * times
489+
490+
greet('hello', 3)
491+
```
492+
493+
In this function definition:
494+
495+
- `name: str` indicates `name` is expected to be a string
496+
- `times: int` indicates `times` is expected to be an integer
497+
- `-> str` indicates the function returns a string
498+
499+
You can also annotate variables directly:
500+
501+
```{code-cell} python3
502+
x: int = 10
503+
y: float = 3.14
504+
name: str = 'Python'
505+
```
506+
507+
### Common types
508+
509+
The most frequently used type hints are the built-in types:
510+
511+
| Type | Example |
512+
|-----------|----------------------------------|
513+
| `int` | `x: int = 5` |
514+
| `float` | `x: float = 3.14` |
515+
| `str` | `x: str = 'hello'` |
516+
| `bool` | `x: bool = True` |
517+
| `list` | `x: list = [1, 2, 3]` |
518+
| `dict` | `x: dict = {'a': 1}` |
519+
520+
For containers, you can specify the types of their elements:
521+
522+
```{code-cell} python3
523+
prices: list[float] = [9.99, 4.50, 2.89]
524+
counts: dict[str, int] = {'apples': 3, 'oranges': 5}
525+
```
526+
527+
### Hints don't enforce types
528+
529+
An important point for new Python programmers: type hints are *not enforced* at runtime.
530+
531+
Python will not raise an error if you pass the "wrong" type:
532+
533+
```{code-cell} python3
534+
def add(x: int, y: int) -> int:
535+
return x + y
536+
537+
# Passes floats — Python doesn't complain
538+
add(1.5, 2.7)
539+
```
540+
541+
The hints say `int`, but Python happily accepts `float` arguments and returns `4.2` --- also not an `int`.
542+
543+
This is a key difference from statically typed languages like C or Java, where mismatched types cause compilation errors.
544+
545+
### Why use type hints?
546+
547+
If Python ignores them, why bother?
548+
549+
1. **Readability**: Type hints make function signatures self-documenting. A reader immediately knows what types a function expects and returns.
550+
2. **Editor support**: IDEs like VS Code use type hints to provide better autocompletion, error detection, and inline documentation.
551+
3. **Error checking**: Tools like [mypy](https://mypy.readthedocs.io/) and [pyrefly](https://pyrefly.org/) analyze type hints to catch bugs *before* you run your code.
552+
4. **LLM-generated code**: Large language models frequently produce code with type hints, so understanding the syntax helps you read and use their output.
553+
554+
### Type hints in scientific Python
555+
556+
Type hints connect to the {doc}`need for speed <need_for_speed>` discussion:
557+
558+
* High-performance libraries like [JAX](https://jax.readthedocs.io/) and [Numba](https://numba.pydata.org/) rely on knowing variable types to compile fast machine code.
559+
* While these libraries infer types at runtime rather than reading Python type hints directly, the *concept* is the same --- explicit type information enables optimization.
560+
* As the Python ecosystem evolves, the connection between type hints and performance tools is expected to grow.
561+
562+
For now, the main benefit of type hints in day-to-day Python is *clarity and tooling support*, which becomes increasingly valuable as programs grow in size.
563+
564+
## Decorators and descriptors
463565

464566
```{index} single: Python; Decorators
465567
```
@@ -485,7 +587,7 @@ It's very easy to say what decorators do.
485587

486588
On the other hand it takes a bit of effort to explain *why* you might use them.
487589

488-
#### An Example
590+
#### An example
489591

490592
Suppose we are working on a program that looks something like this
491593

@@ -576,7 +678,7 @@ Now the behavior of `f` is as we desire, and the same is true of `g`.
576678

577679
At the same time, the test logic is written only once.
578680

579-
#### Enter Decorators
681+
#### Enter decorators
580682

581683
```{index} single: Python; Decorators
582684
```
@@ -677,7 +779,7 @@ In the last two lines we see that `miles` and `kms` are out of sync.
677779

678780
What we really want is some mechanism whereby each time a user sets one of these variables, *the other is automatically updated*.
679781

680-
#### A Solution
782+
#### A solution
681783

682784
In Python, this issue is solved using *descriptors*.
683785

@@ -728,7 +830,7 @@ car.kms
728830

729831
Yep, that's what we want --- `car.kms` is automatically updated.
730832

731-
#### How it Works
833+
#### How it works
732834

733835
The names `_miles` and `_kms` are arbitrary names we are using to store the values of the variables.
734836

@@ -746,7 +848,7 @@ For example, after `car` is created as an instance of `Car`, the object `car.mil
746848
Being a property, when we set its value via `car.miles = 6000` its setter
747849
method is triggered --- in this case `set_miles`.
748850

749-
#### Decorators and Properties
851+
#### Decorators and properties
750852

751853
```{index} single: Python; Decorators
752854
```
@@ -799,7 +901,7 @@ A generator is a kind of iterator (i.e., it works with a `next` function).
799901

800902
We will study two ways to build generators: generator expressions and generator functions.
801903

802-
### Generator Expressions
904+
### Generator expressions
803905

804906
The easiest way to build generators is using *generator expressions*.
805907

@@ -855,7 +957,7 @@ In fact, we can omit the outer brackets in this case
855957
sum(x * x for x in range(10))
856958
```
857959

858-
### Generator Functions
960+
### Generator functions
859961

860962
```{index} single: Python; Generator Functions
861963
```
@@ -1002,7 +1104,7 @@ def g(x):
10021104
x = x * x
10031105
```
10041106

1005-
### Advantages of Iterators
1107+
### Advantages of iterators
10061108

10071109
What's the advantage of using an iterator here?
10081110

0 commit comments

Comments
 (0)