Post on 18-Oct-2020
Type hints w języku Python
Konrad Hałas
4Developers 2018
Plantype hints
dlaczego?
składnia
narzędzia
biblioteki
projekty legacy
def notify_everyone(items): for item in items: item.owner.notify()
def cancel_all(items): notify_everyone(items) ...
Rozwiązaniadokumentacja zewnętrzna
dokumentacja w kodzie (RST, epytext, NumPy, Google)
przepisać wszystko do języka statycznie typowanego
def count_unique_words(text, case_sensitive): ...
def count_unique_words(text: str, case_sensitive: bool) ‑> int: ...
def median(values: list) ‑> float: ...
from typing import List
def median(values: List[float]) ‑> float: ...
from typing import Dict
def count_chars(text: str) ‑> Dict[str, int]: ...
from typing import List
class Point: ...
def calculate_area(vertices: List[Point]) ‑> float: ...
from typing import Optional
def total_amount(order: Order, discount: Optional[Discount]) ‑> Money: ...
from typing import List, Union
def send_confirmation(email: Union[str, List[str]]) ‑> None: ...
from typing import Any
def get_most_frequent(items: list) ‑> Any: ...
from typing import List, TypeVar
T = TypeVar('T')
def get_most_frequent(items: List[T]) ‑> T: ...
from typing import List
class Item: ...
class Basket:
def __init__(self): self.items: List[Item] = []
def add_item(self, item: Item): self.items.append(item)
Narzędziamypy
PyCharm
...
def total_price(items: List[Item], discount: Optional[float] = None) ‑> float: ...
def total_price(items: List[Item], discount: Optional[float] = None) ‑> float: result = 0.0 for item in items: result += item.count * item.product.price result ‑= discount return result
def total_price(items: List[Item], discount: Optional[float] = None) ‑> float: result = 0.0 for item in items: result += item.count * item.product.price result ‑= discount return result
$ mypy ‑‑strict example.pyexample.py:9: error: "Item" has no attribute "count"example.py:10: error: Unsupported operand types for ‑ ("float" and "Optional[float]")
def total_price(items: List[Item], discount: Optional[float] = None) ‑> float: result = 0.0 for item in items: result += item.quantity * item.product.price if discount: result ‑= discount return result
$ mypy ‑‑strict example.py
Bibliotekiinjector
dacite
...
def register_user(user_details: UserDetails): ...
from users.repositories import UsersRepository
def register_user(user_details: UserDetails): ... repository = UsersRepository() repository.create_user(...)
class UsersService:
def __init__(self, repository: UsersRepository): self.repository = repository
def register_user(self, user_details: UserDetails): ... self.repository.create_user(...)
class UsersRepository:
def __init__(self, data_base: DataBase): self.data_base = data_base
def create_user(self, user: User): ... self.data_base.insert(...)
class DataBase:
def __init__(self, session: DataBaseSession): self.session = session
...
session = DataBaseSession(...)data_base = DataBase(session)users_repository = UsersRepository(data_base)users_service = UsersService(users_repository)
class DataBaseSession: ...
class DataBase:
def __init__(self, session: DataBaseSession): ...
class UsersRepository:
def __init__(self, database: DataBase): ...
class UsersService:
def __init__(self, repository: UsersRepository): ...
from injector import inject
class DataBaseSession: ...
class DataBase:
@inject def __init__(self, session: DataBaseSession): ...
class UsersRepository:
@inject def __init__(self, database: DataBase): ...
class UsersService:
@inject def __init__(self, repository: UsersRepository): ...
from injector import Injector
users_service = Injector().get(UsersService)
from dataclasses import dataclass
@dataclassclass User: name: str age: int is_active: bool
user = User(name='John', age=30, is_active=True)
import dacite
data = { 'name': 'John', 'age': 30, 'is_active': True,}
user = dacite.from_dict(data_class=User, data=data)
assert user == User(name='John', age=30, is_active=True)
Type hints w projekcie legacygradual typing
statyczna analiza uruchamiana w ramach CI
wymuszanie type annotations dla:kluczowych modułów
interfejsów
Podsumowaniezrozumiały kod
mniej błędów
nowe możliwości
Zdjęcia
Jacek Kołodziej ‑ http://kolodziejj.info/
Korsan Studio ‑ http://facebook.com/korsanstudio
Narzędzia/biblioteki
mypy ‑ http://mypy‑lang.org
PyCharm ‑ https://www.jetbrains.com/pycharm
dacite ‑ https://github.com/konradhalas/dacite
injector ‑ https://github.com/alecthomas/injector