不可变对象和可变对象

  • 不可变对象:对象对应内存中的值不会变,因此如果指向该对象的变量被改变了,Pyhton则会重新开辟一片内存,变量再指向这个新的内存,包括int、float、str、tuple(元组)等。
    例:
    1
    2
    3
    4
    5
    6
    a = 1
    b = a
    b = 2
    print(a)

    # result: a = 1
  • 可变对象:对象对应内存中的值可以改变,因此变量改变后,该对象也会改变,即原地修改,如list、dictionary、set等。
    1
    2
    3
    4
    5
    6
    a = [1]
    b = a
    b.append(2)
    print(a)

    # result: a = [1,2]

注:

1
2
3
4
5
a = [1]
b = a
b = [1,2]
print(a)
# result: a = [1]

这里的变量a没有发生任何变化因为变量b指向了一个新的内存,详见下列代码进行对比:

1
2
3
4
5
6
a = [1]
b = a
print(id(a)==id(b))
b.append(2)
print(id(a)==id(b))
# result: True,True
1
2
3
4
5
6
7
a = [1]
b = a
print(id(a)==id(b))
b = [1,2]
print(id(a)==id(b))

# result: True,False

浅拷贝和深拷贝

  • 浅拷贝:使用copy()函数,拷贝了list最外围,而list内部的对象仍然是引用。
  • 深拷贝:使用deepcopy()函数,list内外围均为拷贝,因此前后的变量完全隔离,而非引用。
    例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import copy
    a = [1,2,3,[1,2,3]]
    b = a #变量内外部都是引用
    c = copy.copy(a) #浅拷贝,内部是引用
    d = a[:] #同上
    e = copy.deepcopy(a) #深拷贝,内外部都完全隔离不是引用
    a.append(4)
    a[3].append(4)
    print(b)
    print(c)
    print(d)
    print(e)
    '''
    result:
    [1, 2, 3, [1, 2, 3, 4], 4]
    [1, 2, 3, [1, 2, 3, 4]]
    [1, 2, 3, [1, 2, 3, 4]]
    [1, 2, 3, [1, 2, 3]]
    '''

局部变量和全局变量

  • 局部变量是无法修改全局变量的。想要实现局部修改全局变量,通常有两种办法,增加globa等关键字,或者使用list和dict等可变对象的内置函数。
    例:
    1
    2
    3
    4
    5
    6
    7
    a = 1 
    def local():
    a = 2 #局部变量,可以看做为新的变量
    local()
    print(a)

    # result: 1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    a = 1 
    b = [1]
    def local():
    global a #使用global关键字,表明在局部使用的是全局的a变量
    a = 2
    b.append(2) #对于可变对象,使用内置函数则会修改全局变量
    local()
    print(a)
    print(b)

    # result: 2,[1,2]

高阶函数

  • abs(): 返回数字的绝对值
    例:
    1
    2
    3
    print(abs(-1))

    # result: 1
  • map(): 可以将一个函数映射作用到可迭代的序列中,并返回函数输出的序列
    例:
    1
    2
    3
    4
    5
    6
    def f(x):
    return x + 1
    result = list(map(f,[1,2,3])) #python3中输出前需要转化为list
    print(result)

    # reuslt: [2, 3, 4]
  • reduce(): 输入的函数需要传入两个参数。reduce()的过程是先使用输入函数对序列中的前两个元素进行操作,得到的结果再和第三个元素进行运算,直到最后一个元素。
    例:
    1
    2
    3
    4
    5
    6
    7
    from functools import reduce  
    def f(x, y):
    return x*10+y
    result = reduce(f, [1, 2, 3, 4])
    print(result)

    #result: 1234
  • filter(): 通过输入函数对可迭代序列进行过滤,并返回满足过滤条件的可迭代序列
    例:
    1
    2
    3
    4
    5
    6
    def is_odd(n): 
    return n % 2 == 0
    result = list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
    print(result)

    #result: [2, 4, 6, 10]
  • sorted(): 函数可以完成对可迭代序列的排序。与列表本身自带的sort()函数不同,这里的sorted()函数返回的是一个新的列表。sorted()函数可以传入关键字key来指定排序的标准,参数reverse代表是否反向。
    例:
    1
    2
    3
    4
    result = list(sorted([3, 5, -87, 0, -21], key=abs, reverse=True))  # 绝对值排序,并且为反序
    print(result)

    #result: [-87, -21, 5, 3, 0]
  • 对于一些简单逻辑函数,可以使用lambda匿名表达式来取代函数的定义,类似于def
    例:
    1
    2
    add = lambda x,y: x + y
    print(add(1,2))

迭代器与生成器

  • 迭代器(iterator): 不要求事先准备好整个迭代过程中所有的元素,可以使用next()来访问元素。Python中的容器,如list、dict和set等,都属于可迭代对象,对于这些容器,我们可以使用iter()函数封装成迭代器。
    例:
    1
    2
    3
    4
    5
    6
    x = [1,2,3,4,5,6]
    y = iter(x)
    z = iter(x)
    print(next(y),next(z))

    #result: 1 1
    上面的例子证明了迭代器之间相互独立
  • 生成器(generator):是迭代器的一种,可以控制循环遍历的过程,实现一边循环一边计算,并使用yield来返回函数值,每次调用到yield会暂停。生成器迭代的序列可以不是完整的,从而可以节省出大量的内存空间。
    例:斐波那契数列
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def fibonacci():
    a = [1, 1]
    while True:
    a.append(sum(a)) # 往列表里添加下一个元素
    yield a.pop(0) # 取出第0个元素,并停留在当前执行点
    result = []
    for x in fibonacci():
    if x > 10:
    break # 仅打印小于10的数字
    result.append(x)
    print(result)

    #result: [1, 1, 2, 3, 5, 8]
  • 还可以使用”()”创建生成器
    例:
    1
    2
    3
    4
    a = (x for x in range(1,10))
    print(next(a))

    #result: 1