Add support for Optional types#8
Add support for Optional types#8eykamp wants to merge 1 commit intoramazanpolat:masterfrom eykamp:optional
Conversation
|
By default, you can assign e.g: class Car(Prodict):
brand: str
year: int
# Here we assign `None` to `year` and prodict accepts it:
bimmer = Car(brand='BMW', year=None)
print(bimmer)
# {'brand': 'BMW', 'year': None}`
# Here we are assigning a str to `year` but it is converted to `int` by Prodict:
chevy = Car(brand='Chevrolet', year='2020')
(chevy)
# {'brand': 'Chevrolet', 'year': 2020}
type(chevy.year)
# <class 'int'>
# But assigning a type that is different from annotated type(here 'Millenium' as str as opposed to be an int) and
# not convertible to defined type generates Value Error:
lambo = Car(brand='Lamborghini', year='Millenium') # conversion from 'Millenium' to int() throws ValueError.
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "Z:\___DEVEL\py\prodict2\prodict\__init__.py", line 25, in __init__
# self.set_attributes(**kwargs)
# File "Z:\___DEVEL\py\prodict2\prodict\__init__.py", line 205, in set_attributes
# self.set_attribute(k, v)
# File "Z:\___DEVEL\py\prodict2\prodict\__init__.py", line 165, in set_attribute
# self.update({attr_name: constructor(value)})
# ValueError: invalid literal for int() with base 10: 'Millenium'So, in the end, you don't need to annotate your attributes with class Car(Prodict):
brand: str
year: Union[int]
# let's assign `None` to `year`:
benty = Car(brand='Bentley', year=None)
print(benty)
# {'brand': 'Bentley', 'year': None}
# assign an int:
linco = Car(brand='Lincoln', year=2020)
print(linco)
{'brand': 'Lincoln', 'year': 2020}
# assign a str that is convertible to int:
audi = Car(brand='Audi', year='2020')
print(audi)
# {'brand': 'Audi', 'year': 2020}
type(audi.year)
# <class 'int'>
# For the last, assign a str that is not convertible to int:
caddy = Car(brand='Cadillac', year='nah') # conversion from 'nah' to int() throws ValueError which is expected
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "Z:\___DEVEL\py\prodict2\prodict\__init__.py", line 25, in __init__
# self.set_attributes(**kwargs)
# File "Z:\___DEVEL\py\prodict2\prodict\__init__.py", line 205, in set_attributes
# self.set_attribute(k, v)
# File "Z:\___DEVEL\py\prodict2\prodict\__init__.py", line 165, in set_attribute
# self.update({attr_name: constructor(value)})
# ValueError: invalid literal for int() with base 10: 'nah'So you can use |
|
I know that in the prodict universe, Optional is, well, optional, but I like to use it where appropriate to document that something may or may not be appearing in my incoming json (though I realize that in some sense everything is optional). I'm using it in my classes for reasons unrelated to prodict, and I think it's reasonable for the library to handle that case (especially since it can do so without impairing any other functionality). As for Optional is shorthand for a Union with None, so not quite the same thing: My PR pulls the |
|
I've just realized that I've been replacing |
|
You do realize that in prodict, all attributes behave like Check this: class Car(Prodict):
brand: str
year: int
color: Optional[str]
honda = Car(brand='Honda', year=None)
print(honda)
# {'brand': 'Honda', 'year': None}
honda.brand = None
print(honda)
# {'brand': None, 'year': None}
honda.color = None
print(honda)
# {'brand': None, 'year': None, 'color': None}
honda.color = 'White'
print(honda)
# {'brand': None, 'year': None, 'color': 'White'}Therefore you don't need to annotate them with |
I like to use Optional when defining my types; this lets you do that. Includes test support.