结果如图:
总结:+ 和 * 都遵循这个规律,不修改原有的操作对象,而是构建一个全新的序列。注意:如果a*n 这个语句中,序列a里面的元素是对其他可变对象的引用的话,这个结果可能会出乎意料。比如,你想用my_list = [[]]*3 来初始化一个由列表组成的列表,但是你得到的列表里面包含的3个元素其实是3个引用,而且这3个引用指向同一个列表。这显然不是想要的。正确的方式1:
board = [['_'] * 3 for i in range(3)] print(board) board[1][2] = 'X' print(board)结果如图:
错误的方式1:含有3个指向同一对象的引用的列表是毫无用处的。
weird_board = [['_']*3] *3 print(weird_board) weird_board[1][2] = '0' print(weird_board)结果如图:
错误的方式2: 这个和上一个错误的本质是一样的
row = [' _ ']*3 board = [] for i in range(3): board.append(row) print(board) board[1][2] ='0' print(board)结果如图: 正确的方式2: 这里的等同于正确的方式1
board = [] for i in range(3): row = ['_']*3 # 每次迭代中都新建了一个列表,作为新的一行添加到board board.append(row) print(board) board[1][2] ='0' print(board)结果如图:
增量赋值运算符+=和*=的表现取决于它们的第一个操作对象,本次的就集中讨论(+=)。对其他的增量运算符也同样适用。 +=背后的特殊方法是__iadd__(用于‘就地加法’),所谓的‘就地’理解为:不创建新的对象,而是将原来的对象重新赋值。如果一个类没有实现这个方法的话,python会退一步调用__add__. 我们考虑下面这个简单的表达式:
a+=b
讨论: (1).若a实现了__iadd__方法,就会调用这个方法。同时对于可变序列(list,bytearray和array.array)来说,a会就地变动了,就像调用了a.extend(b)一样 (2).若a没有实现__iadd__方法,a+=b这个表达式的效果就会根a=a+b一样了;首先计算a+b,得到一个新的对象,然后赋值给a。
总结: 在这个表达式中,变量名会不会被关联到新的可变对象,完全取决于这个类型有没有实现__iadd__这个方法。总的来说,可变序列一般都实现了__iadd__这个方法,因此+=是就地加法。而不可变序列没有实现这个方法,就会像步骤(2)那样操作。后者相对应的是__imul__.
例子:
l = [1, 2, 3] print('before id(l):',id(l)) l *=2 print(l) print('after id(l):',id(l)) t = (1, 2, 3) print('before id(t):',id(t)) t *= 2 print('after id(t):',id(t)) # 运用增量惩罚后,新的元组被创建结果如图:
结果:t[2]被改动了,但是也有异常出现。但是我们可以写成t[2].extend([50,60])来避免这个异常。
教训:
不要把可变对象放在元组里面增量赋值不是一个原子操。它虽然会抛出异常,但是还是完成了操作查看python的字节码并不难,它对我们了解代码背后的运行机制很有帮助。结果如图: 结论: 结果是一样的,但是+号生成的是一个新的对象,而extend则是在原地的修改对象。 extend()的运算效率比+更高。但是, d.extend(e) 的返回结果是None,而不是合并后的序列。d序列是合并后的序列。 c返回的是新的序列。
list.sort方法会就地排序列表,这也是这个方法返回None的原因,按照python的惯例:如果一个函数或者方法对对象进行的是就地改动,那它就应该返回None. 与list.sort相反的是sorted,它会新建一个列表作为返回值。这个方法可以接受人和形式的可迭代对象作为参数,甚至包括不可变序列或者生成器,但是它总是返回一个列表。 不管list.sort或者sorted函数,都有两个参数,reverse和key
reverse:默认为False,就是升序排列,True为降序排列key:对一些字符串进行排序的时候,key=str.lower来实现忽略大小写的排序;key=len来显示根据字符串长度来排序。例子:
fruits = ['grape', 'raspberry', 'apple', 'banana'] print("未排序:", fruits) print(sorted(fruits)) print("sorted:", fruits) print(sorted(fruits,reverse=True)) print(sorted(fruits,key=len)) print(sorted(fruits,key=len,reverse=True)) print(fruits) fruits.sort() print(fruits)结果如下: 可以发现,直到执行了fruits.sort方法,fruits本身才发生了变化。 排好序的序列可以用来进行快速搜索,而标准库的bisect模块给我们提供了二分查找算法。
bisect模块包含类 两个主要函数,bisect和insert,两个函数都利用二分查找算法在有序序列中查找或者插入元素。 在有序序列中用bisect查找某个元素的插入位置。 例子1:
import bisect import sys HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30] NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31] ROW_FMT = '{0:2d} @ {1:2d} {2}{0:2d}' def demo(bisect_fn): for needle in reversed(NEEDLES): position = bisect_fn(HAYSTACK, needle) #返回插入的位置 offset = position * ' |' print(ROW_FMT.format(needle, position, offset)) # return bisect_fn if __name__ == '__main__': if sys.argv[-1] == 'left': bisect_fn = bisect.bisect_left #当碰到相等的元素的时候,会放在它左边(前边) else: bisect_fn = bisect.bisect #或者是bisect_fn = bisect.bisect_right,当碰到相等的元素的时候,会放在它的右边(后边) print('DEMO:', bisect_fn.__name__) print('haystack ->', ' '.join('-' % n for n in HAYSTACK)) demo(bisect_fn)例子2:根据分数找到它对应的成绩
import bisect def grade(score,breakpoint = [60,70,80,90],grades='FDCBA'): i = bisect.bisect(breakpoint,score) # 返回i,匹配grades return grades[i] t = [grade(score) for score in [33,99,77,70,89,91,10]] print(t)输出:
['F', 'A', 'C', 'C', 'B', 'A', 'F']insort(seq,item) 把变量item插入到序列seq中,并能保持seq的升序顺序。
import bisect import random SIZE = 7 random.seed(1729) my_list = [] for i in range(SIZE): new_item = random.randrange(SIZE*2) bisect.insort(my_list, new_item) print('- ->' % new_item, my_list)结果如图:
补充: %和format的区别参考:https://www.cnblogs.com/zhaopanpan/p/8875765.html (%和format的区别)
科学计算有关的算法
窥探NumPy二维数组的基本操作
import numpy a = numpy.arange(12) print(a) print(type(a)) print(a.shape)#查看维度 a.shape = 3, 4 #变成二维的 print(a) print(a[2]) print(a[2, 1]) print(a[:, 1]) t = a.transpose() #行列交换,得到**新的数组** print(t)结果如下: 可以发现t是一个新的数组。
补充:markdown生成目录方式,在开头输出[toc]