防僞碼:忘情公子著
什麼是清單解析?
清單解析就是根據已有清單,高效生成新清單的方式
清單解析是python疊代機制的一種應用,它常用于實作建立新的清單,是以要放置于[]中
文法:
[expression for iter_var in iterable]
[expression for iter_var in iterable if cond_expr]
例:
In [1]: L = [i**2 for i in xrange(9)]
In [2]: print L
[0, 1, 4, 9, 16, 25, 36, 49, 64]
假設現在有一個清單list1,需要取得清單list1中每一個元素的平方,并生成一個新清單,可以這樣做:
In [3]: list1 = [1,2,3,4,5,6,7,8]
In [4]: list2 = [i**2 for i in list1]
In [5]: print list2
[1, 4, 9, 16, 25, 36, 49, 64]
隻把list1中元素值大于或等于3的計算機其平方,并生成新清單,可以這樣做:
In [6]: list3 = [i**2 for i in list1 if i >= 3]
In [7]: print list3
[9, 16, 25, 36, 49, 64]
清單解析能夠使用比for循環快近一倍的方式基于已有的清單來生成新清單
求1到10範圍内10個數字的平方除以2的結果:
In [8]: for i in [i**2 for i in xrange(1,11)]:
...: print i / 2
...:
0
2
4
8
12
18
24
32
40
50
傳回1到10範圍内所有偶數的平方除以2的結果:
In [9]: for i in [i**2 for i in xrange(1,11) if i %2 == 0]:
...: print i / 2
...:
2
8
18
32
50
把/root目錄下所有以.log結尾的檔案取出來,生成一個新清單:
In [10]: import os
In [11]: filelist = [i for i in os.listdir("/root") if i.endswith(".log")]
In [12]: print filelist
['install.log']
把/etc目錄下所有目錄取出來,生成一個新清單:
In [13]: directory1 = [i for i in os.listdir("/etc") if os.path.isdir("/etc/"+i)]
In [14]: print directory1
['pkcs11', 'sasl2', 'xml', 'pear', 'terminfo', 'iscsi', 'sysconfig', 'security', 'statetab.d', 'NetworkManager', '.java', 'rc4.d', 'gcrypt', 'xinetd.d', 'ssh', 'httpd', 'dhcp', 'php.d', 'rc6.d', 'rpm', 'cron.daily', 'audit', 'selinux', 'depmod.d', 'dracut.conf.d', 'ntp', 'xdg', 'init', 'init.d', 'ConsoleKit', 'pm', 'yum', 'openldap', 'sudoers.d', 'logrotate.d', 'lxc', 'ppp', 'ld.so.conf.d', 'rwtab.d', 'dbus-1', 'gnupg', 'cron.weekly', 'sgml', 'pki', 'rc2.d', 'modprobe.d', 'audisp', 'exim', 'blkid', 'profile.d', 'cron.hourly', 'default', 'opt', 'rc0.d', 'ssl', 'fonts', 'sysctl.d', 'iproute2', 'X11', 'rc1.d', 'plymouth', 'rc.d', 'yum.repos.d', 'polkit-1', 'cron.monthly', 'pango', 'pam.d', 'rsyslog.d', 'prelink.conf.d', 'alternatives', 'rc3.d', 'makedev.d', 'popt.d', 'docker', 'udev', 'gconf', 'lvm', 'cgconfig.d', 'cron.d', 'chkconfig.d', 'bash_completion.d', 'skel', 'multipath', 'rc5.d']
在使用清單解析時,for循環還可以嵌套for循環。舉例說明:
假設現在有兩個清單list1和list2,現在要實作兩個清單的元素交叉相乘,可以這樣做:
In [15]: list1 = ['x','y','z']
In [16]: list2 = [1,2,3]
In [17]: list3 = [(i,j) for i in list1 for j in list2]
In [18]: print list3
[('x', 1), ('x', 2), ('x', 3), ('y', 1), ('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
接下來實作當list2中元素值不為1時與list1交叉相乘,可以這樣做:
In [19]: list4 = [(i,j) for i in list1 for j in list2 if j != 1]
In [20]: print list4
[('x', 2), ('x', 3), ('y', 2), ('y', 3), ('z', 2), ('z', 3)]
清單解析會直接傳回一個新清單,如果某個原清單裡面的元素非常多,而清單解析又以幾何倍數向上增長以後,就會極其占用記憶體,如此将導緻效率低下。
但是清單生成以後,一般一次隻取一個元素,而for循環一次隻周遊疊代其中的一個元素,基于此,使用直接生成一個清單的方式如此占用記憶體是極其不好的。
由此我們可以對清單解析作一個擴充,就是把[]變成(),這就叫做生成器
生成器和清單解析的關系就相當于xrange和range的關系,它可以讓我們使用一個表達式,一次隻生成計算出一個值,叫惰性計算方式。
生成器不像清單解析一樣,不管你用不用直接就生成了。生成器僅僅是你調用一次,它就可以傳回一個,再調用一次,再傳回一個。
生成器表達式并不真正建立數字清單,而是傳回一個生成器對象,此對象在每次計算出一個條目後,把這個條目“産生”(yield)出來.生成器表達式使用了“惰性計算”或稱作“延遲求值”的機制
當序列過長,并且每次隻需要擷取一個元素時,應當考慮使用生成器表達式而不是清單解析
生成器特點:
1、隻有在調用時才會生成相對應的資料
2、隻記錄目前位置,無法向前回溯,隻能一步步往後走
3、可以通過__next__()方法一次取一個值
4、可以通過for循環取得所有值
生成器表達式文法:
(expr for iter_var in iterable)
(expr for iter_var in iterable if cond_expr)
生成器生成方法:
1、通過清單解析進行生成
[ i*2 for i in range(10) ]
2、通過函數生成(這裡用著名的裴波那契數列來舉例)
def fib(max):
count,a,b = 0,0,1
while count < max:
yield b
a,b = b,a+b
count += 1
return 'done'
生成器的return傳回值包含在異常當中,為避免程式抛出異常,僅通過捕捉異常的方式才能取得return傳回值。
yield儲存了函數的中斷狀态。
生成器是什麼情況下會用到呢?這裡通過一個例子來說明一下:
生成器可以實作在單線程(串行)的情況下實作并發運算的效果
生成器的__next__()方法隻調用生成器的狀态而不傳值;
生成器的send()方法在調用生成器的狀态的同時給生成器傳一個值
import time
def cut(cutter):
print('%s 已經準備好撕紙了。'% cutter)
num = 1
while True:
paper = yield
print('第%s張%s剛做好了,已經被%s撕了。'% (num,paper,cutter))
num += 1
def make(maker):
cutter1 = cut('撕紙員1')
cutter2 = cut('撕紙員2')
cutter1.__next__()
cutter2.__next__()
print('%s已經準備好做紙了。'% maker)
for i in range(1,11):
time.sleep(1)
print('%s已經做好了1張紙'% maker)
cutter1.send(i)
cutter2.send(i)
make('Tom')
傳回1到10範圍内所有正整數的平方:
In [21]: g1 = (i**2 for i in range(1,11))
In [22]: g1.next()
Out[22]: 1
In [23]: g1.next()
Out[23]: 4
In [24]: g1.next()
Out[24]: 9
In [25]: g1.next()
Out[25]: 16
In [26]: g1.next()
Out[26]: 25
In [27]: g1.next()
Out[27]: 36
In [28]: g1.next()
Out[28]: 49
In [29]: g1.next()
Out[29]: 64
In [30]: g1.next()
Out[30]: 81
In [31]: g1.next()
Out[31]: 100
In [32]: g1.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-32-9066a8f18086> in <module>()
----> 1 g1.next()
StopIteration:
傳回1到10範圍内所有正整數的平方除以2的結果:
In [33]: for i in (i**2 for i in range(1,11)):
...: print i / 2
...:
0
2
4
8
12
18
24
32
40
50
In [35]: url = 'www.python.org'
In [36]: g2 = enumerate(url)
In [37]: g2.next()
Out[37]: (0, 'w')
In [38]: g2.next()
Out[38]: (1, 'w')
In [39]: g2.next()
Out[39]: (2, 'w')
In [40]: g2.next()
Out[40]: (3, '.')
In [41]: g2.next()
Out[41]: (4, 'p')
In [42]: g2.next()
Out[42]: (5, 'y')
In [43]: g2.next()
Out[43]: (6, 't')
In [44]: g2.next()
Out[44]: (7, 'h')
In [45]: g2.next()
Out[45]: (8, 'o')
In [46]: g2.next()
Out[46]: (9, 'n')
In [47]: g2.next()
Out[47]: (10, '.')
In [48]: g2.next()
Out[48]: (11, 'o')
In [49]: g2.next()
Out[49]: (12, 'r')
In [50]: g2.next()
Out[50]: (13, 'g')
In [51]: g2.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-51-6d69cefe8ba3> in <module>()
----> 1 g2.next()
StopIteration: