2010-03-09 8 views
7

Je songe à passer de Matlab à Python/numpy pour l'analyse de données et les simulations numériques. J'ai utilisé Matlab (et SML-NJ) pendant des années, et je suis très à l'aise dans l'environnement fonctionnel sans effets secondaires (sauf les E/S), mais je suis un peu réticent à propos des effets secondaires en Python. Les gens peuvent-ils partager leurs trucs favoris concernant les effets secondaires et, si possible, comment ils ont pu les contourner? À titre d'exemple, j'étais un peu surpris quand j'ai essayé le code suivant en Python:effets secondaires gotchas en python/numpy? histoires d'horreur et évasions étroites voulues

lofls = [[]] * 4 #an accident waiting to happen! 
lofls[0].append(7) #not what I was expecting... 
print lofls   #gives [[7], [7], [7], [7]] 
#instead, I should have done this (I think) 
lofls = [[] for x in range(4)] 
lofls[0].append(7) #only appends to the first list 
print lofls   #gives [[7], [], [], []] 

merci à l'avance

+0

Ce fut la principale Gotcha pour moi de passer à Python, mais je pense qu'il est aussi un Gotcha commun pour les personnes qui commencent à utiliser Python sans avoir utilisé Matlab avant. Je pourrais toutefois avoir tord. –

+1

En relation: http://zephyrfalcon.org/labs/python_pitfalls.html http://www.ferg.org/projects/python_gotchas.html – jfs

Répondre

9

références Confondre à l'objet même (mutable) avec des références à des objets séparés est en effet un " gotcha "(subi par tous les langages non fonctionnels, ceux qui ont des objets mutables et, bien sûr, des références). Un bug souvent vu dans le code Python débutants utilise à mauvais escient une valeur par défaut qui est mutable, par exemple:

def addone(item, alist=[]): 
    alist.append(item) 
    return alist 

Ce code peut être correct si le but est d'avoir addone garder son propre état (et le retour d'une liste croissante aux appels successifs), autant que les données static fonctionneraient en C; ce n'est pas correct si le codeur suppose à tort qu'une nouvelle liste vide sera faite à chaque appel.

La décision de conception command-query separation dans les conteneurs intégrés de Python peut également perturber les débutants bruts utilisés dans les langages fonctionnels: les méthodes de mutations qui n'ont rien à renvoyer (la grande majorité des méthodes de mutation) ne retournent rien (en particulier, ils retournent None) - ils font tout leur travail "sur place". Les bogues provenant d'un malentendu sont faciles à repérer, par ex.

alist = alist.append(item) 

est à peu près garanti d'être un bug - il ajoute un élément à la liste mentionnée par nom alist, mais reliaisons puis le nom alist-None (la valeur de retour de l'appel append). Alors que le premier problème que j'ai mentionné concerne une liaison précoce qui peut tromper les gens qui pensent que la liaison est plutôt tardive, il y a des problèmes qui vont dans l'autre sens, où les attentes de certaines personnes sont pour une liaison précoce. tandis que la reliure est plutôt en retard. Par exemple (avec un cadre graphique hypothétique ...):

for i in range(10): 
    Button(text="Button #%s" % i, 
      click=lambda: say("I'm #%s!" % i)) 

cela montrera dix boutons disant « Bouton # 0 », « Bouton # 1 », etc, mais, quand on clique dessus, chacun des ils c'est #9 - parce que le idans le lambda est lié tardivement (avec une fermeture lexicale). Un correctif est de tirer profit du fait que les valeurs par défaut pour l'argument sont précoce liés (comme je l'ai souligné au sujet de la première question -) et changer la dernière ligne

  click=lambda i=i: say("I'm #%s!" % i)) 

Maintenant lambda « s i est un argument avec une valeur par défaut, pas une variable libre (recherchée par fermeture lexicale), et donc le code fonctionne comme prévu (il y a d'autres façons, bien sûr).

+1

Le problème des valeurs par défaut mutables est si courant qu'il apparaît dans tous les tutoriels en python que j'ai vu, et j'ai été effrayé directement par elle. La règle empirique que vous énoncez concernant la modification «en place» est bonne, et je pense que cela me prendra très loin. La liaison tardive dans l'expression de lambda que vous donnez est, cependant, très énigmatique à moi; c'est inverse de ce qui se passe dans matlab, et je ne suis pas sûr que j'aime ça. c'est les pauses, cependant. – shabbychef

+0

@shabbychef, la valeur liée à un nom est toujours recherchée au moment où elle est nécessaire - pas plus tôt (et bien sûr pas plus tard ;-). Les variables libres utilisées dans les fonctions (lambda ou autres) qui sont des variables locales dans une fonction "externe" contenant le lexique ne font pas exception à cette règle! Le corps d'une fonction (encore une fois, lambda ou non) s'exécute toujours quand la fonction est appelée, pas plus tôt (et, bien sûr, pas plus tard) - si clairement que les valeurs des variables libres sont nécessaires, donc c'est quand leva les yeux. Quel autre comportement pourrait être cohérent? –

+0

curieusement, le comportement de Matlab à cet égard est ce que j'ai appris à connaître et à tolérer; on peut définir une fonction anonyme (essentiellement un 'lambda'), qui utilise des variables de la portée externe, et elles sont liées à la valeur lors de la création de la fonction anonyme. en fait, on peut enregistrer la fonction anonyme à classer (essentiellement décapage), fermer matlab, éteindre l'ordinateur, revenir, redémarrer, recharger, et la fonction anonyme fonctionne toujours, a encore des variables de son contexte. Je pense que Mathworks a dû le faire b/c leurs fonctions anonymes sont si limitées dans l'utilité sinon ... – shabbychef

0

Je suis tombé sur celui-ci récemment encore, (après des années de python) tout en essayant d'enlever une petite dépendance sur numpy.

Si vous venez de matlab, vous devez utiliser et faire confiance aux fonctions numpy pour la gestion des matrices de type mono. Avec matplotlib, ils sont des paquets très pratiques pour une transition en douceur.

import numpy as np 
np.zeros((4,)) # to make an array full of zeros [0,0,0,0] 
np.zeros((4,1)) # another one full of zeros but 2 dimensions [[0],[0],[0],[0]] 
np.zeros((4,0)) # an empty array like [[],[],[],[]] 
np.zeros((0,4)) # another empty array, which can not be represented with python lists o_O 

etc.

Questions connexes