80 votes

Pourquoi Python autorise-t-il les appels de fonction avec un nombre erroné d'arguments ?

Python est mon premier langage dynamique. J'ai récemment codé un appel de fonction en fournissant un nombre erroné d'arguments. Cela a échoué avec une exception au moment où cette fonction a été appelée. Je m'attendais à ce que, même dans un langage dynamique, ce type d'erreur puisse être détecté lors de l'analyse du fichier source.

Je comprends que le type des arguments réels n'est pas connue avant l'appel de la fonction, car la même variable peut contenir des valeurs de n'importe quel type à différents moments. Mais la numéro des arguments est connu dès que le fichier source est analysé. Il ne va pas changer pendant l'exécution du programme.

Pour que ce ne soit pas une question philosophique

Pour rester dans le cadre de Stack Overflow, laissez-moi formuler la question comme suit. Python offre-t-il une fonctionnalité qui l'oblige à retarder la vérification du nombre d'arguments dans un appel de fonction jusqu'à ce que le code soit réellement exécuté ?

17 votes

Et si le nom fait référence à une fonction totalement différente au moment de l'exécution ?

0 votes

Ainsi, comme contre-argument, le nombre d'arguments pour invoquer f(*args) ne sera pas connu pendant la phase d'analyse syntaxique.

0 votes

@Rogalski mais d'un autre côté, aucun nombre d'arguments positionnels à cela ne soulèverait la TypeError !

146voto

Martijn Pieters Points 271458

Python ne peut pas savoir à l'avance quel objet vous allez appeler, parce qu'étant dynamique, vous pouvez échanger l'objet fonction . A tout moment. Et chacun de ces objets peut avoir un nombre différent d'arguments.

Voici un exemple extrême :

import random

def foo(): pass
def bar(arg1): pass
def baz(arg1, arg2): pass

the_function = random.choice([foo, bar, baz])
print(the_function())

Le code ci-dessus a 2 chances sur 3 de lever une exception. Mais Python ne peut pas savoir a-priori si ce sera le cas ou non !

Et je n'ai même pas commencé avec les importations dynamiques de modules, la génération dynamique de fonctions, d'autres objets appelables (tout objet avec un __call__ peut être appelée), ou des arguments fourre-tout ( *args y **kwargs ).

Mais pour que ce soit encore plus clair, vous dites dans votre question :

Il ne va pas changer pendant que le programme est en cours.

Ce n'est pas le cas, pas en Python. Une fois le module chargé, vous pouvez supprimer, ajouter ou remplacer n'importe quel objet dans l'espace de noms du module, y compris les objets de fonction.

0 votes

J'ai vu des tables de fonctions avant, donc ça pourrait ne pas être tout à fait si maléfique. Oh attends, les fonctions dans la table n'utilisent pas * arguments.... et c'est Python so.... yep, assez extrême.

3 votes

C'est un t-shirt maintenant. (Je n'ai pas réussi à trouver comment ajouter l'attribution. Si vous la voulez, dites-le moi, et je peux probablement trouver une solution).

0 votes

@PyRulez : Mon t-shirt avec un Copie en forme de licorne du post de regex-parsing utilise une URL. Vous pourriez simplement ajouter https://stackoverflow.com/a/34567789 et en finir avec ça.

35voto

Jonas Wielicki Points 4624

Le nombre d'arguments passés est connu, mais pas la fonction qui est réellement appelée. Voir cet exemple :

def foo():
    print("I take no arguments.")

def bar():
    print("I call foo")
    foo()

Cela peut sembler évident, mais plaçons-les dans un fichier appelé "fubar.py". Maintenant, dans une session Python interactive, faites ceci :

>>> import fubar
>>> fubar.foo()
I take no arguments.
>>> fubar.bar()
I call foo
I take no arguments.

C'était évident. Maintenant, la partie amusante. Nous allons définir une fonction qui nécessite un nombre non nul d'arguments :

>>> def notfoo(a):
...    print("I take arguments!")
...

Maintenant, nous faisons quelque chose qui s'appelle _singe Parcheando_ . Nous pouvons en effet remplacer la fonction foo dans le fubar module :

>>> fubar.foo = notfoo

Maintenant, quand nous appelons bar , a TypeError sera relevé ; le nom foo fait maintenant référence à la fonction que nous avons définie ci-dessus au lieu de la fonction originale anciennement connue sous le nom de- foo .

>>> fubar.bar()
I call foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/horazont/tmp/fubar.py", line 6, in bar
    foo()
TypeError: notfoo() missing 1 required positional argument: 'a'

Donc même dans une situation comme celle-ci, où il pourrait sembler évident que la fonction appelée foo ne prend pas d'arguments, Python peut seulement savoir qu'il s'agit en fait de la fonction foo qui est appelée lorsqu'elle exécute cette ligne source.

C'est une propriété de Python qui le rend puissant, mais qui est aussi à l'origine de certaines de ses lenteurs. En effet, rendre les modules en lecture seule pour améliorer les performances a été discuté sur la liste de diffusion python-ideas il y a quelque temps, mais elle n'a pas obtenu de réel soutien.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X