-
Notifications
You must be signed in to change notification settings - Fork 78
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
Add is_ok
and is_err
typeguard functions
#135
Conversation
update changelog & docs add typeguard mypy tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few minor things.
Otherwise PR looks great. Thank you for the help.
@wbolster if you have any thought or opinions on this, since you were working in this area prior, feel free to take a look and share your comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again @mishamsk
Few minor typos that I'll take care of post merge.
@@ -120,7 +120,8 @@ Creating an instance: | |||
>>> res1 = Ok('yay') | |||
>>> res2 = Err('nay') | |||
|
|||
Checking whether a result is ``Ok`` or ``Err``. With ``isinstance`` you get type safe | |||
Checking whether a result is ``Ok`` or ``Err``. You can either use ``is_ok`` and ``is_err`` functions | |||
or ``isinstance``. This way you get type safe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self, small typo here, remove the .
at the end of safe
@@ -13,6 +13,8 @@ Possible log types: | |||
|
|||
## [Unreleased] | |||
|
|||
-- `[added]` `is_ok` and `is_err` type guard functions as alternatives to `isinstance` checks (#69) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self, small typo here, remove the extra -
at the start of this line
@@ -120,7 +120,8 @@ Creating an instance: | |||
>>> res1 = Ok('yay') | |||
>>> res2 = Err('nay') | |||
|
|||
Checking whether a result is ``Ok`` or ``Err``. With ``isinstance`` you get type safe | |||
Checking whether a result is ``Ok`` or ``Err``. You can either use ``is_ok`` and ``is_err`` functions | |||
or ``isinstance``. This way you get type safe. | |||
access that can be checked with MyPy. The ``is_ok()`` or ``is_err()`` methods can be | |||
used if you don't need the type safety with MyPy: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to self, perhaps make methods
and functions
bold to distinguish them since is_ok
and is_err
have the same names
sorry for late reply. #72 suffered from some type checker limitations, and this one does not. do i understand that correctly?
i think this is misguided. why would |
unfortunately not, it is all the same. But a new PEP is being drafted which will "fix" that
Oh, I assumed it should be slower, but apparently I am wrong. Although it may be different in 3.11 with reduced "price" for additional frames and specializing attribute getters, which I'd hope would speed up bound methods getters as well... Thanks for the tip about isinstance checks. Do you happen to have a good link handy that explores how CPython implements them by any chance? |
this should help:
… so the simple cases boil down to a bit of pointer chasing and a pointer comparison, which is all very fast. |
@wbolster thanks a lot! I got curious, and run a very dumb test (sorry for formatting, doing it from iPad while on vacation): import timeit
class T:
cattr = True
def __init__(self) -> None:
self.attr = True
@property
def prop(self) -> bool:
return True
def ist(self) -> bool:
return True
class T1:
__slots__ = ("attr",)
cattr = True
def __init__(self) -> None:
self.attr = True
@property
def prop(self) -> bool:
return True
def ist(self) -> bool:
return True
t = T()
inst_check = timeit.timeit("isinstance(t, T)", globals=globals())
fn_check = timeit.timeit("t.ist()", globals=globals())
prop_check = timeit.timeit("t.prop", globals=globals())
attr_check = timeit.timeit("t.attr", globals=globals())
cattr_check = timeit.timeit("t.cattr", globals=globals())
print(f"Non-slotted. Isintance: {inst_check}. Method: {fn_check}. Prop: {prop_check}. Inst attr: {attr_check}. Class attr: {cattr_check}")
t = T1()
inst_check = timeit.timeit("isinstance(t, T)", globals=globals())
fn_check = timeit.timeit("t.ist()", globals=globals())
prop_check = timeit.timeit("t.prop", globals=globals())
attr_check = timeit.timeit("t.attr", globals=globals())
cattr_check = timeit.timeit("t.cattr", globals=globals())
print(f"Slotted. Isintance: {inst_check}. Method: {fn_check}. Prop: {prop_check}. Inst attr: {attr_check}. Class attr: {cattr_check}") Which gives for 3.10 & 3.11 (in docker running on synology NAS;-), but we do not care about absolute numbers): py310: commands[0]> python isinstance_test.py
Non-slotted. Isintance: 0.16529686748981476. Method: 0.22358105331659317. Prop: 0.20174939185380936. Inst attr: 0.10315892100334167. Class attr: 0.10350674390792847
Slotted. Isintance: 0.2414640188217163. Method: 0.1983325481414795. Prop: 0.20569878816604614. Inst attr: 0.09698593616485596. Class attr: 0.08725540339946747
py310: OK ✔ in 1.99 seconds
py311: commands[0]> python isinstance_test.py
Non-slotted. Isintance: 0.0721818059682846. Method: 0.10906121879816055. Prop: 0.16895851492881775. Inst attr: 0.03669581562280655. Class attr: 0.0829509049654007
Slotted. Isintance: 0.12867401540279388. Method: 0.10731867700815201. Prop: 0.17078260332345963. Inst attr: 0.035740092396736145. Class attr: 0.06810076534748077
py310: OK (1.99=setup[0.30]+cmd[1.68] seconds)
py311: OK (1.05=setup[0.01]+cmd[1.04] seconds)
congratulations :) (3.19 seconds) So, apparently, speedwise the optimal way would be to create an instance attribute (something like PS. surprised to see that property access is slower than a method call on py311. I guess some of their call optimisations do not work for descriptors🤷♂️ |
I'd say this is a more performant implementation than #72 as it doesn't use
is instance
checks at all.Added tests, changelog & took a stab at main readme (although this is up to maintainers, whether this should be advertised as a better approach).
On my laptop one of the mypy tests is failing on 3.9, but that's not related to my change (complains about
|
whereUnion
should be)closes #69