-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Graceful handling of extra properties from clients #270
Labels
Comments
Note: Python 3.10.4 (main, Apr 2 2022, 11:07:58) [GCC 9.3.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.4.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: class A:
...: def __init__(self, a, b, c, **kwargs):
...: self.a = a
...: self.b = b
...: self.c = c
...:
In [2]: from dataclasses import dataclass, fields
In [3]: def create_instance(cls, props):
...: """
...: Creates an instance of a given dataclass type, ignoring extra properties.
...: """
...: class_fields = {f.name for f in fields(cls)}
...: return cls(**{k: v for k, v in props.items() if k in class_fields})
...:
In [4]: @dataclass
...: class B:
...: a: int
...: b: int
...: c: int
...:
In [5]: A(**{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5})
Out[5]: <__main__.A at 0x7fd850abe200>
In [6]: data = {"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
In [7]: a = A(**data)
In [8]: a.a
Out[8]: 1
In [9]: a.b
Out[9]: 2
In [10]: a.c
Out[10]: 3
In [11]: %timeit A(**data)
420 ns ± 1.45 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [12]: b = create_instance(B, data)
In [13]: b.a
Out[13]: 1
In [14]: b.b
Out[14]: 2
In [15]: b.c
Out[15]: 3
In [16]: %timeit A(**data)
419 ns ± 0.351 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [17]: B(**data)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [17], in <cell line: 1>()
----> 1 B(**data)
TypeError: B.__init__() got an unexpected keyword argument 'd'
In [18]: %timeit create_instance(B, data)
1.38 µs ± 3.22 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [19]: class_fields = {f.name for f in fields(B)}
In [20]: class_fields
Out[20]: {'a', 'b', 'c'}
In [21]: B(**{k: v for k, v in data.items() if k in class_fields})
Out[21]: B(a=1, b=2, c=3)
In [22]: %timeit B(**{k: v for k, v in data.items() if k in class_fields})
657 ns ± 1.59 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [23]: %timeit B(**{k: v for k, v in data.items() if k in class_fields})
651 ns ± 0.331 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each) |
In [24]: FIELDS = {}
In [27]: def get_fields(cls):
...: try:
...: return FIELDS[cls]
...: except KeyError:
...: FIELDS[cls] = {f.name for f in fields(cls)}
...: return FIELDS[cls]
...:
In [28]:
In [28]: def create_instance(cls, props):
...: """
...: Creates an instance of a given dataclass type, ignoring extra properties.
...: """
...: class_fields = get_fields(cls)
...: return cls(**{k: v for k, v in props.items() if k in class_fields})
...:
In [29]: %timeit create_instance(B, data)
796 ns ± 2.11 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
In [30]: FIELDS
Out[30]: {__main__.B: {'a', 'b', 'c'}}
In [31]: %timeit create_instance(B, data)
802 ns ± 6.04 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ignore extra properties when parsing binding input from the request payload?
Example for dataclasses:
The text was updated successfully, but these errors were encountered: