@@ -4,7 +4,7 @@ Getting started
4
4
===============
5
5
6
6
This chapter introduces some core concepts of mypy, including function
7
- annotations, the :py:mod: `typing ` module, library stubs , and more.
7
+ annotations, the :py:mod: `typing ` module, stub files , and more.
8
8
9
9
Be sure to read this chapter carefully, as the rest of the documentation
10
10
may not make much sense otherwise.
@@ -317,28 +317,119 @@ syntax like so:
317
317
# If you're using Python 3.6+
318
318
my_global_dict: Dict[int , float ] = {}
319
319
320
- .. _stubs-intro :
321
320
322
- Library stubs and typeshed
323
- **************************
321
+ Types and classes
322
+ *****************
323
+
324
+ So far, we've only seen examples of pre-existing types like the ``int ``
325
+ or ``float `` builtins, or generic types from ``collections.abc `` and
326
+ ``typing ``, such as ``Iterable ``. However, these aren't the only types you can
327
+ use: in fact, you can use any Python class as a type!
328
+
329
+ For example, suppose you've defined a custom class representing a bank account:
330
+
331
+ .. code-block :: python
332
+
333
+ class BankAccount :
334
+ # Note: It is ok to omit type hints for the "self" parameter.
335
+ # Mypy will infer the correct type.
336
+
337
+ def __init__ (self , account_name : str , initial_balance : int = 0 ) -> None :
338
+ # Note: Mypy will infer the correct types of your fields
339
+ # based on the types of the parameters.
340
+ self .account_name = account_name
341
+ self .balance = initial_balance
342
+
343
+ def deposit (self , amount : int ) -> None :
344
+ self .balance += amount
345
+
346
+ def withdraw (self , amount : int ) -> None :
347
+ self .balance -= amount
348
+
349
+ def overdrawn (self ) -> bool :
350
+ return self .balance < 0
351
+
352
+ You can declare that a function will accept any instance of your class
353
+ by simply annotating the parameters with ``BankAccount ``:
354
+
355
+ .. code-block :: python
356
+
357
+ def transfer (src : BankAccount, dst : BankAccount, amount : int ) -> None :
358
+ src.withdraw(amount)
359
+ dst.deposit(amount)
360
+
361
+ account_1 = BankAccount(' Alice' , 400 )
362
+ account_2 = BankAccount(' Bob' , 200 )
363
+ transfer(account_1, account_2, 50 )
364
+
365
+ In fact, the ``transfer `` function we wrote above can accept more then just
366
+ instances of ``BankAccount ``: it can also accept any instance of a *subclass *
367
+ of ``BankAccount ``. For example, suppose you write a new class that looks like this:
368
+
369
+ .. code-block :: python
370
+
371
+ class AuditedBankAccount (BankAccount ):
372
+ def __init__ (self , account_name : str , initial_balance : int = 0 ) -> None :
373
+ super ().__init__ (account_name, initial_balance)
374
+ self .audit_log: list[str ] = []
324
375
325
- Mypy uses library *stubs * to type check code interacting with library
326
- modules, including the Python standard library. A library stub defines
327
- a skeleton of the public interface of the library, including classes,
328
- variables and functions, and their types. Mypy ships with stubs for
329
- the standard library from the `typeshed
330
- <https://github.com/python/typeshed> `_ project, which contains library
331
- stubs for the Python builtins, the standard library, and selected
332
- third-party packages.
376
+ def deposit (self , amount : int ) -> None :
377
+ self .audit_log.append(f " Deposited { amount} " )
378
+ self .balance += amount
333
379
334
- For example, consider this code:
380
+ def withdraw (self , amount : int ) -> None :
381
+ self .audit_log.append(f " Withdrew { amount} " )
382
+ self .balance -= amount
383
+
384
+ Since ``AuditedBankAccount `` is a subclass of ``BankAccount ``, we can directly pass
385
+ in instances of it into our ``transfer `` function:
335
386
336
387
.. code-block :: python
337
388
338
- x = chr (4 )
389
+ audited = AuditedBankAccount(' Charlie' , 300 )
390
+ transfer(account_1, audited, 100 ) # Type checks!
391
+
392
+ This behavior is actually a fundamental aspect of the PEP 484 type system: when
393
+ we annotate some variable with a type ``T ``, we are actually telling mypy that
394
+ variable can be assigned an instance of ``T ``, or an instance of a *subclass * of ``T ``.
395
+ The same rule applies to type hints on parameters or fields.
396
+
397
+ See :ref: `class-basics ` to learn more about how to work with code involving classes.
398
+
399
+
400
+ .. _stubs-intro :
401
+
402
+ Stubs files and typeshed
403
+ ************************
339
404
340
- Without a library stub, mypy would have no way of inferring the type of ``x ``
341
- and checking that the argument to :py:func: `chr ` has a valid type.
405
+ Mypy also understands how to work with classes found in the standard library.
406
+ For example, here is a function which uses the ``Path `` object from the
407
+ `pathlib standard library module <https://docs.python.org/3/library/pathlib.html >`_:
408
+
409
+ .. code-block :: python
410
+
411
+ from pathlib import Path
412
+
413
+ def load_template (template_path : Path, name : str ) -> str :
414
+ # Mypy understands that 'file_path.read_text()' returns a str...
415
+ template = template_path.read_text()
416
+
417
+ # ...so understands this line type checks.
418
+ return template.replace(' USERNAME' , name)
419
+
420
+ This behavior may surprise you if you're familiar with how
421
+ Python internally works. The standard library does not use type hints
422
+ anywhere, so how did mypy know that ``Path.read_text() `` returns a ``str ``,
423
+ or that ``str.replace(...) `` accepts exactly two ``str `` arguments?
424
+
425
+ The answer is that mypy comes bundled with *stub files * from the
426
+ the `typeshed <https://github.com/python/typeshed >`_ project, which
427
+ contains stub files for the Python builtins, the standard library,
428
+ and selected third-party packages.
429
+
430
+ A *stub file * is a file containing a skeleton of the public interface
431
+ of that Python module, including classes, variables, functions -- and
432
+ most importantly, their types.
342
433
343
434
Mypy complains if it can't find a stub (or a real module) for a
344
435
library module that you import. Some modules ship with stubs or inline
@@ -349,7 +440,7 @@ the stubs for the ``requests`` package like this:
349
440
350
441
.. code-block :: shell
351
442
352
- python3 -m pip install types-requests
443
+ $ python3 -m pip install types-requests
353
444
354
445
The stubs are usually packaged in a distribution named
355
446
``types-<distribution> ``. Note that the distribution name may be
@@ -363,17 +454,8 @@ often suggest the name of the stub distribution:
363
454
prog.py:1: note: Hint: "python3 -m pip install types-PyYAML"
364
455
...
365
456
366
- .. note ::
367
-
368
- Starting in mypy 0.900, most third-party package stubs must be
369
- installed explicitly. This decouples mypy and stub versioning,
370
- allowing stubs to updated without updating mypy. This also allows
371
- stubs not originally included with mypy to be installed. Earlier
372
- mypy versions included a fixed set of stubs for third-party
373
- packages.
374
-
375
457
You can also :ref: `create
376
- stubs <stub-files>` easily. We discuss ways of silencing complaints
458
+ stubs <stub-files>` easily. We discuss strategies for handling errors
377
459
about missing stubs in :ref: `ignore-missing-imports `.
378
460
379
461
Configuring mypy
0 commit comments