声明:所做笔记是基于廖雪峰老师的python教程
list,set,dict
-
只有list是可变对象
-
set 中的元素是无序的,比如s = {1, 2, 3},即使现在看起来1是第一个元素,2是第二个元素,但实际上可能并非如此,因此没有s[0],s[1]这种操作。
None和‘’、[]、{}等不一样。 -
只要x是非零数值、非空字符串、非空list等,就判断为True,否则为False。非零即为一。
-
for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value。默认迭代的是value,当然可以用d.keys()对key进行迭代。
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.items():
... print(k, '=', v)
...
y = B
x = A
z = C
切片
- tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple。
- 字符串’xxx’也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串。
- l[start: end] 表示的是从start到end-1的元素。
迭代
- Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。
- 因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。
- dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。
- 字符串也是可迭代对象
- 通过collections模块的Iterable类型判断某个对象是否是可迭代对象。
from collections import Iterable
isinstance('abc', Iterable)
- 如果要对list实现类似Java那样的下标循环怎么办?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身。
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
- 任何可迭代对象都可以作用于for循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for循环。
列表生成式
- range(start, end)生成的是从start到end-1的序列。
- 写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来。
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
- 还可以使用两层循环
>>> [x * y for x in range(1, 6) for y in range(6, 11)]
[6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 18, 21, 24, 27, 30, 24, 28, 32, 36, 40, 30, 35, 40, 45, 50]
>>> [x * y for x in range(1, 6) if x % 2 == 0 for y in range(6, 11) if y % 2 == 1]
[14, 18, 28, 36]
- 放在for后面的if是筛选条件,表示只选取序列中满足条件的元素。而放在前面的if是表达式,作用于x的取指,没有筛选功能。
- if写在for前面必须加else,否则报错
>>> [x if x % 2 == 0 for x in range(1, 11)]
File "<stdin>", line 1
[x if x % 2 == 0 for x in range(1, 11)]
^
SyntaxError: invalid syntax
生成器
- generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
- generator也是可迭代对象,所以,我们创建了一个generator后,一般不会去调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。
>>> L1 = ['Hello', 'World', 18, 'Apple', None]
>>> g = (x if x % 2 else -x for x in range(11))
>>> for x in g:
print(x)
0
1
-2
3
-4
5
-6
7
-8
9
-10
- 赋值语句
a, b = b, a + b
相当于
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]
- 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。
- 函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
- 把函数改成generator后,我们一般不会用next()来获取下一个返回值,而是直接使用for循环来迭代。但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中。
- 注意区分普通函数和generator函数,普通函数调用直接返回结果,generator函数的“调用”实际返回一个generator对象。
迭代器
-
可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
-
可以被next()函数调用并不断返回下一个值的对象称为迭代器。
-
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
-
Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
-
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列; -
range()返回的是一个可迭代对象(Iterable)。
高阶函数
- abs(-10)是函数调用,而abs是函数本身。
- 函数本身也可以赋值给变量,即:变量可以指向函数:
>>> f = abs
>>> f(-1)
1
- 函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!
如果把abs指向其他对象,会有什么情况发生:
>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
- 既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
map/reduce
- 我们先看map。map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
- reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
- 所有的Iterator都是Iterable,反之不成立。
filter
- 和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
- filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。
sorted
- sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
list= [1, -2, 3, -4, 5]
re = sorted(l, key = abs)
然后sorted()函数按照keys进行排序,并按照对应关系返回list相应的元素。
返回函数
- 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
- 我们在函数lazy_sum中又定义函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数,且他们不共用lazy_sum中的的变量。
- 返回的函数并没有立刻执行,而是直到调用了f()才执行。
- 另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
>>> f1()
9
>>> f2()
9
>>> f3()
9
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
装饰器
- 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
- 本质上,decorator就是一个返回函数的高阶函数。
偏函数
- functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单:
>>> def add(x, y):
return x + y
>>> ad = functools.partial(add, y = 1)
>>> ad(2)
3
>>> ad(4)
5
>>>
注意的问题:
def fun(a, b, c):
return a + b + c
f = functools.partial(fun, 1, 2)
如果用 partial 这么声明会将前两个参数固定,只需要传入第三个参数就行了。
一旦用了像ad = functools.partial(fun, b = 1)
这样的形式,则在调用fun函数时,b往后的参数需要加上对应的变量名:
def fun(x, y, z):
print('sum is', x + y + z)
ad(2, 3) #显示错误为 TypeError: fun() got multiple values for argument 'y'
ad(2, z = 3) #运行正确
即若不指定参数名,则默认的是从左边的参数开始赋值。
额外知识点
socket.socket()绑定的是一个随机的端口,使用bind()方法可以重新绑定。