Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
323 views
in Technique[技术] by (71.8m points)

python - PEP 484: exclusive type for type hint

Could I specify exclusive type? Something like this:

def foo(bar: Not[str]) -> None:
    assert not isinstance(bar, str)
    print(type(bar))
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

PEP 484 does not include a built-in way of specifying a "blacklisted" type.

Doing that kind of thing typically doesn't really make sense -- the only reason I can think of why you might want to do something like this is if you wanted to distinguish between str and Iterable[str] or Sequence[str] in some fashion. If that's the case, PEP 484 unfortunately doesn't have a way of letting you do this -- str, for better or for worse, is a legitimate subclass of those two types.

Your best bet in this case is to modify your type signature to distinguish between str and List[str] which are legitimately distinguishable classes. Not ideal, but better then nothing.


That said, if you really do want to do something like this, it's sort of possible to do so, assuming you're using mypy and don't mind resorting to dirty hacks.

One hack is to basically modify your foo function so it always returns something and abuse overload and the semantics of non-strict optional mode, like so:

from typing import overload, Optional

@overload
def foo(bar: str) -> None: ...

# Alternatively, replace "object" below
# with the types you *do* want to allow

@overload
def foo(bar: object) -> int: ...

def foo(bar: object) -> Optional[int]:
    assert not isinstance(bar, str)
    print(type(bar))
    return 0

def main() -> None:
    x = 0

    x = foo(3)       # typechecks
    x = foo("foo")   # fails

If you try running this in mypy WITHOUT the --strict-optional tag, mypy will flag the last line with a "foo" does not return a value error. You would then need to remember that this error message occurs because you passed in a string argument. You would also need to remember to always assign the output of your foo function to a value.

(If you do enable strict-optional, mypy will complain that your two overloads have incompatible signatures.)

What we're basically doing here is:

  1. Exploiting the fact that with strict-optional disabled, None is a legal value of every type (so the overload is legal)
  2. Exploiting the fact that mypy will (as an extra perk) warn you of cases where you assign a value from a function that doesn't return anything.

This is all pretty hacky and very likely a bad idea. I also make no promises that this interaction will continue to work in the future.


The second hack you could try is to write a mypy plugin to catch instances of your specific case. The mypy plugin system is, as of time of writing, an undocumented, tentative, and highly-prone-to-change feature, but if you really want to do this, that might be something to investigate.


If you're specifically trying to distinguish between str and Sequence[str] or Iterable[str] (or something similar), something else you can try is to:

  1. Create a custom fork of Typeshed and/or the typing module
  2. Modify the class definition/stubs for str so it doesn't inherit from either Sequence[str] or Iterable[str] (or add phantom types or something)
  3. Make mypy use your custom type definitions using the --custom-typeshed-dir and --custom-typing command line arguments.

Basically, if the default type hierarchy isn't quite what you want, invent a custom one.


Of course, if you're using some other PEP 484 compliant checker (such as Pycharm's built in checker), you're probably out of luck.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...