5 ms·
Please, I am very curious about the kind of "non boring" classes you are talking about. If you could show an example. I couldn't come up with anything that I wo
by lysecret 4y ago
Please, I am very curious about the kind of "non boring" classes you are talking about. If you could show an example. I couldn't come up with anything that I would define as reasonable where we couldn't define the types beforehand.
- kortex 4y agoThunks-style functors, where you bind a callback to a private variable, to be later called, often with kwargs. Like what most HTTP frameworks do. You can use Callable but the actual function prototype is unknown.
- half0wl 4y agoORMs come to mind. There's typically a lot of reflection and dynamic instantiation magic baked into its classes, which you really have to hack around typing to make it work nicely (or even at all).
- dec0dedab0de 4y agoFairly 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").
- 4y ago