Side effect of getchas in python / numpy? scary stories and narrow shoots

I am considering moving from Matlab to Python / numpy for data analysis and numerical modeling. I have used Matlab (and SML-NJ) for many years, and I am very comfortable working in a functional environment without side effects (with the exception of I / O), but I'm a little reluctant to have side effects in Python. Can people share their favorite articles about side effects and, if possible, how they got around them? As an example, I was a bit surprised when I tried the following Python code:

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], [], [], []] 

early

+7
python functional-programming matlab side-effects
source share
2 answers

Obfuscating references to the same (mutable) object with references to individual objects is really "gotcha" (it suffers from all non-functional languages ​​that have mutable objects and, of course, links). A common mistake in Python code for beginners incorrectly uses the default value, which can be changed, for example:

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

This code may be correct if the goal is to addone to maintain its own state (and return one growing list to consecutive subscribers), since static data will work in C; this is not true if the encoder mistakenly believes that a new empty list will be created with each call.

The original newbies used for functional languages ​​can also be confused by the separation of the command line in Python's design solutions: mutating methods that have nothing in particular to return (i.e. the vast majority of mutation methods) return nothing (in particular, they come back None ) - they do all their work on the spot. Errors arising from misunderstanding are easy to detect, for example.

 alist = alist.append(item) 

pretty much guaranteed to be an error - it adds an item to the list denoted by the name alist , but then restores the name alist to None (the return value of the append call).

While the first issue I mentioned is about early binding, which can be confusing for people who think that binding is not late, there are problems that go differently when some people expect early binding , instead, the binding is belated. For example (with a hypothetical GUI ...):

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

ten buttons with the inscription "Button # 0", "Button # 1", etc. will be shown here, but each time they are pressed, each of them will say it #9 - because i inside lambda delayed (with a lexical closure). The fix is ​​to take advantage of the fact that the default values ​​for the early binding argument (as I pointed out the first problem! -) and change the last line to

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

Now lambda i is an argument with a default value, and not a free variable (distorted by lexical closure), and therefore, the code works as intended (of course, there are other ways).

+9
source share

I came across this recently again (after several years of python) trying to remove a little numpy dependency.

If you came from Matlab, you should use and trust numpy functions to handle a mono-type array. Along with matplotlib, they are very convenient packages for a smooth transition.

 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 

and etc.

0
source share

All Articles