Skip to content

Commit 1c32867

Browse files
committed
pre-language corrections commit
1 parent e70ea33 commit 1c32867

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

documentation/write-user-documentation/document-your-code-api-docstrings.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,154 @@ the topic that we will uncover later).
310310

311311
### Type hints: complex data types
312312

313+
We can use type hints to describe other objects available in Python.
314+
The little sample of those objects are:
315+
316+
- `List` (= `list`)
317+
- `Dict` (= `dict`)
318+
- `Tuple` (= `tuple`)
319+
- `Set` (= `set`)
320+
321+
How do `pystiche` developers use those objects in their code? Let's take a look at the example below:
322+
323+
```python
324+
from typing import List, Optional
325+
import torch
326+
327+
328+
def _extract_prev(self, idx: int, idcs: List[int]) -> Optional[str]:
329+
...
330+
331+
```
332+
333+
The function has two parameters. Parameter `idcs` is a list of integers. We may write it as `List[int]` or `List` without
334+
square brackets and data type that is within a list.
335+
336+
The `_extract_prev` function returns `Optional` type. It is a special type that is used to describe inputs and output
337+
that can be `None`. There are more interesting types that we can use in our code:
338+
339+
- `Union` – we can use it to describe a variable that can be of multiple types, the common example could be:
340+
341+
```python
342+
from typing import List, Union
343+
import numpy as np
344+
import pandas as pd
345+
346+
347+
def process_data(data: Union[np.ndarray, pd.DataFrame, List]) -> np.ndarray:
348+
...
349+
350+
```
351+
352+
What's the problem with the example above? With more data types that can be passed into parameter `data`, the function definition
353+
becomes unreadable. We have two solutions for this issue. The first one is to use `Any` type that is a wildcard type:
354+
355+
```python
356+
from typing import Any
357+
358+
359+
def process_data(data: Any) -> np.ndarray:
360+
...
361+
362+
```
363+
364+
The second solution is to think what is a high level representation of passed data types. The examples are:
365+
366+
- `Sequence` – we can use it to describe a variable that is a sequence of elements. Sequential are `list`, `tuple`, `range` and `str`.
367+
- `Iterable` – we can use it to describe a variable that is iterable. Iterables are `list`, `tuple`, `range`, `str`, `dict` and `set`.
368+
- `Mapping` – we can use it to describe a variable that is a mapping. Mappings are `dict` and `defaultdict`.
369+
- `Hashable` – we can use it to describe a variable that is hashable. Hashables are `int`, `float`, `str`, `tuple` and `frozenset`.
370+
- `Collection` - we can use it to describe a variable that is a collection. Collections are `list`, `tuple`, `range`, `str`, `dict`, `set` and `frozenset`.
371+
372+
Thus, the function could look like:
373+
374+
```python
375+
from typing import Iterable
376+
377+
378+
def process_data(data: Iterable) -> np.ndarray:
379+
...
380+
381+
```
382+
383+
### Type hints: special typing objects
384+
385+
The `typing` module provides us with more objects that we can use to describe our variables.
386+
Interesting object is `Callable` that we can use to describe a variable that is a function. Usually,
387+
when we write decorators or wrappers, we use `Callable` type. The example in the context of `pystiche` package:
388+
389+
```python
390+
from typing import Callable
391+
392+
393+
def _deprecate(fn: Callable) -> Callable:
394+
...
395+
396+
397+
```
398+
399+
The `Callable`can be used as a single word or as a word with square brackets that has two parameters: `Callable[[arg1, arg2], return_type]`.
400+
The first parameter is a list of arguments, the second one is a return type.
401+
402+
There is one more important case around type hints. Sometimes we want to describe a variable that comes from within
403+
our package. Usually we can do it without any problems:
404+
405+
```python
406+
from my_package import my_data_class
407+
408+
409+
def my_function(data: my_data_class) -> None:
410+
...
411+
412+
```
413+
414+
and it will work fine. But we may encounter *circual imports* that are a problem. What is a *circular import*?
415+
It is a case when we want to import module B into module A but module A is already imported into module B.
416+
It seems like we are importing the same module twice into itself. The issue is rare when we program without type
417+
hinting. However, with type hints it could be tedious.
418+
419+
Thus, if you encounter this error:
420+
421+
```python
422+
from my_package import my_data_class
423+
424+
425+
def my_function(data: my_data_class) -> None:
426+
...
427+
428+
```
429+
430+
```shell
431+
ImportError: cannot import name 'my_data_class' from partially initialized module 'my_package' (most likely due to a circular import) (/home/user/my_package/__init__.py)
432+
```
433+
434+
Then you should use `typing.TYPE_CHECKING` clause to avoid circular imports. The example:
435+
436+
```python
437+
from __future__ import annotations
438+
from typing import TYPE_CHECKING
439+
440+
if TYPE_CHECKING:
441+
from my_package import my_data_class
442+
443+
444+
def my_function(data: my_data_class) -> None:
445+
...
446+
447+
```
448+
449+
Unfortunately, the solution is dirty because we have to
450+
use `if TYPE_CHECKING` clause and `from __future__ import annotations` import to make it work! Type hinting
451+
is not only roses and butterflies!
452+
453+
### Type hinting: final remarks and tools
454+
455+
There are few tools designed for static type checking. The most popular one is [`mypy`](https://mypy.readthedocs.io/en/stable/).
456+
It's a good idea to add it to your Continuous Integration (CI) pipeline.
457+
Other tools are integrated with popular IDEs like `PyCharm` or `VSCode`, most of them are based on `mypy` logic.
458+
459+
At this point, we have a good understanding of type hints and how to use them in our code. There is one last thing to
460+
remember. **Type hints are not required in all our functions and we can introduce those gradually, it won't damage our code**.
461+
It is very convenient way of using this extraordinary feature!
462+
313463

0 commit comments

Comments
 (0)