IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

conversion de type avec les types hints


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné
    Homme Profil pro
    BTS SN IR
    Inscrit en
    Mai 2017
    Messages
    514
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : BTS SN IR

    Informations forums :
    Inscription : Mai 2017
    Messages : 514
    Par défaut conversion de type avec les types hints
    Bonjour,

    alors voila j'ai comme projet de faire une lib qui permettrait en décorant une fonction de convertir les paramètres de la fonction automatiquement, voici un petite exemple illustratif :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    def add(a: int, b: int) -> int:
    	return a +b
     
    @auto_convert()
    add(5, 6) # retourne 11
     
    @auto_convert()
    add("11", 2) # retourner 13
    Alors bon pour l'instant encore rien de bien concret je fais quelques tests, il semble qu'un moyen serait inspect.signature(f).parameters, un peu de regex et des eval.

    2 questions:
    - est ce que selon vous c'est une idée intéressante ou bien est-ce que les annotations de types ne doivent être utiliser qu'en tant que simples annotations ?
    - avez vous des conseils ?

  2. #2
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Salut,

    Dans l'absolu je dirais que add("11", 2) doit retourner une TypeError et rien d'autre.

    Ce n'est pas le rôle d'un inspecteur de code de corriger un bogue et encore moins silencieusement.

    Mes deux open cents.

  3. #3
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Warning dangers detected !!!

    Alors bon pour l'instant encore rien de bien concret je fais quelques tests, il semble qu'un moyen serait inspect.signature(f).parameters, un peu de regex et des eval.
    Surtout pas eval, si tu veux des entiers, alors tu détectes que l'ensemble des caractères de ta chaîne représente des entiers...

    alors voila j'ai comme projet de faire une lib qui permettrait en décorant une fonction de convertir les paramètres de la fonction automatiquement
    À quelle fin ? où se trouve le besoin ? Si derrière il n'y a pas un objectif, ou le besoin de s'en servir, l'intérêt se trouve où ?
    Je ne vois pas dans quelle utilisation on a besoin de cela, même dans une calculatrice.

  4. #4
    Membre chevronné
    Homme Profil pro
    BTS SN IR
    Inscrit en
    Mai 2017
    Messages
    514
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : BTS SN IR

    Informations forums :
    Inscription : Mai 2017
    Messages : 514
    Par défaut
    En faite j'en ai un peu marre dans mes codes de faire une tonne de if not isinstance(...): raise TypeError(...), je reviendrais d'ici avec un bout de code.

  5. #5
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Je pense que ton code exemple permet d'éviter (en ce qui me concerne) à juste titre isinstance

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def auto_convert(f):    
    def wrap(*args):
            values = list(map(int, args))
            return f(*values)
        return wrap
     
     
    @auto_convert
    def add(*numbers):
        return sum(numbers)
     
     
    print(add(5, 6)) # retourne 11
    print(add("11", 2)) # retourner 13
    Après ton exemple ne perçoit peut-il pas l'entièreté de ton problème, mais va falloir trouver quelque chose d'assez modulable si tu veux l'appliquer d'une manière répétée.

  6. #6
    Membre chevronné
    Homme Profil pro
    BTS SN IR
    Inscrit en
    Mai 2017
    Messages
    514
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 26
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : BTS SN IR

    Informations forums :
    Inscription : Mai 2017
    Messages : 514
    Par défaut
    bonjour,

    alors voila j'ai vraiment un embryon de test, pour pour montrer le principe. Ce code doit surement être avec des failles, il ne marche pas avec *args, **kwargs, les itérables en général mais c'est vraiment pour vous montrer le principe.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    import inspect
     
     
    def convert(f):
    	def wrapper(*args, **kwargs):
    		signature = inspect.signature(f)
    		params = signature.parameters
    		bind = signature.bind(*args, **kwargs)
     
    		converted_args = []
    		converted_kwargs = {}
     
    		if True:
    			converted_args = args
    			converted_kwargs = kwargs
     
    		for k, v in bind.arguments.items():
    			if params[k].annotation != inspect._empty:
    				v = eval(f"{params[k].annotation.__name__}({v})")
    				print(type(v), v)
    			else: # il n'y a pas d'annotation
    				print(type(v), v)
     
    		print()
     
    		return f(*converted_args, **converted_kwargs)
    	return wrapper
     
     
     
    @convert
    def foobar(a: int, b: int, c: int)-> int:
    	pass
     
     
    @convert
    def foobar2(a: str, b: bool, c: int)-> int:
    	pass
     
     
     
     
     
    if __name__ == "__main__":
    	foobar(5, "8", True)
    	foobar2(5, "8", True)
    Alors des cas d'utilisation pratique ça pourrais être par exemple on a un command prompt où on ne peut saisir que sous forme de chaine de caractère, après tout pourquoi pas, c'est un cas fictif, mais imaginons:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    def datime_converter(argument):
    	date_formats = [
    		"%d/%m/%Y+%Hh%M",	# [dd]/[mm]/[aaaa]+[HH]h[MM]
    		"%d/%m/%Y+%HH%M",	# [dd]/[mm]/[aaaa]+[HH]H[MM]
    		"%d/%m/%Y+%H:%M",	# [dd]/[mm]/[aaaa]+[HH]:[MM]
    		"%d/%m/%Y",			# [dd]/[mm]/[aaaa]
    		"%d-%m-%Y+%Hh%M",	# [dd]-[mm]-[aaaHH+[HH]h[MM]
    		"%d-%m-%Y+%HH%M",	# [dd]-[mm]-[aaaHH+[HH]H[MM]
    		"%d-%m-%Y+%H:%M",	# [dd]-[mm]-[aaaa]+[HH]:[MM]
    		"%d-%m-%Y",			# [dd]-[mm]-[aaaa]
    	]
     
    	for date_format in date_formats:
    		try:
    			return dt.strptime(argument, date_format)
    		except ValueError:
    			pass
    	raise ValueError("time date {0} does not match formats {1}".format(argument, date_formats))
     
     
    def delta_d(date1: datime_converter, date2: datime_converter):
    	return date1 - date2
    bon bien sur c'est un exemple bateau, mais dans le cas d'un système de command prompt on pourra faire delta_d 11/11/2012 03/08/2015.

    J'ai eu cette idée en utilisant d.py et le système de converter, et je dois avouer que c'est pratique sauf que .. c'est propre à d.py
    Je n'ai rien inventé je me suis juste dit "est-ce que c'est possible de faire ça? est-ce que ça ne va pas pas à l'encontre même de python ?" et bien essayons, demandons

  7. #7
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par défaut
    Citation Envoyé par flapili Voir le message
    En faite j'en ai un peu marre dans mes codes de faire une tonne de if not isinstance(...): raise TypeError(...)
    Si c'est le seul motif, alors le plus simple et le plus efficace est d'utiliser un outil qui vérifie le typage statique, par exemple mypy.

    Par exemple, si je crée un fichier "test.py" avec le contenu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    def add(a: int, b: int) -> int:
    	return a + b
     
    add(5, 6)
     
    add("11", 2)
    et que je lance en ligne de commande mypy test.py, cela m'écrit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    test.py:6: error: Argument 1 to "add" has incompatible type "str"; expected "int"
    Je précise que mypy n'est pas un interpréteur : il n'exécute pas le code. Il détecte l'erreur uniquement en lisant le code.

  8. #8
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Hello,

    Je ne sais pas si vous avez vu cette phrase, alors je là cite à nouveau sur ce post,

    Je converti arg en fonction de l'annotation (par exemple si j'ai int comme annotation je veux qu'il soit converti en entier, si j'ai comme annotation MaClasse alors je veux que mon argument soit converti en une instance de MaClasse
    Si je comprends bien, le PO souhaite selon les annotations, caster les paramètres de la fonction.
    Ceci est pour moi pas le travail des annotations de types.

    On est bien d'accord selon vos réponses, vous le confirmez, que les annotations imposent un type ou des types bien précis. La solution serait la création d'un décorateur.

    Cependant, à part la réponse de bistouille en partie, vous ne répondez pas à la question du PO et on est d'accord sur le fait que ce que demande le PO n'est pas une bonne pratique...

    Je verrai quand même pour ajouter une solution à la question de "convertir arg", le code ci-dessous,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import sys
     
    def deco(f):
     
        obj = {
            'int': int,
            'str': str,
        }
     
     
        def wrap(*args):
            values = []
            for ind, key in enumerate(f.__annotations__):
                fname = f.__annotations__[key].__name__
                try:
                    value = obj[fname](args[ind])
                    values.append(value)
                except ValueError:
                    print('Error with argument {}'.format(args[ind]))
                    sys.exit(-1)
            return f(*values)
        return wrap
     
     
     
     
     
     
    @deco
    def f(a: int, b: int):
        return a + b
     
     
    print(f(11, 3))
    print(f('11', 3))
    print(f('11', '3'))
    Voilà, c'est plutôt moche, moins qu'avec eval, mais c'est assez lourd... et on fait bien un cast selon les annotations présentées

    P.S J'ai pas fais tous les tests possibles (par exemple des annotations de types différentes).

Discussions similaires

  1. Souligner une ligne particulière avec les java hints
    Par steackfrite dans le forum Langage
    Réponses: 1
    Dernier message: 24/06/2013, 15h39
  2. Réponses: 0
    Dernier message: 04/11/2011, 10h10
  3. Réponses: 4
    Dernier message: 24/05/2007, 10h11
  4. Problème avec les champs de type table
    Par devdev dans le forum MS SQL Server
    Réponses: 5
    Dernier message: 16/12/2004, 16h05
  5. [Débutant][Phppgadmin] problème avec les types
    Par PoY dans le forum PostgreSQL
    Réponses: 3
    Dernier message: 19/08/2004, 17h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo