Python in the real world
John J. Camilleri
Me
Scalable content creation for E-commerce
├─ subpart/subpart
│ ├─ quality/property='flared_13049'
│ ├─ identifier/kind='leg_567'
│ │ ├─ identifier/hypernym='leg_567'
│ │ ├─ kind/number='pl'
├─ subpart/subpart
│ ├─ identifier/kind='quality_7753'
│ │ ├─ identifier/hypernym='quality_7753'
│ ├─ quality/property='ribbed_1678'
│ ├─ material/cloth='jersey_635'
├─ subpart/subpart
│ ├─ identifier/kind='elastic_43945'
│ │ ├─ identifier/hypernym='elastic_43945'
│ ├─ phrase/position='at_the_waist_6720'
├─ subpart/subpart
│ ├─ identifier/kind='fit_5352'
│ │ ├─ identifier/hypernym='fit_5352'
│ ├─ quality/property='fitted_5842'
├─ template/template='gina-tricot_produktionse…
│ ├─ template/data={'text': 'Pick & Choose AB'}
├─ template/template='gina-tricot_produktionse…
│ ├─ template/data={'text': 'Melek Tekstil -…
├─ identifier/name='Aurora trousers'
├─ identifier/kind='pants_585'
Aurora trousers
Byxa med utsvängda ben. Byxan har en ribbad kvalitet i trikå. Den har en resår i midjan och en figurnära passform.
Material & Hållbarhet
Aurora trousers
Housut levenevillä jaloilla. Housuissa on ribattu laatu trikoosta. Niissä on kuminauha vyötäröllä ja vartalonmyötäinen istuvuus.
Materiaalit ja kestävyys
Aurora trousers
Hose mit ausgestellten Beinen. Die Hose hat eine gerippte Qualität aus Jersey. Sie hat ein Gummiband in der Taille und eine taillierte Passform.
Materialien & Nachhaltigkeit
Tech stack
Code repository in numbers
Tickets and boards
Git “feature branch” workflow
Code review
Continuous integration/delivery (CI/CD)
Feature flags
if getFeatureFlag(user, 'new-ui'):
# new code
else:
# old code
Application code
Control panel
Application monitoring (APM)
Error tracking
Unexpected errors�happen.
A lot.
Why?
Traceback (most recent call last):
File "/Users/john/repositories/Textual/textual-app/app/products/views/details/tabs/templates_tab.py", line 285, in make_inline_form_html
text = TemplateGenerator.render_template(
File "/Users/john/repositories/Textual/textual-app/app/planner/data.py", line 646, in render_template
text = template.render(context_data)
File "/usr/local/lib/python3.9/site-packages/jinja2/asyncsupport.py", line 76, in render
return original_render(self, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 1008, in render
return self.environment.handle_exception(exc_info, True)
File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 780, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.9/site-packages/jinja2/_compat.py", line 37, in reraise
raise value.with_traceback(tb)
File "<template>", line 1, in <module>
File "/usr/local/lib/python3.9/site-packages/jinja2/environment.py", line 411, in getitem
return obj[argument]
jinja2.exceptions.UndefinedError: 'phrase' is undefined
Nullability
Any thing at any point could be None (or undefined)
Dynamicity
Any thing at any point could have an unexpected Type
Python is a dynamic language
Everything happens at runtime
Dynamic typing | Infer types at runtime |
Dynamic binding | Add/change object attributes at runtime |
“Duck” typing | Consider object attributes rather than type |
Introspection | Examine object attributes at runtime |
Monkey patching | Modify code, replace functions at runtime |
Dynamic loading | Import modules at runtime |
Dynamic code execution | Execute arbitrary code at runtime |
The Python interpreter
$ python3
Python 3.10.12 (main, Jun 20 2023, 17:00:24) [Clang 14.0.3 ...
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.ceil(67/4)
17
>>> "hello".replace
<built-in method replace of str object at 0x106343fb0>
>>> "hello".replace("e", "a").replace("o", "å")
'Hallå'�>>> ^d�$
How is this implemented?
Implementing the Python interpreter
while True: exec(str(input('>>>')))
Dynamic code
Dynamic typing
$ cat test.py
foo = "hello"
print(foo[2])
foo_l = [foo]
print(foo_l[0])
foo.append("!")
$ python3 test.py
l
hello
Traceback (most recent call last):
File "…/test.py", line 30, …
foo.append("!")
AttributeError: 'str' object has no attribute 'append'
>>> foo = "hello"
>>> type(foo)
<class 'str'>
>>> foo[2]
"l"
>>> foo_l = [foo]
>>> type(foo_l)
<class list>
>>> foo_l[0]
"hello"
>>> foo.append("!")
AttributeError: 'str' object has no attribute 'append'
Dynamic errors
Dynamicity encourages quick hacks
👹
Convenient when you�know what you’re doing™
👼🏻
No one in a team�knows all parts of a system
Static typing
def fib(n):
a, b = 0, 1
while a < n:
yield a
a, b = b, a+b
print(list(fib(4)))
print(list(fib("hello")))
from typing import Iterator
def fib(n: int) -> Iterator[int]:
a, b = 0, 1
while a < n:
yield a
a, b = b, a+b
print(list(fib(4)))
print(list(fib("hello")))
$ python3 fib.py
[0, 1, 1, 2, 3]
Traceback (most recent call last):
File "/tmp/fib.py", line 10, in <module>
print(list(fib("hello")))
File "/tmp/fib.py", line 5, in fib
while a < n:
TypeError: '<' not supported between instances of 'int' and 'str'
$ mypy fib.py
fib.py:10: error: Argument 1 to "fib" has incompatible type "str"; expected "int"
Found 1 error in 1 file (checked 1 source file)
$ python3 fib.py
[0, 1, 1, 2, 3]
Traceback (most recent call last): …
Without static type checking
With static type checking
“an optional static type checker for Python�that aims to combine the benefits of�dynamic (or "duck") typing and static typing”
Other typing solutions
Object Orientation (OO) in Python
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def woof(self):
print(f"{self.name} says woof!")
dog1 = Dog("Lassie", 8)
>>> dog1.woof()
Lassie says woof!
>>> type(dog1)
<class '__main__.Dog'>
def woof(dog):
print(f"{dog['name']} says woof!")
dog2 = {
"name": "Rex",
"age": 4
}
>>> woof(dog2)
Rex says woof!
>>> type(dog2)
<class 'dict'>
Function + POPO (Plain Old Python Object)
Class with fields and method
Web Apps: Frontend/Backend
Storage
Frontend
Backend
Data�JSON/XML
Data
SQL/JSON
Code
Code
numbers
strings
relations
numbers
strings
lists�dictionaries
Data conversion / “Serialization”
Frontend
Storage
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def woof(self):
print(f"{self.name} says woof!")
dog = Dog("Rex", 4)
dog = {
"name": "Rex",
"age": 4
}
Python class & instance
JSON object
“Objects bind functions and data structures together in indivisible units.
This is a fundamental error since functions and data structures belong in totally different worlds.”
Joe Armstrong�Why OO Sucks (2011)�http://harmful.cat-v.org/software/OO_programming/why_oo_sucks
Every computer system ever
Processing
Input
Output
data
functions
Object-Relational Mapping (ORM)
You can have ORM without OO!
from django.db import models
class Manufacturer(models.Model):
name = models.CharField(max_length=100)
class Car(models.Model):
name = models.CharField(max_length=50)
year = models.DateField()
manufacturer = models.ForeignKey(
Manufacturer,on_delete=models.CASCADE)
volvo = Manufacturer.objects.create(
name="Volvo")
xc40 = Car.objects.create(
name="XC 40",
year=2021,
manufacturer=volvo)
all_volvos = Car.objects.filter(� manufacturer=volvo)
Concurrency
In practice
Performance: in theory
Performance: in reality
Conclusions
Sources
🐍 Thanks!
Unused slides…
“Duck-typing”
class Duck:
def swim(self):
print("Duck swimming")
def fly(self):
print("Duck flying")
class Whale:
def swim(self):
print("Whale swimming")
for animal in [Duck(), Whale()]:
animal.swim()
animal.fly()
Duck swimming
Duck flying
Whale swimming
AttributeError: 'Whale' object has no attribute 'fly'
"If it walks like a duck and it quacks like a duck, then it must be a duck"
Encapsulation (or lack thereof)
class Foo:
# public
def qux(self):
print "qux"
# protected
def _qib(self):
print "qib"
# private
def __qak(self):
print "qak"
>>> f = Foo()
>>> f.qux()
qux
>>> f._qib()
qib
>>> f._Foo__qak()
qak