Le code suivant:

import inspect
from typing import NamedTuple

class Example(NamedTuple):
    a: str

if __name__== "__main__":
    signature: inspect.Signature = inspect.signature(Example)
    print(signature)

Les sorties:

(a: str)

Cependant, lors de l'activation du PEP 563 - Évaluation différée des annotations:

from __future__ import annotations
import inspect
from typing import NamedTuple

class Example(NamedTuple):
    a: str

if __name__== "__main__":
    signature: inspect.Signature = inspect.signature(Example)
    print(signature)

La sortie est:

(a: 'str')

Comment puis-je obtenir exactement le même objet de type inspect.Signature avec PEP 563 comme sans lui?

6
Tobias Hermann 20 nov. 2018 à 13:29

3 réponses

Meilleure réponse

Le but de l'utilisation du PEP 536 est de ne pas évaluer les annotations, sauf si cela est nécessaire. La signature fait simplement état des annotations.

Si, pour vos besoins, vous devez résoudre les annotations, vous devez le faire vous-même. Le PEP 536 indique aux documents comment vous procédez:

Pour le code qui utilise des indications de type, la fonction typing.get_type_hints(obj, globalns=None, localns=None) évalue correctement les expressions à partir de sa forme chaîne.

[...]

Pour le code qui utilise des annotations à d'autres fins, un appel régulier à eval (ann, globals, local) suffit pour résoudre l'annotation.

Vous pouvez même utiliser la typing.get_type_hints() fonction pour attribuer de nouveau à __annotations__ avant d'obtenir la signature:

import typing

Example.__new__.__annotations__ = typing.get_type_hints(Example.__new__)
signature: inspect.Signature = inspect.signature(Example)

Cela est sûr même si from __future__ import annotations n'a pas été utilisé.

4
Martijn Pieters 22 nov. 2018 à 23:38

Tout d'abord, exécutons un autre exemple:

signature: inspect.Signature = inspect.signature(Example)
print(signature)
print(Example.__annotations__)

Cela imprime:

(a: str)
OrderedDict([('a', <class 'str'>)])

Jusqu'ici tout va bien, nous avons ou Signature et notre __anotations__ comme nous l'espérions.

Maintenant, faisons de même pour le deuxième exemple, il affiche:

(a: 'str')
OrderedDict([('a', ForwardRef('str'))])

Vous n'obtenez donc pas les mêmes Signature ici. L'un vous donne la classe réelle et l'autre un typing.ForwardRef à la classe.

1
yorodm 22 nov. 2018 à 22:53

Vous devez réellement utiliser eval pour obtenir le même comportement:

from __future__ import annotations
import inspect
from typing import NamedTuple

class Example(NamedTuple):
    a: str

signature: inspect.Signature = inspect.signature(Example)
print(signature)

# extra bit
globalns = getattr(Example, '__globals__', {})
for param in list(signature.parameters.values()):
  if isinstance(param.annotation, str):
    param._annotation = eval(param.annotation, globalns)

print(signature)

Tu auras:

(a: 'str')
(a: str)

Vous pouvez également modifier __annotations__ avant d'appeler inspect.signature(obj), mais je trouve cela trop difficile, car je dois couvrir plusieurs cas différents.

Réponse de @Martijn Pieters manque un détail sur typing.get_type_hints:

si nécessaire, ajoute facultatif [t] si une valeur par défaut égale à Aucune est définie

Exemple:

# without imporing annotations from __future__
import inspect
import typing

def func(a: str=None): pass
print(inspect.signature(func))
func.__annotations__ = typing.get_type_hints(func)
print(inspect.signature(func))

Tu auras:

(a: str = None)
(a: Union[str, NoneType] = None)
0
m4js7er 1 nov. 2019 à 20:22