Skip to content

Commit

Permalink
More improvements to getting started docs
Browse files Browse the repository at this point in the history
For the most part, this shortens the Getting Started page, which was
getting a little too long to read comfortably and had caveats that
aren't super important.

The cheat sheet does a really great job of "show, don't tell", so
recommend that even more aggressively for beginners. The BankAccount
example was nice, and the cheat sheet was missing a discussion on
inheritance, so move a version of that over there.

Finally, most users of mypy don't need to know the details of typeshed
and stub files, especially not when getting started. So reframe as a
more generic section about types for third party libraries.

Linking python#13681
  • Loading branch information
hauntsaninja committed Feb 1, 2023
1 parent 91e8581 commit 2ab5f6f
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 205 deletions.
65 changes: 46 additions & 19 deletions docs/source/cheat_sheet_py3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Useful built-in types
.. code-block:: python
# For most types, just use the name of the type
# Note that mypy can usually infer the type of a variable from its value,
# so technically these annotations are redundant.
x: int = 1
x: float = 1.0
x: bool = True
Expand Down Expand Up @@ -100,12 +102,18 @@ Functions
def show(value: str, excitement: int = 10) -> None:
print(value + "!" * excitement)
# Note that arguments without a type are dynamically typed (treated as Any)
# and that functions without any annotations not checked
def untyped(x):
x.anything() + 1 + "string" # no errors
# This is how you annotate a callable (function) value
x: Callable[[int, float], float] = f
def register(callback: Callable[[str], int]) -> None: ...
# A generator function that yields ints is secretly just a function that
# returns an iterator of ints, so that's how we annotate it
def g(n: int) -> Iterator[int]:
def gen(n: int) -> Iterator[int]:
i = 0
while i < n:
yield i
Expand Down Expand Up @@ -143,38 +151,57 @@ Classes

.. code-block:: python
class MyClass:
# You can optionally declare instance variables in the class body
attr: int
# This is an instance variable with a default value
charge_percent: int = 100
class BankAccount:
# The "__init__" method doesn't return anything, so it gets return
# type "None" just like any other method that doesn't return anything
def __init__(self) -> None:
...
def __init__(self, account_name: str, initial_balance: int = 0) -> None:
# mypy will infer the correct types for these instance variables
# based on the types of the parameters.
self.account_name = account_name
self.balance = initial_balance
# For instance methods, omit type for "self"
def my_method(self, num: int, str1: str) -> str:
return num * str1
def deposit(self, amount: int) -> None:
self.balance += amount
def withdraw(self, amount: int) -> None:
self.balance -= amount
# User-defined classes are valid as types in annotations
x: MyClass = MyClass()
account: BankAccount = BankAccount("Alice", 400)
def transfer(src: BankAccount, dst: BankAccount, amount: int) -> None:
src.withdraw(amount)
dst.deposit(amount)
# Functions that accept BankAccount also accept any subclass of BankAccount!
class AuditedBankAccount(BankAccount):
# You can optionally declare instance variables in the class body
audit_log: list[str]
# This is an instance variable with a default value
auditor_name: str = "The Spanish Inquisition"
def __init__(self, account_name: str, initial_balance: int = 0) -> None:
super().__init__(account_name, initial_balance)
self.audit_log: list[str] = []
def deposit(self, amount: int) -> None:
self.audit_log.append(f"Deposited {amount}")
self.balance += amount
def withdraw(self, amount: int) -> None:
self.audit_log.append(f"Withdrew {amount}")
self.balance -= amount
# You can also declare the type of an attribute in "__init__"
class Box:
def __init__(self) -> None:
self.items: list[str] = []
audited = AuditedBankAccount("Bob", 300)
transfer(audited, account, 100) # type checks!
# You can use the ClassVar annotation to declare a class variable
class Car:
seats: ClassVar[int] = 4
passengers: ClassVar[list[str]]
# If you want dynamic attributes on your class, have it
# override "__setattr__" or "__getattr__":
# - "__getattr__" allows for dynamic access to names
# - "__setattr__" allows for dynamic assignment to names
# override "__setattr__" or "__getattr__"
class A:
# This will allow assignment to any A.x, if x is the same type as "value"
# (use "value: Any" to allow arbitrary types)
Expand Down
Loading

0 comments on commit 2ab5f6f

Please sign in to comment.