27 votes

Pourquoi foo (* arg, x) n'est pas autorisé en Python?

Regardez l'exemple suivant

point = (1, 2)
size = (2, 3)
color = 'red'

class Rect(object):
    def __init__(self, x, y, width, height, color):
        pass

Il serait très tentant d'appel:

Rect(*point, *size, color)

Solutions de contournement possibles seraient:

Rect(point[0], point[1], size[0], size[1], color)

Rect(*(point + size), color=color)

Rect(*(point + size + (color,)))

Mais pourquoi est - Rect(*point, *size, color) pas permis, il n'existe aucune ambiguïté sémantique ou inconvénient, en revanche, vous pourriez penser?

EDIT: Questions Spécifiques

Pourquoi sont multiples *arg expansions pas autorisés dans les appels de fonction?

Pourquoi sont des arguments de position pas permise après *arg expansions?

11voto

ironchefpython Points 2209

Je ne vais pas vous dire pourquoi de multiples tuple déballage ne fait pas partie de Python, mais je ferai remarquer que vous n'êtes pas correspondant à votre classe à vos données dans votre exemple.

Vous avez le code suivant:

point = (1, 2)
size = (2, 3)
color = 'red'

class Rect(object):
    def __init__(self, x, y, width, height, color):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.color = color

mais une meilleure façon d'exprimer vos Rect objet serait comme suit:

class Rect:
    def __init__(self, point, size, color):
        self.point = point
        self.size = size
        self.color = color

r = Rect(point, size, color)

En général, si vos données sont dans des tuples, demandez à votre constructeur de prendre des n-uplets. Si vos données sont dans un dict, demandez à votre constructeur de prendre un dict. Si vos données est un objet, demandez à votre constructeur de prendre un objet, etc.

En général, vous voulez travailler avec les idiomes de la langue, plutôt que d'essayer de travailler autour d'eux.


MODIFIER En voyant à quel point cette question est, je vais vous donner un décorateur qui vous permet d'appeler le constructeur toutefois vous le souhaitez.

class Pack(object):

    def __init__(self, *template):
        self.template = template

    def __call__(self, f):
        def pack(*args):
            args = list(args)
            for i, tup in enumerate(self.template):
                if type(tup) != tuple:
                    continue
                for j, typ in enumerate(tup):
                    if type(args[i+j]) != typ:
                        break
                else:
                    args[i:i+j+1] = [tuple(args[i:i+j+1])]
            f(*args)
        return pack    


class Rect:
    @Pack(object, (int, int), (int, int), str)
    def __init__(self, point, size, color):
        self.point = point
        self.size = size
        self.color = color

Maintenant, vous pouvez initialiser votre objet tout comme vous l'aimez.

r1 = Rect(point, size, color)
r2 = Rect((1,2), size, color)
r3 = Rect(1, 2, size, color)
r4 = Rect((1, 2), 2, 3, color)
r5 = Rect(1, 2, 2, 3, color)

Bien que je ne recommande pas l'utilisation de ce dans la pratique (il viole le principe que vous ne devez avoir qu'une seule façon de le faire), il ne servent à démontrer qu'il y a généralement un moyen de faire quelque chose en Python.

9voto

dr jimbob Points 6876

Pour autant que je sais, c'est un choix de conception, mais il semble y avoir une logique derrière elle.

EDIT: l' *args de la notation dans un appel de fonction a été conçue de sorte que vous pouvez entrer un n-uplet de variables d'une longueur arbitraire qui pourrait changer entre les appels. Dans ce cas, avoir quelque chose comme f(*a, *b, c) n'a aucun sens, comme un appel, comme si a des changements de longueur de tous les éléments de b sont attribuées à la mauvaise variables, et c n'est pas dans le bon endroit non plus.

En gardant un langage simple, puissant, et normalisés est une bonne chose. Garder en synchronisation avec ce qui se passe vraiment dans le traitement des arguments est aussi une très bonne chose.

Pensez à la façon dont le langage déballe votre appel de fonction. Si plusieurs *arg sont autorisés dans n'importe quel ordre, comme Rect(*point, *size, color), note que l'important pour bien décompresser, c'est que le point et la taille ont un total de quatre éléments. Donc, point=(), size=(1,2,2,3), etcolor='red') permettrait Rect(*point, *size, color) à travailler comme un appel correct. Fondamentalement, la langue lorsqu'il analyse le *point et *la taille est de le traiter comme un combiné *arg n-uplet, alors Rect(*(point + size), color=color) plus fidèle représentation.

Il n'y a jamais besoin d'être deux tuples des arguments passés en la forme *args, vous pouvez toujours la représenter comme une. Depuis la cession des paramètres dépend uniquement de l'ordre dans ce combiné *arg la liste, il est logique de le définir comme tel.

Si vous pouvez faire des appels de fonction comme f(*a, *b), la langue prie pour vous permettre de définir des fonctions multiples *args dans la liste des paramètres, et ceux qui ne pouvaient pas être traitées. E. g.,

 def f(*a, *b): 
     return (sum(a), 2*sum(b))

Comment serait - f(1,2,3,4) seront-elles traitées?

Je pense que c'est la raison pourquoi syntaxique concret, la langue forces appels de fonction et les définitions dans la suite de formulaire spécifique; comme f(a,b,x=1,y=2,*args,**kwargs) qui est de l'ordre de la dépendance.

Tout ce qu'il y a un sens précis dans la définition d'une fonction et l'appel de fonction. a et b sont des paramètres définis sans valeurs par défaut, les prochaines x et y sont des paramètres définis avec des valeurs par défaut (qui peut être sauté; venez donc après l'aucun paramètres par défaut). Ensuite, *args est peuplée que d'un n-uplet avec tous les arguments rempli avec le reste des paramètres à partir d'un appel de fonction qui n'étaient pas des paramètres de mot clé. Cela vient après les autres, comme cela pourrait changer la longueur, et vous ne voulez pas quelque chose qui pourrait changer la longueur entre les appels à affecter affectation de variables. À la fin **kwargs prend tous les mots clés des arguments qui n'étaient pas définis ailleurs. Avec ces béton définitions vous n'avez jamais besoin d'avoir plusieurs *args ou **kwargs.

0voto

inspectorG4dget Points 25092

*point dit que vous êtes de passage dans toute une série d'éléments - quelque chose comme tous les éléments dans une liste, mais pas comme une liste.

Dans ce cas, vous ne pouvez pas limiter la façon dont beaucoup d'éléments sont actuellement en cours de transmission. Par conséquent, il n'existe aucun moyen pour l'interprète de savoir quels éléments de la séquence sont une partie de l' *points et *size

Par exemple, si vous avez passé le suivant en entrée: 2, 5, 3, 4, 17, 87, 4, 0, pouvez-vous me dire lesquels de ces nombres sont représentés par *points et qui en *size? C'est le même problème que l'interprète visage ainsi

Espérons que cette aide

0voto

e-satis Points 146299

Python est plein de ces petits défauts subtils. Par exemple, vous pouvez faire:

 first, second, last = (1, 2, 3)
 

Et vous ne pouvez pas faire:

 first, *others = (1, 2, 3)
 

Mais dans Python 3, vous pouvez maintenant.

Votre suggestion va probablement être suggérée dans un PEP et intégrée ou rejetée un jour.

0voto

tzot Points 32224

Eh bien, en Python 2, vous pouvez dire:

 point = 1, 2
size = 2, 3
color = 'red'

class Rect(object):
    def __init__(self, (x, y), (width, height), color):
        pass
 

Ensuite, vous pouvez dire:

 a_rect= Rect(point, size, color)
 

en veillant à ce que les deux premiers arguments soient des séquences de len == 2.
NB: Cette fonctionnalité a été supprimée de Python 3.

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