Pro Yearly is on sale from $80 to $50! »

Classy Abstractions @ Python Web Conf

Classy Abstractions @ Python Web Conf

174e7b0ff60963f821d0b9a4f1a3ef52?s=128

Hynek Schlawack

June 18, 2020
Tweet

Transcript

  1. Classy Abstractions Hynek Schlawack

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

  3. Primitive Obsession

  4. Premature Optimization

  5. None
  6. –me, today “You’re wasting precious resources if you use a

    high-level language but only use low-level primitives.”
  7. None
  8. PATHLIB

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

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

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

    p.absolute() PosixPath('/Users/hynek/foo/bar') >>> p.absolute() WindowsPath('C:/Users/Hynek Schlawack/foo/bar') >>> p.name 'bar'
  12. 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() ...
  13. IPADDRESS

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

  15. 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
  16. • /?a=fetch&content=<php>die(@md5(HelloThinkCMF))</php> • /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

  17. YARL

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

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

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

    '/articles/' >>> u / "speaking/" URL('https://hynek.me/articles/speaking/')
  21. 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/')
  22. >>> 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:tiger@redis.local:369/1?socket_timeout=42')
  23. >>> 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:tiger@redis.local:369/1?socket_timeout=42') >>> redis.ConnectionPool.from_url(str(u))
  24. None
  25. def f(a=False, b=False, c=False): ... f(b=True)

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

  27. None
  28. ENUM from enum import Enum class What(Enum): A = "a"

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

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

    B = "b" C = "c" def f(what: What): ... f(What.D) f(What.A)
  31. 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"
  32. @app.route("/") def view(): return { "foo": "bar" }

  33. @app.route("/") def view(): return { "data": { "id": "some-id", "type":

    "example", "attributes": { "foo": "bar" } } "links": "http://localhost/", }
  34. @bp.route("/<int:id>", 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)
  35. @bp.route("/<int:id>", 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)
  36. @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"}, ) # ...
  37. @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"}, ) # ...
  38. @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"}, ) # ...
  39. @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"}, ) # ...
  40. Locked Un- locked

  41. Locked Un- locked Start

  42. Locked Un- locked Insert Coin Start

  43. Locked Un- locked Insert Coin Push Start

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

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

  46. Classes

  47. None
  48. None
  49. None
  50. None
  51. “Dunder” double underscores: •__init__ •__eq__ •...

  52. 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)
  53. 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__ (<class '__main__.Point'>, <class '__main__.Point'>, <class 'tuple'>, <class 'object'>)
  54. CHARACTERISTIC @attributes( ["a"], defaults={"a": 42} ) class C: pass

  55. @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)
  56. None
  57. Dataclasses

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

    x: int y: int
  59. None
  60. • 2.7

  61. • 2.7 • __slots__

  62. • 2.7 • __slots__ • types optional

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

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

    free to innovate
  65. from attrs import define, field @define class Point3D: x: int

    y: int z: int = field( validator=positive )
  66. None
  67. • embrace abstractions

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

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

    wear your masks
  70. ox.cx/a @hynek hynek.me vrmd.de