Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Type Hinting in Python (Stockholm Python User Group 2017-03-28)

Type Hinting in Python (Stockholm Python User Group 2017-03-28)

eriklundberg

March 28, 2017
Tweet

Other Decks in Technology

Transcript

  1. $ javac URLUtil.java $ java URLUtil true $ python urlutil.py

    True Superquick Java Tutorial™ (1/2) class URLUtil: ssl_protocols = ['https', 'imaps', 'ssh'] def url_uses_ssl(self, url): return url.startswith(tuple(URLUtil.ssl_protocols)) print(URLUtil().url_uses_ssl('https://pwny.se')) urlutil.py public class URLUtil { String[] sslProtocols = {"https", "imaps", "ssh"}; public boolean urlUsesSSL(String url) { for(String protocol : sslProtocols) { if (url.startsWith(protocol)) { return true; } } return false; } public static void main(String[] args) throws Exception { System.out.println( new URLUtil().urlUsesSSL("https://pwny.se") ); } } URLUtil.java
  2. $ javac URLUtil.java URLUtil.java:18: error: incompatible types: String cannot be

    converted to URL new URLUtil().urlUsesSSL("https://pwny.se") $ python urlutil.py Traceback (most recent call last): File "urlutil.py", line 10, in <module> print(URLUtil().url_uses_ssl('https://pwny.se')) File "urlutil.py", line 7, in url_uses_ssl return url.scheme in URLUtil.ssl_protocols AttributeError: 'str' object has no attribute 'scheme' Superquick Java Tutorial™ (2/2) class URLUtil: ssl_protocols = ['https', 'imaps', 'ssh'] def url_uses_ssl(self, url): return url.scheme in URLUtil.ssl_protocols print(URLUtil().url_uses_ssl('https://pwny.se')) urlutil.py import java.net.URL; public class URLUtil { String[] sslProtocols = {"https", "imaps", "ssh"}; public boolean urlUsesSSL(URL url) { for(String protocol : sslProtocols) { if (protocol.equals(url.getProtocol())) { return true; } } return false; } public static void main(String[] args) throws Exception { System.out.println( new URLUtil().urlUsesSSL("https://pwny.se") ); } } URLUtil.java
  3. PEP 3107, Function Annotations def foo(bar: <annotation>, baz: <annotation>): ->

    <annotation> def get_temperature( location: ’the location to fetch temperature for’, time: ’the time to fetch temperature for’ ): -> ’the temperature in Celsius’ def test_aggregation( retrieval_mock: mock.patch(’connection.retrieve’), process_mock: mock.patch(’data.process’) ):
  4. PEP 484, Type Hinting def foo(bar: <type>, baz: <type>): ->

    <type> Traceback (most recent call last): File ”urlutil.py", line 11, in <module> print(URLUtil().url_uses_ssl('https://pwny.se')) File "urlutil.py", line 8, in url_uses_ssl return url.scheme in URLUtil.ssl_protols AttributeError: 'str' object has no attribute 'scheme' from urllib.parse import ParseResult from typing import List class URLUtil: ssl_protocols: List[str] = ['https', 'imaps', 'ssh'] def url_uses_ssl(self, url: ParseResult) -> bool: return url.scheme in URLUtil.ssl_protocols print(URLUtil().url_uses_ssl('https://pwny.se')) class URLUtilTwo: ssl_protocols = ['https', 'imaps', 'ssh'] def url_uses_ssl(self, url): return url.scheme in URLUtilTwo.ssl_protocols print(URLUtilTwo().url_uses_ssl('https://pwny.se')) urlutil.py urlutil.py
  5. mypy $ pip install mypy Collecting mypy Using cached mypy-0.501-py3-none-any.whl

    Collecting typed-ast<1.1.0,>=1.0.0 (from mypy) Using cached typed_ast-1.0.2-cp36-cp36m-macosx_10_11_x86_64.whl Installing collected packages: typed-ast, mypy Successfully installed mypy-0.501 typed-ast-1.0.2 $ mypy urlutil.py urlutil.py:11: error: Argument 1 to "url_uses_ssl" of "URLUtil" has incompatible type "str"; expected "ParseResult"
  6. The typing Module • List[<item types>] • Tuple[<item1_type>, <item2_type>, …]

    • Dict[<key_type>, <value_type>] • Callable[[<arg1_type>, <arg2_type>, …], <return_type>] • Union[<type1>, <type2>, …] • Optional[<type>] = Union[<type>, None] • Any • … and many more!
  7. Type Hinted Example from typing import Dict, List, Optional, Union

    from anyjson import deserialize import requests API_KEY: str = '<key>' def fetch_departures(station_id: int, timespan: Optional[int]=None) \ -> List[Dict[str, Union[str, int]]]: timespan = timespan or 30 api_url = (f'http://api.sl.se/api2/realtimedeparturesV4.json?' f’key={API_KEY}&siteid={station_id}’ f'&timewindow={timespan}') departure_data = deserialize(requests.get(api_url)) return [ { 'line': int(train['LineNumber']), 'destination': train['Destination'], 'arrival': train['ExpectedDateTime'] } for train in departure_data['ResponseData']['Metros'] ] departures.py from departures import fetch_departures solna_station_id: int = 9305 for departure in fetch_departures(solna_station_id): print(f'Line {departure[”line"]} ' f'to {departure[”destination"]} ' f'arrives at {departure["arrival"]}') getdepartures.py
  8. Stub Files (1/2) $ mypy getdepartures.py departures.py:2: error: No library

    stub file for module 'anyjson' from typing import Any, Dict def deserialize(x: str) -> Dict[str, Any]: ... anyjson.pyi
  9. Stub Files (2/2) from typing import Dict, List, Optional, Union

    from anyjson import deserialize import requests API_KEY: str = '<key>' def fetch_departures(station_id: int, timespan: Optional[int]=None) \ -> List[Dict[str, Union[str, int]]]: timespan = timespan or 30 api_url = (f'http://api.sl.se/api2/realtimedeparturesV4.json?' f’key={API_KEY}&siteid={station_id} f’&timewindow={timespan}') departure_data = deserialize(requests.get(api_url)) return [ { 'line': int(train['LineNumber']), 'destination': train['Destination'], 'arrival': train['ExpectedDateTime'] } for train in departure_data['ResponseData']['Metros'] ] departures.py from anyjson import deserialize import requests API_KEY = '<key>' def fetch_departures(station_id, timespan=None): timespan = timespan or 30 api_url = (f'http://api.sl.se/api2/realtimedeparturesV4.json?' f'key={API_KEY}&siteid={station_id}' f'&timewindow={timespan}') departure_data = deserialize(requests.get(api_url)) return [ { 'line': int(train['LineNumber']), 'destination': train['Destination'], 'arrival': train['ExpectedDateTime'] } for train in departure_data['ResponseData']['Metros'] ] departures.py from typing import Dict, List, Optional, Union API_KEY: str = ... def fetch_departures(station_id: int, timespan: Optional[int]=None) \ -> List[Dict[str, Union[int, str]]]: ... departures.pyi
  10. Where to go From Here? • Use Python 3 •

    Try type hinting out! • Integrate in your workflow and CI pipeline