Hacktoberfest 2026: die Issues, die Maintainer für den Oktober markiert haben – offen und einsteigerfreundlich. Hacktoberfest-Issues durchsuchen

Proposal: Add coerced type narrowing similar to 'cast'

Offen
#1,773 2 Kommentare 4 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Maintainer antworten meist innerhalb von 1 Tag

Dieses Issue hat noch niemand übernommen.

Bewertung

Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Anfängerfreundlichkeit
25/100
Issue-Typ
Feature
Klarheit
Muss geklärt werden
Aktivitätsstatus
Veraltet
Tech-Stack
python
Bereich
devtools

Rechercherichtung

Das Issue nennt keine Dateien, Tests oder Einstiegspunkte für die Implementierung. Beginne mit der Überprüfung der vorgeschlagenen Formen type_assert und ensure_type sowie der verknüpften Diskussionen zu Intersection und Not; definiere die unterstützte Semantik, das Laufzeitverhalten und die Akzeptanztests vor der Implementierung.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Beschreibung

topic: feature

My suggestion is to add a way to coerce type narrowing without any runtime change,
by adding new type_assert and/or ensure_type.

def function(arg: Any):
    type_assert isinstance(arg, int)
    reveal_type(arg)  # Revealed type: "builtins.int"
def function(arg: Any):
    ensure_type(arg, int)
    reveal_type(arg)  # Revealed type: "builtins.int"

These functions do nothing at runtime! they only help with the types.

Why not cast(int, arg)?

  1. cast completely replaces the type and we need to write a full new type.
    With type narrowing it's only necessary to limit the options of the existing type.

    def function(arg: Union[int, str, list]):
        arg = cast(Union[str, list], arg)
        reveal_type(arg)  # Revealed type: "str | list"
    
    def function(arg: Union[int, str, list]):
        type_assert not isinstance(arg, int)  # only need to remove 'int'
        reveal_type(arg)  # Revealed type: "str | list"
    
  2. cast is more dangerous because we ignore the previous type. with narrowing we just limit the options of the previous type.

    def function(arg: Union[int, str, list]):
        arg1 = cast(dict, arg)
        reveal_type(arg1)  # Revealed type: "dict"
    
    def function(arg: Union[int, str, list]):
        type_assert not isinstance(arg, dict)  # Error: Subclass of "int" and "dict[Any, Any]" cannot exist
    
    def function(arg: Union[int, str, list]):
        type_assert not isinstance(arg, int)
        type_assert not isinstance(arg, str)
        type_assert not isinstance(arg, list)
        reveal_type(arg)  # Error: Statement is unreachable
    
  3. cast can't do Intersection (yet). explained below.

Why type_assert and not a normal assert?

  1. assert makes it slower at runtime, and sometimes we care
  2. assert can break things when improving types of an old code base

The downsides:

  1. Without assert or if-else conditions at runtime, the coerced narrowing ignores the real type and is dangerous, almost like cast.

With Intersection type

Until we have Intersection type, this kind of things are problematic:

class Animal:
    def say_my_name(self) -> None:
        print("My name is Animal")

@runtime_checkable
class CanFlyProtocol(Protocol):
    def fly(self) -> None: ...

class Bird(Animal, CanFlyProtocol):
    def fly(self) -> None:
        print("Fly")

def let_it_fly(animal: Animal):  # we can't restrict the argument type to be Animal AND CanFlyProtocol
    animal.say_my_name()
    animal.fly()  # Error: "Animal" has no attribute "fly"

Even cast can't help us, but we can narrow the type!

def let_it_fly(animal: Animal):
    assert isinstance(animal, CanFlyProtocol)
    animal.say_my_name()
    animal.fly()
    reveal_type(animal)  # Revealed type: "<subclass of "Animal" and "CanFlyProtocol">"

Type checkers can understand Intersection when we narrow the type, great!

But what if we don't want to change runtime behavior? for this we can
replace assert with the suggested type_assert or ensure_type!

def let_it_fly(animal: Animal):
    type_assert isinstance(animal, CanFlyProtocol)
    animal.say_my_name()
    animal.fly()
    reveal_type(animal)  # Revealed type: "<subclass of "Animal" and "CanFlyProtocol">"

A side note: with type_assert we probably can stop adding @runtime_checkable to Protocols, if we only added it for this kind of type-hint only issues that assert isinstance(animal, CanFlyProtocol) solved.
Performing isinstance with Protocol is very slow so this benefit is not small.

ensure_type with Not

See:

def function(arg: Union[int, str, list]):
    type_assert not isinstance(arg, int)
    reveal_type(arg)  # Revealed type: "str | list"

How to do not isinstance with ensure_type? we have two options:

  1. Add a similar ensure_not_type

    def function(arg: Union[int, str, list]):
        ensure_not_type(arg, int)
        reveal_type(arg)  # Revealed type: "str | list"
    
  2. Wait for the Not[] type

    def function(arg: Union[int, str, list]):
        ensure_type(arg, Not[int])
        reveal_type(arg)  # Revealed type: "str | list"
    
Vorherrschende Sprache
Python
Sterne
1.8k
Forks
302
Ø Merge
23 Std.
Gemergte PRs (30 T.)
8

Entwicklungsumgebung

Die Einrichtungsdateien dieses Projekts haben wir noch nicht geprüft. Beginnen Sie mit der README; die allgemeinen Schritte stehen in unserem Leitfaden für den ersten Beitrag.

Erste Schritte

  1. Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
  3. Forken Sie das Repository und arbeiten Sie in einem Branch.
  4. Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.

Mehr aus python/typing

Alle Issues in python/typing

Ähnliche Issues

Weitere Issues zu Python

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.