Python tips: type safety and operator

I was pretty confident that python is probably the best programming language for interview six years ago. My recent interview experience confirmed that claim. Python is succinct, battery-included; making it a perfect programming language in the live code session. I would like to share some new tips learned in my recent job hunting.

Type Safety

The type safety has been widely accepted in the python community despite that the original typing system was built upon duck typing. I take a pragmatic approach: always enforce the strong type until it breaks.

Assume a function compute takes an action to execute math operations:

from typing import Literal

ActionType = Literal["add", "subtract"]

def compute(action: ActionType, *args):
    print(action)

The Literal provides the static type checking, but will not prevent the consumer to invoke compute("nop"). A better option, arguably, is to use enum.Enum to limit the value of action:

from enum import Enum

class ActionType(Enum):
    ADD = 'add'
    SUBTRACT = 'subtract'

def compute(action: ActionType, *args):
    print(action.value)

compute(ActionType.ADD)

Dive into Enum

Bonus point: we can bind the compute function to the ActionEnum like vtable does:

import operator
from functools import reduce

class ActionEnum(Enum):
    ADD = lambda args: sum(args)
    SUBTRACT = lambda args: reduce(operator.sub, args)

def compute(action: ActionEnum, *args):
    f = action
    return f(args)

print(compute(ActionEnum.ADD, 1, 2, 3)) # 6
print(compute(ActionEnum.SUBTRACT, 1, 2, 3)) # -4

Do you notice the ActionEnum.ADD is callable, while ActionType.ADD is not. The inspection shows ActionEnum.ADD is a function!

[f"{x}: iscallable={callable(x)}, type={type(x)}" for x in (ActionType.ADD, ActionEnum.ADD)]

["ActionType.ADD: iscallable=False, type=<enum 'ActionType'>",
 "<function ActionEnum.<lambda> at 0x10a3c9440>: iscallable=True, type=<class 'function'>"]

ActionEnum is NOT even enumerable!

[f"{x}: members={[m for m in x]}" for x in (ActionType, ActionEnum)]

["<enum 'ActionType'>: members=[<ActionType.ADD: 'add'>, <ActionType.SUBTRACT: 'subtract'>]",
 "<enum 'ActionEnum'>: members=[]"]

I am no sure whether this behavior is by design, or purely coincidence.

Prefer operators over lambda

You have encountered the operator.sub in the previous section, which provides a higher abstraction than the lambda function and make the code more readable; — and in the AI era, more token efficient 😅.

For example, we can sort by the value attribute of Enum as:

sorted(AnyType, key=operator.attrgetter('value'))

[<ActionType.ADD: 'add'>, <ActionType.SUBTRACT: 'subtract'>]

And sort the students by names, also group by the department:

sorted(students, key=operator.itemgetter('department', 'name'))

Hidden gems

Besides the standard libraries we have discussed before, there are more hidden gems I found, updating