Skip to content

Commit d6a4603

Browse files
committed
More improvements to getting started docs (#14572)
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 #13681
1 parent 319980b commit d6a4603

File tree

5 files changed

+111
-207
lines changed

5 files changed

+111
-207
lines changed

docs/source/cheat_sheet_py3.rst

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ Useful built-in types
3434

3535
.. code-block:: python
3636
37-
# For most types, just use the name of the type
37+
# For most types, just use the name of the type.
38+
# Note that mypy can usually infer the type of a variable from its value,
39+
# so technically these annotations are redundant
3840
x: int = 1
3941
x: float = 1.0
4042
x: bool = True
@@ -100,12 +102,18 @@ Functions
100102
def show(value: str, excitement: int = 10) -> None:
101103
print(value + "!" * excitement)
102104
105+
# Note that arguments without a type are dynamically typed (treated as Any)
106+
# and that functions without any annotations not checked
107+
def untyped(x):
108+
x.anything() + 1 + "string" # no errors
109+
103110
# This is how you annotate a callable (function) value
104111
x: Callable[[int, float], float] = f
112+
def register(callback: Callable[[str], int]) -> None: ...
105113
106114
# A generator function that yields ints is secretly just a function that
107115
# returns an iterator of ints, so that's how we annotate it
108-
def g(n: int) -> Iterator[int]:
116+
def gen(n: int) -> Iterator[int]:
109117
i = 0
110118
while i < n:
111119
yield i
@@ -143,38 +151,57 @@ Classes
143151

144152
.. code-block:: python
145153
146-
class MyClass:
147-
# You can optionally declare instance variables in the class body
148-
attr: int
149-
# This is an instance variable with a default value
150-
charge_percent: int = 100
151-
154+
class BankAccount:
152155
# The "__init__" method doesn't return anything, so it gets return
153156
# type "None" just like any other method that doesn't return anything
154-
def __init__(self) -> None:
155-
...
157+
def __init__(self, account_name: str, initial_balance: int = 0) -> None:
158+
# mypy will infer the correct types for these instance variables
159+
# based on the types of the parameters.
160+
self.account_name = account_name
161+
self.balance = initial_balance
156162
157163
# For instance methods, omit type for "self"
158-
def my_method(self, num: int, str1: str) -> str:
159-
return num * str1
164+
def deposit(self, amount: int) -> None:
165+
self.balance += amount
166+
167+
def withdraw(self, amount: int) -> None:
168+
self.balance -= amount
160169
161170
# User-defined classes are valid as types in annotations
162-
x: MyClass = MyClass()
171+
account: BankAccount = BankAccount("Alice", 400)
172+
def transfer(src: BankAccount, dst: BankAccount, amount: int) -> None:
173+
src.withdraw(amount)
174+
dst.deposit(amount)
175+
176+
# Functions that accept BankAccount also accept any subclass of BankAccount!
177+
class AuditedBankAccount(BankAccount):
178+
# You can optionally declare instance variables in the class body
179+
audit_log: list[str]
180+
# This is an instance variable with a default value
181+
auditor_name: str = "The Spanish Inquisition"
182+
183+
def __init__(self, account_name: str, initial_balance: int = 0) -> None:
184+
super().__init__(account_name, initial_balance)
185+
self.audit_log: list[str] = []
186+
187+
def deposit(self, amount: int) -> None:
188+
self.audit_log.append(f"Deposited {amount}")
189+
self.balance += amount
190+
191+
def withdraw(self, amount: int) -> None:
192+
self.audit_log.append(f"Withdrew {amount}")
193+
self.balance -= amount
163194
164-
# You can also declare the type of an attribute in "__init__"
165-
class Box:
166-
def __init__(self) -> None:
167-
self.items: list[str] = []
195+
audited = AuditedBankAccount("Bob", 300)
196+
transfer(audited, account, 100) # type checks!
168197
169198
# You can use the ClassVar annotation to declare a class variable
170199
class Car:
171200
seats: ClassVar[int] = 4
172201
passengers: ClassVar[list[str]]
173202
174203
# If you want dynamic attributes on your class, have it
175-
# override "__setattr__" or "__getattr__":
176-
# - "__getattr__" allows for dynamic access to names
177-
# - "__setattr__" allows for dynamic assignment to names
204+
# override "__setattr__" or "__getattr__"
178205
class A:
179206
# This will allow assignment to any A.x, if x is the same type as "value"
180207
# (use "value: Any" to allow arbitrary types)

0 commit comments

Comments
 (0)