基础知识

Iterable可迭代对象

可用isinstance()来判断是否是Iterable可迭代对象

from collections import Iterable

i = isinstance("tinuv",Iterable)
print(i)

常见的Iterable可迭代对象

Iterator迭代器对象

Iterator对象是一种惰性计算序列,不能提前知道长度,只能不断计算直到抛出StopIteration异常 同样可以通过isinstance()来判断是否是Iterator对象

from collections import Iterator

i = (x for x in range(1,9))
q = isinstance(i,Iterator)
print(q)

常见的Iterator对象

生成器(generator)都是Iterator但集合数据类型不是Iterator

Iterable对象变为Iterator对象

使用iter()函数 如:

from collections import Iterator
from collections import Iterable

my_name = "tinuv"
p = isinstance(my_name,Iterable)
print(p)
my_name = iter(my_name)
p = isinstance(my_name,Iterator)
print(p)

输出两个都是True

生成器

介绍

生成器是我在其他语言每怎么接触过的东西,所以要好好说一说.通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。这个例子我觉得说得很好,生成器保存的是一个算法,通过算法来推算出值,这根矢量图和位图的关系是差不多的,但局限也在这里,因为不可能永远能找到得一个函数或者说算法来刻画一个模型.

使用

生成器的使用很简单,类似于列表生成式,只是把中括号变为小括号

my_generator = (x*x for x in range(3,10))
print(next(my_generator))

next()方法返回下一个计算的值 当然也可以用迭代的方式

my_generator = (x*x for x in range(3,10))
for n in my_generator:
    print(n)

另外一种定义的generator的方法

用第一种定义generator的局限性实在是太大了,用定义函数的方法能解决大部分的问题,在定义函数的时候要加入关键字yield,返回的是一个generator,那么这个函数的执行过程与普通函数的执行过程有什么不同呢,这里引用廖雪峰老师的总结

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

再引用廖雪峰老师的一个例子,我觉得这个例子对说明这句话的含义有很大的表现力

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)

返回

>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

再写一个例子 斐波那契shulie

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

if __name__ == '__main__':
    my_generator = fib(5)
    print(next(my_generator))
    print(next(my_generator))
    print(next(my_generator))
    print(next(my_generator))

列表生成式

第一种列表生成式

list(range(start,end))返回一个list列表 要注意的是列表从start开始包括start(start不是下标而是自然的序号),结束于end但不包括end,他是一个前闭后开的区间.

比如

my_list = list(range(3,10))
print(my_list)

形成的列表是这样的

[3, 4, 5, 6, 7, 8, 9]

比较高级的列表生成式(带有for循环的表达式)

这个不好描述,但用起来是非常舒服的

用这种方式实现的示例:

my_list = [x for x in range(3,10)]
print(my_list)

结果是一样的

[3, 4, 5, 6, 7, 8, 9]

这种方式更大的优点在于可以实现筛选功能 如

my_list = [x for x in range(3,10) if x%2==0]
print(my_list)

上例可以筛选出偶数

还可以实现简单的处理功能

my_list = [x*x for x in range(3,10)]
print(my_list)

上例中返回了x的平方

切片操作(Slice)

切片操作是为了增强取元组tuple或者列表list部分元素的便利性而设计的,使用一个切片操作符:,这个操作符在很多数据处理软件都有.表示从某一个索引值开始到某一索引值结束,但不包括最后一个数,如my_list[3:6]表示从索引的3开始(包括3)到6(不包括6),是一个左闭右开区间.

如果是从0开始的可以将0省略如my_list[0:3]my_list[:3]是等价的

它同样支持倒数切片(因为listtuple支持倒数索引)如my_list[-3:-1]

但要注意的是切片只支持正的切片,也就是说必须索引从小到大,不支持从大到小切片

字符串也可看成是一个列表,因此也可以对字符串进行切片操作

如以下程序:

#coding:utf-8;
my_list = ["tinuv","deng","hui"]
# my_list[0:2]表示选择从0到2但不包括2
print(my_list[0:2])

会输出以下结果

['tinuv','deng']

介绍

这里的偏函数可不是高数里面的偏函数,他是通过设置参数的默认值降低函数调用的难度,如果是Java的话你不得不针对不同的参数写一堆不同的重载函数.

举个栗子

当在python中使用int函数将字符串转化成整型,默认是转化成十进制的

a = int("145")
print(a)

但其实他还有个参数可以控制进制 如转化成二进制

a = int('10101011', base=2)
print(a)

定义一个默认为二进制转十进制的函数

def int2(x,base = 2):
    return int(x,base)

a = int2('10101011')
print(a)

使用python的functools.partial包创建偏函数

from functools import partial


int2 = partial(int,base = 2)
a = int2('10101011')
print(a)

介绍

高阶函数不仅可以把函数作为参数,还可以返回函数,在这里我嗅到了浓浓的自定义高阶函数的味道,就是像map那样的函数,我不知道Python可不可以,但我猜是可以的,到底可不可以这里不做探究,以后再说吧

使用

因为我不太熟悉,我就借用廖雪峰老师的例子吧,好吧,为自己找了个借口

通常实现数组求和是这样的

def sum_list(list):
    s = 0
    for n in list:
        s = s+n
    return s

if __name__ == '__main__':
    my_list = [1,2,3,4,5]
    s = sum_list(my_list)
    print(s)

但如果现在不求和以后根据需要再求怎么办,那就返回函数

def sum_list(list):
    def sum_later():
        s = 0
        for n in list:
            s = s+n
        return s
    return sum_later

if __name__ == '__main__':
    s = sum_list([1,2,3,4,5])
    print(s())
值得注意的地方(踩了一个坑)

在放回函数的时候千万不能返回函数,否则相当于调用了内部的这个函数

def sum_list(list):
    def sum_later():
        s = 0
        for n in list:
            s = s+n
        return s
    return sum_later # 返回不能带括号

说明

闭包

另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs


if __name__ == '__main__':
    f1,f2,f3 = count()
    print(f1())
    print(f2())
    print(f3())

第一眼看可能的结果是1,4,9,的确我也是这样觉得的但是实际的结果却是

9
9
9

原因在于它并不立即执行,而是在返回时才会引用变量i,这是变量i已经变为了3

注意

返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量.

解决办法

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

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
🌹💗正文结束💗🌹