Python基本特性进阶

Python中有许多强大的功能,简单实用,接下来一一列举。

切片

这个功能不用说了,实用指数五颗星!list、tuple、string等类型都可以使用切片操作,不多说,直接上代码

>>> l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[5:6]
[5]
>>> l[:5]
[0, 1, 2, 3, 4]
>>> l[5:]
[5, 6, 7, 8, 9]
>>> l[5:-1]
[5, 6, 7, 8]
>>> l[::2]
[0, 2, 4, 6, 8]
>>> l[2::]
[2, 3, 4, 5, 6, 7, 8, 9] 
>>> str = "Jack Dauson"
>>> str[-3:]
'son'
>>> str[:]
'Jack Dauson'
>>> str[5:]
'Dauson'

总之就是相当灵活,省去了很多循环操作!

迭代

只要能够用for循环遍历的数据结构,都属于可迭代的!像list、tuple、dict、string都是可以迭代的对象!

list

>>> l = [2,3,4]
>>> for i in l:
    print(i)

2
3
4

有一个问题:如果想要得到list里面的索引改怎么办?使用enumerate()可以得到所有可迭代对象的索引及相应的值!

>>> for i,value in enumerate(l):
    print(i,value)

0 2
1 3
2 4 

string


>>> for letter in 'Jack':
    print(letter)

J
a
c
k

dict

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
    print(key,d[key])
b 2
a 1
c 3

以上只是最简单的迭代,如果想要仅仅遍历dict的键、值、键值对,应该怎么做呢?

>>> for value in d.values():
    print(value)
2
1
3
>>> for key in d.keys():
    print(key)
b
a
c
>>> for key,value in d.items():
    print(key,value)
b 2
a 1
c 3
>>> for i in d.items():
    print(i)
('b', 2)
('a', 1)
('c', 3)

使用d.keys()d.values()d.items()可以获得dict的更多内容!

那么,问题来了,我怎么样能够判断一个对象是不是可迭代对象呢?

>>> from collections import Iterable
>>> isinstance([1,2,3],Iterable)
True
>>> isinstance({1:2,2:3},Iterable)
True
>>> isinstance('Jack',Iterable)
True
>>> isinstance(123,Iterable)
False

使用collection模块的Iterable类型可以判断某一对象是否为可迭代对象!

列表生成式

列表生成式,顾名思义,就是用来生成列表的啦!大多数人生成列表,先定义一个l = [],再使用for循环将元素一个个append到list中,毫无美感!!!

简单列表的生成,可以使用list(range(1,100)),那么如果需要生成[1*1,2*2,3*3...]呢?不要告诉我循环!

>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

同时,还可以添加一些更多的要素,判断使用符合要求的x参与计算

>>> [x*x for x in range(10) if x%2 == 0]
[0, 4, 16, 36, 64]

使用多重循环还可以生成全排列!

>>> [x+y for x in 'ABC' for y in 'LMN']
['AL', 'AM', 'AN', 'BL', 'BM', 'BN', 'CL', 'CM', 'CN']

>>> [x+y+z for x in 'ABC' for y in 'LMN' for z in 'OPQ' if z == 'Q']
['ALQ', 'AMQ', 'ANQ', 'BLQ', 'BMQ', 'BNQ', 'CLQ', 'CMQ', 'CNQ']
>>> 

功能强大吧!这样就可以替代for循环优雅地生成列表

上面的功能总体来说还比较易于接受,接下来要放大招了!

生成器generator

通过列表生成器,我们可以创造一个列表。但是,受到内存容量的限制,列表的容量肯定是有限的,如若我们只需要使用列表中的一部分,就不需要一下子生成很大的列表,直接按需生成就好了!列表生成器提供了这样的功能。

生成器相当于是一个生成列表元素的算法,有了这个算法,就不用一下子生成很多元素了,只需要在需要的时候循环生成就可以了,节省了很多空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

那么,如何创建一个generator呢?只需要把列表生成器的[]改成()就可以了!

>>> [x*x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> (x*x for x in range(10))
<generator object <genexpr> at 0x03340C60>

如何获取generator里面的元素呢?可以使用next()函数生成generator的下一个元素。

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<pyshell#52>", line 1, in <module>
    next(g)
StopIteration

一直调用next()函数就可以遍历generator的所有元素,直到抛出StopIteration错误。
纳尼!一直next()到结束?太奇怪了!最常用的当然还是要for循环,毕竟generator也是Iterable的对象嘛!

>>> g = (x*x for x in range(10))
>>> for element in g:
    print(element)
0
1
4
9
16
25
36
49
64
81

所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

有时候,推算元素的算法很复杂,并不能通过列表生成器实现,而需要通过函数打印出来,譬如fibonacci数列

>>> def fib(Max):
    n = 0
    a,b = 0,1
    while n < Max:
        print(b)
        a,b = b,a+b
        n = n + 1   
>>> fib(5)
1
1
2
3
5

这个时候,我应该如何根据一个函数生成generator呢?

一个函数只要含有yield关键字,就不再是一个普通函数,而是一个generator!
所以只需要把print(b)改为yield b就构造了一个generator

>>> def fib(Max):
    n = 0
    a,b = 0,1
    while n < Max:
        yield b
        a,b = b,a+b
        n = n + 1
>>> g = fib(5)
>>> next(g)
1
>>> next(g)
1
>>> for i in g:
    print(i)
2
3
5

看到了没有,在函数的执行过程中,遇到return才会返回;但是变成了generator的函数,遇到了yield就会中断,并且返回yield的参数。

那么,遇到了yield就会中断并返回,我们能不能通过yield传一些参数进去呢?

>>> def fib(Max):
    n = 0
    a,b = 0,1
    while n < Max:
        e = yield b
        print(e)
        a,b = b,a+b
        n = n + 1
>>> g = fib(6)
>>> next(g)
1
>>> next(g)
None
1
>>> g.send('the third element')
the third element
2
>>> g.send('forth')
forth
3

在上面的generator定义中,使用了e = yield b语句,这样遇到yield返回b的同时,还能够通过g.send(arg)传入一个参数赋给e,这样,就可以通过generator进行参数传递了!

>>> g = fib(6)
>>> g.send('the First element')
Traceback (most recent call last):
  File "<pyshell#94>", line 1, in <module>
    g.send('the First element')
TypeError: can't send non-None value to a just-started generator
>>> g.send(None)
1

注意,看上面的代码,为什么会报错?说明启动一个generator的时候,只能传入None作为参数,而g.send(None)就相当于next(g)!

迭代器

说完了生成器generator,我们来看看迭代器iterator

前面提到,凡是能够用for循环遍历的数据结构和对象统统可以称为可迭代的,即Iterable。我们现在接触到Iterable数据结构有list、tuple、dict、string、set,还有生成器generator。

但是,他们之间有什么区别呢?generator不但可以使用for循环,还可以被next()函数不断调用并返回下一个值,直到返回StopIteration错误。

能够被next()函数不断调用并返回下一个值的对象称为迭代器Iterator。generator是Iterable,同时也是一个Iterator;而list、tuple、str、dict不是Iterator。当然,可以通过isinstance()判断一个对象是否为Iterator。

>>> from collections import Iterator
>>> isinstance((x*x for x in range(10)),Iterator)
True
>>> isinstance([],Iterator)
False
>>> isinstance({},Iterator)
False
>>> isinstance("ABC",Iterator)
False

如果想让list、tuple、str、dict等Iterable对象转变成Iterator,应该要怎么做呢?iter()函数能够让Iterable对象变成Iterator

>>> isinstance(iter("ABC"),Iterator)
True
>>> i = iter("ABC")
>>> next(i)
'A'
>>> next(i)
'B'
>>> next(i)
'C'

那么有人要问了,Iterable和Iterator对象到底有什么区别呢?

实际上,Python中Iterator对象表示的是一个数据流,Iterator对象可以被next()函数不断调用返回下一个值,直到数据全部被返回。Iterator对象数据流可以被看作是一个序列,但是这个序列的长度是不可提前预知的,因为只能通过next()或者for循环按照需要调用下一个数据,所以Iterator对象的计算是一种惰性计算

正式因为这样的特性,Iterator可以表示一个无限大的数据流,譬如全体自然数,而list是无法完成这样的任务的。

后记

python强大的功能有很多,只有时常运用才能使得它成为你优雅代码的一部分。不要只满足于使用简单功能,要多尝试才能多进步。

(By MrHammer 2016-05-19 下午4点 @Hohai Clody)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值