为了不因为一上来就是一大堆概念导致看不下去,我还是先举个栗子吧:
代码源自 http://www.python.org/doc/essays/graphs/
'''http://www.python.org/doc/essays/graphs/''' graph = {'A': ['B', 'C'], 'B': ['C', 'D'], 'C': ['D'], 'D': ['C'], 'E': ['F'], 'F': ['C'] } def find_all_paths(graph, start, end, path=[]): path = path + [start] if start == end: return [path] if not graph.has_key(start): return [] paths = [] for node in graph[start]: if node not in path: newpaths = find_all_paths(graph, node, end, path) for newpath in newpaths: paths.append(newpath) return paths print find_all_paths(graph, 'A', 'D')
程序输出为:
>>>[['A', 'B', 'C', 'D'], ['A', 'B', 'D'], ['A', 'C', 'D']]但是如果我把find_all_paths函数的第一行
path = path + [start]换成
path += [start]再运行程序,输出为:
>>>[['A', 'B', 'C', 'D']]啊咧~说好的正确答案呢。
其实发现这个问题是在学习设计模式时遇到的,出问题的代码是这里:http://www.pythontip.com/pythonPatterns/detail/graph_search
正上方链接的例子里其实还有一点问题:self.start,self.end,self.path这样写真的大丈夫?self.xxx是实例属性,在方法中给其赋值,会改变他的值,而且此处的方法还是个递归方法,当某一层的递归返回时,它的上一层方法里的对象属性self.xxx却被修改了,出错是必须的,以上。好的,继续说这个+=的问题。
为什么会出现+=的结果出错呢,这里让我引经据典一下(具体内容来自《Python核心编程(第二版)》第42~43页):
P42“在Python语言中,对象是通过引用传递的,在赋值时,不管这个对象是新创建的,还是一个已经存在的,都是将该对象的引用(并不是值)赋值给变量。”
P43“增量赋值相对普通赋值不仅是写法上的改变,最有意义的变化是第一个对象仅被处理一次。可变对象会被就地修改(无修拷贝引用)[注:无修是神马,推测可能是想说无需。。],不可变对象则和A=A+B的结果一样(分配一个新对象),...”
也就是说 例:A=[1];A+=[2];那么A还是那个A(也就是id(A)没变)
而A=[1];A = A+[2];此时A不是以前的那个A了,而是一个新分配的对象(id(A)变了)
很坑爹对不对,对不对,竟然还有这样的小细节,说起来真得感谢http://www.pythontip.com这个网站的朋友(非嘲讽,真心感谢)。
回到find_all_paths这个函数,传入参数时,函数参数是否可变的问题。
1.若传入不可变类型,比如int类型,那么在函数中对该int类型进行+=操作也会生成新的int对象(参见上方P43的引用)
2若是传入可变类型就是另一回事了。因为path是可变类型list,所以作为函数参数传入后,如果使用path = path + xxx,那么新的path对象产生,一切OK;但是若使用path+=xxx,那么直接修改了该path对象的值(让我想起了以前与C指针战斗的日子,多么怀念。。。)
第一次写博文,不知道有没有讲清楚,反正先就这样吧。:)