6 ms·
Having to specify the names and, if you hate yourself, the types of class members up front is a major limitation. Only when it is possible to do so (because the
by HelloNurse 4y ago
Having to specify the names and, if you hate yourself, the types of class members up front is a major limitation. Only when it is possible to do so (because the class is a boring one) the repetitiveness of typical __init__ methods can become an issue.
- eru 4y agoHow is that a major limitation? You only have to specify them 'up front' in terms of how the code is laid out. But in terms of which order you write the code in, you can add them as you need them. Specifying the names and types is extremely useful for readers (and users) of your code.
- HelloNurse 4y agoAnything requiring e.g. setattr, getattr, delattr? Without looking far, https://github.com/python-attrs/attrs/blob/main/src/attr/_make.py https://github.com/python-attrs/attrs/blob/main/src/attr/_ma...
- lysecret 4y agoPlease, 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}' )