4 ms·
Fairly contrived example, but something like this. Where you are accepting dirty input and need to normalize it. In this example I suppose you could set the typ
by dec0dedab0de 4y ago
Fairly contrived example, but something like this. Where you are accepting dirty input and need to normalize it. In this example I suppose you could set the type as int|str|Decimal|float|bytes, but the normalization still needs to happen somewhere.
class Food:
def __init__(self, temp):
if str(temp).lower().strip() in ['hot', 'warm', 'cold']:
self.temp = temp.lower().strip()
else:
try:
temp_int = int(temp)
if temp_int > 120:
self.temp = 'hot'
elif temp_int < 60:
self.temp = 'cold'
else:
self.temp = 'warm'
except ValueError:
raise ValueError(f'temp must be a number or one of the following: "hot" "warm" "cold", received: {temp}')
- thanatropism 4y agoPydantic has __post_init__. This is a based on code I have in my editor right now. Typing straight into the comment box so it might be wrong. Toy = Literal['toy'] class Spec(BaseModel): sample_size = Union[float, Toy] def __post_init__(self): self.sample_size = 0.1 if self.sample_size == 'toy' else self.sample_size
- zzzeek 4y agohere's how that is much nicer with dataclasses: import dataclasses from typing import Literal @dataclasses.dataclass class Food: temp: Literal["hot", "cold", "warm"] = dataclasses.field(init=False) temp_arg: dataclasses.InitVar[str | int] def __post_init__(self, temp_arg): # optional bonus, get the list of temps from the annotation at runtime # possible_temps = self.__annotations__["temp"].__args__ if str(temp_arg).lower().strip() in ["hot", "warm", "cold"]: self.temp = temp_arg.lower().strip() else: try: temp_int = int(temp_arg) if temp_int > 120: self.temp = "hot" elif temp_int < 60: self.temp = "cold" else: self.temp = "warm" except ValueError: raise ValueError( f"temp must be a number or one of the " f'following: "hot" "warm" "cold", received: {temp_arg}' )
- hbrn 4y agoOther than having type annotations, in what way is this nicer? Seems way more verbose and less readable.
- zzzeek 4y agoit's fully typed for one thing the previous example was not verbose enough IMO, the intent here is clear and the two variables with different purposes are kept separate
- hbrn 4y agoHe could've used different variable names in his example too, which makes me believe variable clashing was in fact intentional. And while it's definitely a controversial approach, there are cases where it can be considered more readable (e.g. where provider and consumer of "temp" are isolated entities which have their own meanings of "temp").
- zzzeek 4y agopep-484 typing hates when you use the same variable name for two different purposes and I can't say I disagree with that. Being strict about what kinds of types you attach to variables and allowing variables to be statically typed is a good idea that reduces bugs.
- hbrn 4y agoWe have two variables with the same name, but one is a constructor argument, another one is an instance attribute. PEP-484 is absolutely fine with that, it's downsides (and benefits) are purely in the human domain. > allowing variables to be statically typed is a good idea that reduces bugs Not when it comes at the cost of readability. I'd argue more bugs (and definitely more severe bugs) are caused by poor readability than by lack of static types.