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

Classy Abstractions @ Python Web Conf

Classy Abstractions @ Python Web Conf

Hynek Schlawack

June 18, 2020
Tweet

More Decks by Hynek Schlawack

Other Decks in Programming

Transcript

  1. Classy
    Abstractions
    Hynek Schlawack

    View full-size slide

  2. [({},),({},)]

    View full-size slide

  3. Primitive
    Obsession

    View full-size slide

  4. Premature
    Optimization

    View full-size slide

  5. –me, today
    “You’re wasting precious resources if
    you use a high-level language but only
    use low-level primitives.”

    View full-size slide

  6. PATHLIB
    >>> p = Path() / "foo" / "bar"

    View full-size slide

  7. PATHLIB
    >>> p = Path() / "foo" / "bar"
    >>> p.absolute()
    PosixPath('/Users/hynek/foo/bar')
    >>> p.absolute()
    WindowsPath('C:/Users/Hynek Schlawack/foo/bar')

    View full-size slide

  8. PATHLIB
    >>> p = Path() / "foo" / "bar"
    >>> p.absolute()
    PosixPath('/Users/hynek/foo/bar')
    >>> p.absolute()
    WindowsPath('C:/Users/Hynek Schlawack/foo/bar')
    >>> p.name
    'bar'

    View full-size slide

  9. PATHLIB
    >>> p = Path() / "foo" / "bar"
    >>> p.absolute()
    PosixPath('/Users/hynek/foo/bar')
    >>> p.absolute()
    WindowsPath('C:/Users/Hynek Schlawack/foo/bar')
    >>> p.name
    'bar'
    >>> Path("README.md").read_text()
    ...

    View full-size slide

  10. IPADDRESS
    >>> i = ipaddress.ip_address("::1")
    >>> i.exploded
    '0000:0000:0000:0000:0000:0000:0000:0001'

    View full-size slide

  11. IPADDRESS
    >>> i = ipaddress.ip_address("::1")
    >>> i.exploded
    '0000:0000:0000:0000:0000:0000:0000:0001'
    >>> ipaddress.ip_address("10.23.45.56") \
    in ipaddress.ip_network("10.0.0.0/8")
    True

    View full-size slide

  12. • /?a=fetch&content=die(@md5(HelloThinkCMF))
    • /wp-content/plugins/google-document-embedder/libs/pdf.php?
    fn=lol.pdf&file=../../../../wp-config.php
    • /wp-admin/admin-ajax.php?action=kbslider_show_image&img=../
    wp-config.php

    View full-size slide

  13. YARL
    >>> u = yarl.URL("https://hynek.me/articles/")

    View full-size slide

  14. YARL
    >>> u = yarl.URL("https://hynek.me/articles/")
    >>> u.host
    'hynek.me'
    >>> u.path
    '/articles/'

    View full-size slide

  15. YARL
    >>> u = yarl.URL("https://hynek.me/articles/")
    >>> u.host
    'hynek.me'
    >>> u.path
    '/articles/'
    >>> u / "speaking/"
    URL('https://hynek.me/articles/speaking/')

    View full-size slide

  16. YARL
    >>> u = yarl.URL("https://hynek.me/articles/")
    >>> u.host
    'hynek.me'
    >>> u.path
    '/articles/'
    >>> u / "speaking/"
    URL('https://hynek.me/articles/speaking/')
    >>> u.with_path("about/")
    URL('https://hynek.me/about/')

    View full-size slide

  17. >>> u = yarl.URL.build(
    scheme="redis", user="scott", password="tiger",
    host="redis.local", port=369, path="/1",
    query={"socket_timeout": 42})
    >>> u
    URL('redis://scott:[email protected]:369/1?socket_timeout=42')

    View full-size slide

  18. >>> u = yarl.URL.build(
    scheme="redis", user="scott", password="tiger",
    host="redis.local", port=369, path="/1",
    query={"socket_timeout": 42})
    >>> u
    URL('redis://scott:[email protected]:369/1?socket_timeout=42')
    >>> redis.ConnectionPool.from_url(str(u))

    View full-size slide

  19. def f(a=False, b=False, c=False):
    ...
    f(b=True)

    View full-size slide

  20. def f(a=False, b=False, c=False):
    ...
    f(b=True)
    def f(what):
    ...
    f("b")

    View full-size slide

  21. ENUM
    from enum import Enum
    class What(Enum):
    A = "a"
    B = "b"
    C = "c"

    View full-size slide

  22. ENUM
    from enum import Enum
    class What(Enum):
    A = "a"
    B = "b"
    C = "c"
    f(What.A)

    View full-size slide

  23. ENUM
    from enum import Enum
    class What(Enum):
    A = "a"
    B = "b"
    C = "c"
    def f(what: What):
    ...
    f(What.D)
    f(What.A)

    View full-size slide

  24. ENUM
    from enum import Enum
    class What(Enum):
    A = "a"
    B = "b"
    C = "c"
    def f(what: What):
    ...
    f(What.D)
    f(What.A) error: "Type[What]" has no attribute "D"

    View full-size slide

  25. @app.route("/")
    def view():
    return {
    "foo": "bar"
    }

    View full-size slide

  26. @app.route("/")
    def view():
    return {
    "data": {
    "id": "some-id",
    "type": "example",
    "attributes": {
    "foo": "bar"
    }
    }
    "links": "http://localhost/",
    }

    View full-size slide

  27. @bp.route("/", methods=["GET"])
    def get_dns_record(id: int) -> APIResult:
    try:
    return APIResult(
    get_uow().dns_records.get(id)
    )
    except UnknownDNSRecordError:
    raise_not_found("DNS record", id)

    View full-size slide

  28. @bp.route("/", methods=["GET"])
    def get_dns_record(id: int) -> APIResult:
    try:
    return APIResult(
    get_uow().dns_records.get(id)
    )
    except UnknownDNSRecordError:
    raise_not_found("DNS record", id)

    View full-size slide

  29. @bp.route("/", methods=["POST"])
    @validated_body
    def create_dns_record(ndr: CreateDNSRecord) -> APIResult:
    try:
    dr_id = svc.create_record(
    get_uow(), ndr.domain, # ...
    )
    except PlanError as e:
    raise APIError(
    id=error_ids.PLAN_NOT_SUFFICIENT,
    status="402",
    title=e.args[0],
    source={"pointer": "/data/attributes/domain"},
    )
    # ...

    View full-size slide

  30. @bp.route("/", methods=["POST"])
    @validated_body
    def create_dns_record(ndr: CreateDNSRecord) -> APIResult:
    try:
    dr_id = svc.create_record(
    get_uow(), ndr.domain, # ...
    )
    except PlanError as e:
    raise APIError(
    id=error_ids.PLAN_NOT_SUFFICIENT,
    status="402",
    title=e.args[0],
    source={"pointer": "/data/attributes/domain"},
    )
    # ...

    View full-size slide

  31. @bp.route("/", methods=["POST"])
    @validated_body
    def create_dns_record(ndr: CreateDNSRecord) -> APIResult:
    try:
    dr_id = svc.create_record(
    get_uow(), ndr.domain, # ...
    )
    except PlanError as e:
    raise APIError(
    id=error_ids.PLAN_NOT_SUFFICIENT,
    status="402",
    title=e.args[0],
    source={"pointer": "/data/attributes/domain"},
    )
    # ...

    View full-size slide

  32. @bp.route("/", methods=["POST"])
    @validated_body
    def create_dns_record(ndr: CreateDNSRecord) -> APIResult:
    try:
    dr_id = svc.create_record(
    get_uow(), ndr.domain, # ...
    )
    except PlanError as e:
    raise APIError(
    id=error_ids.PLAN_NOT_SUFFICIENT,
    status="402",
    title=e.args[0],
    source={"pointer": "/data/attributes/domain"},
    )
    # ...

    View full-size slide

  33. Locked
    Un-
    locked

    View full-size slide

  34. Locked
    Un-
    locked
    Start

    View full-size slide

  35. Locked
    Un-
    locked
    Insert Coin
    Start

    View full-size slide

  36. Locked
    Un-
    locked
    Insert Coin
    Push
    Start

    View full-size slide

  37. Locked
    Un-
    locked
    Insert Coin
    Push Insert Coin
    Start

    View full-size slide

  38. Locked
    Un-
    locked
    Push
    Insert Coin
    Push Insert Coin
    Start

    View full-size slide

  39. “Dunder”
    double underscores:
    •__init__
    •__eq__
    •...

    View full-size slide

  40. class Point(namedtuple('Point', ['x', 'y'])):
    __slots__ = ()
    @property
    def hypot(self):
    return (self.x ** 2 + self.y ** 2) ** 0.5
    def __str__(self):
    return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (
    self.x, self.y, self.hypot)

    View full-size slide

  41. class Point(namedtuple('Point', ['x', 'y'])):
    __slots__ = ()
    @property
    def hypot(self):
    return (self.x ** 2 + self.y ** 2) ** 0.5
    def __str__(self):
    return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (
    self.x, self.y, self.hypot)
    >>> Point.__mro__
    (,
    ,
    ,
    )

    View full-size slide

  42. CHARACTERISTIC
    @attributes(
    ["a"],
    defaults={"a": 42}
    )
    class C: pass

    View full-size slide

  43. @attr.s
    class Point:
    x = attr.ib()
    y = attr.ib()
    @property
    def hypot(self):
    return (self.x ** 2 + self.y ** 2) ** 0.5
    def __str__(self):
    return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (
    self.x, self.y, self.hypot)

    View full-size slide

  44. @dataclasses.dataclass
    class Point:
    x: int
    y: int
    @attr.dataclass
    class Point:
    x: int
    y: int

    View full-size slide

  45. • 2.7
    • __slots__

    View full-size slide

  46. • 2.7
    • __slots__
    • types optional

    View full-size slide

  47. • 2.7
    • __slots__
    • types optional
    • validators/converters

    View full-size slide

  48. • 2.7
    • __slots__
    • types optional
    • validators/converters
    • free to innovate

    View full-size slide

  49. from attrs import define, field
    @define
    class Point3D:
    x: int
    y: int
    z: int = field(
    validator=positive
    )

    View full-size slide

  50. • embrace abstractions

    View full-size slide

  51. • embrace abstractions
    • don’t be afraid of classes

    View full-size slide

  52. • embrace abstractions
    • don’t be afraid of classes
    • wear your masks

    View full-size slide

  53. ox.cx/a
    @hynek
    hynek.me
    vrmd.de

    View full-size slide