标签归档:python

Python 生成器用法

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

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

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

如果要一个一个打印出来,可以通过generator的next()方法:

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

我们讲过,generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next()方法实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

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

所以,我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它。

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, …

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print b
        a, b = b, a + b
        n = n + 1

上面的函数可以输出斐波那契数列的前N个数:

>>> fib(6)
1
1
2
3
5
8

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print b改为yield b就可以了:

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

这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

>>> fib(6)
<generator object fib at 0x104feaaa0>

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

举个简单的例子,定义一个generator,依次返回数字1,3,5:

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

可以看到,odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next()就报错。

同样的,把函数改成generator后,我们基本上从来不会用next()来调用它,而是直接使用for循环来迭代:

>>> for n in fib(6):
... print n
...
1
1
2
3
5
8

xrange 用法与 range 完全相同,所不同的是生成的不是一个list对象,而是一个生成器。所以xrange做循环的性能比range好,尤其是返回很大的时候

Python 列表生成式

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用range(1, 11):

>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但如果要生成[1×1, 2×2, 3×3, …, 10×10]怎么做?方法一是循环:

>>> L = []
>>> for x in range(1, 11):
... L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list:

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

写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来,十分有用,多写几次,很快就可以熟悉这种语法。

for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:

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

还可以使用两层循环,可以生成全排列:

>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

三层和三层以上的循环就很少用到了。

运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:

>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']

for循环其实可以同时使用两个甚至多个变量,比如dict的iteritems()可以同时迭代key和value:

>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> for k, v in d.iteritems():
... print k, '=', v
... 
y = B
x = A
z = C

因此,列表生成式也可以使用两个变量来生成list:

>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.iteritems()]
['y=B', 'x=A', 'z=C']

最后把一个list中所有的字符串变成小写:

>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']

python的可变参数和关键字参数(*args **kw)

1、可变参数


可变参数就是传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。
比如现在要实现给定一串数字a,b,c,计算出平方和。
要定义出这个函数,我们必须确定输入的参数。由于参数个数不确定,我们首先想到可以把a,b,c……作为一个list或tuple传进来,这样,函数可以定义如下:

def calc(numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

但是调用的时候,需要先组装出一个list或tuple:

>>> calc([1, 2, 3])
14
>>> calc((1, 3, 5, 7))
84

如果利用可变参数,调用函数的方式可以简化成这样:

>>> calc(1, 2, 3)
14
>>> calc(1, 3, 5, 7)
84

所以,我们把函数的参数改为可变参数:

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

如果已经有一个list或者tuple,把list或tuple的元素变成可变参数传进去。

>>> nums = [1, 2, 3]
>>> calc(*nums)
14

2、关键字参数


可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple,而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数:

>>> person('Michael', 30)
name: Michael age: 30 other: {}

也可以传入任意个数的关键字参数:

>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。

和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:

>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

3、命名关键字参数


命令关键字参数在Python3中支持,如果在Python2.7上运行会因为*号报错。
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,后面的参数被视为命名关键字参数。

调用方式如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:

>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given

由于调用时缺少参数名city和job,Python解释器把这4个参数均视为位置参数,但person()函数仅接受2个位置参数。
命名关键字参数可以有缺省值,从而简化调用:

def person(name, age, *, city='Beijing', job):
	print(name, age, city, job)

由于命名关键字参数city具有默认值,调用时,可不传入city参数:

>>> person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer

4、参数组合


在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用,除了可变参数无法和命名关键字参数混合。

但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数/命名关键字参数和关键字参数。

比如定义一个函数,包含上述若干种参数:

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。

>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

最神奇的是通过一个tuple和dict,你也可以调用上述函数:

>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = () kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

所以,对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的。

5、小结


Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。
默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
要注意定义可变参数和关键字参数的语法:
*args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict。
以及调用函数时如何传入可变参数和关键字参数的语法:
可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{‘a’: 1, ‘b’: 2})。
使用*args和**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
定义命名的关键字参数不要忘了写分隔符*,否则定义的将是位置参数。

参考文章:
http://www.jianshu.com/p/98f7e34845b5

【Python】模拟ssh登录执行命令

通过收集WAF日志获取到攻击IP,想调用机房出口FW封禁攻击IP,看了下python通过ssh登录juniper并封禁IP。
1)pexpect

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pexpect

def ssh_cmd(ip, passwd, cmd):
    ssh = pexpect.spawn('ssh root@%s "%s"' % (ip, cmd))
    try:
        i = ssh.expect(['password:', 'continue connecting (yes/no)?'], timeout=5)
        if i == 0 :
            ssh.sendline(passwd)
        elif i == 1:
            ssh.sendline('yes\n')
            ssh.expect('password: ')
            ssh.sendline(passwd)
        ssh.sendline(cmd)
        r = ssh.read()
        print r
    except pexpect.EOF:
        print "EOF"
        ssh.close()
    except pexpect.TIMEOUT:
        print "TIMEOUT"
        ssh.close()

ssh_cmd('10.59.0.248','******','ls')

2)paramiko

#-*- coding: utf-8 -*-
#!/usr/bin/python 
import paramiko

def ssh_cmd(ip,username,passwd,cmd):
    try:
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(ip,22,username,passwd,timeout=5)
        for m in cmd:
            stdin, stdout, stderr = ssh.exec_command(m)
#           stdin.write("Y")   #简单交互,输入 ‘Y’ 
            out = stdout.readlines()
            #屏幕输出
            for o in out:
                print o,
        print '%s\tOK\n'%(ip)
        ssh.close()
    except :
        print '%s\tError\n'%(ip)
if __name__=='__main__':
    cmd = ['whoami','echo hello!']#你要执行的命令列表
	ssh_cmd('10.59.0.248','******',cmd)

【Python】IPY包用法总结

通过version方法来区分出IPv4和IPv6
>>> import IPy
>>> IPy.IP(‘10.0.0.0/8’).version()
4
>>> IPy.IP(‘::1’).version()
6
通过指定的网段输出该网段的IP个数和所有的IP地址清单

如下:

$ more ip.py
#!/usr/bin/env python
import IPy

ip = IPy.IP(‘192.168.0.0/16’)
print ip.len()
for x in ip:
print x
反向解析名称、IP类型、IP转换等

>>> ip = IP(‘192.168.1.20’)
>>> ip.reverseNames() #反向解析地址格式
[‘20.1.168.192.in-addr.arpa.’]
>>> ip.iptype() #私网类型
‘PRIVATE’
>>> IP(‘8.8.8.8’).iptype() #公网类型
‘PUBLIC’
>>> IP(‘8.8.8.8’).int() #转换为整型格式
134744072
>>> IP(‘8.8.8.8’).strHex() #转换为十六进制格式
‘0x8080808’
>>> IP(‘8.8.8.8’).strBin() #转换成二进制格式
‘00001000000010000000100000001000’
>>> print IP(‘0x8080808’) #十六进制转换为IP格式
8.8.8.8
>>> print IP(134744072) #整型格式转换为IP格式
8.8.8.8
IP方法也支持网络地址的转换,例如根据IP和掩码产生网段格式

>>> print (IP(‘192.168.1.0’).make_net(‘255.255.255.0’))
192.168.1.0/24
>>> print (IP(‘192.168.1.0/255.255.255.0’,make_net=True))
192.168.1.0/24
>>> print (IP(‘192.168.1.0-192.168.1.255’,make_net=True))
192.168.1.0/24
通过strNormal方法指定不同wantprefixlen参数值以定制不同输出类型的网段,输出类型为字符串

>>> IP(‘192.168.1.0/24’).strNormal(0) #无返回
‘192.168.1.0’
>>> IP(‘192.168.1.0/24’).strNormal(1) #prefix格式
‘192.168.1.0/24’
>>> IP(‘192.168.1.0/24’).strNormal(2) #decimalnetmask格式
‘192.168.1.0/255.255.255.0’
>>> IP(‘192.168.1.0/24’).strNormal(3) #lastIP格式
‘192.168.1.0-192.168.1.255’
多网络计算方法详解
比较两个网段是否存在包含、重叠等关系,比如同网络但不同prefixlen会认为是不相等的网段,如10.0.0.0/16不等于10.0.0.0/24,另外即使具有相同的prefixlen但处于不同的网络地址,同样也视为不相等,如10.0.0.0/16不等于192.0.0.0/16。IPy支持类似于数值型数据的比较,以帮助IP对象进行比较。

比较IP大小

>>> IP(‘10.0.0.0/24’) < IP(‘12.0.0.0/24’)
True
判断IP地址和网段是否包含于另一个网段中

>>> ‘192.168.1.100’ in IP(‘192.168.1.0/24’)
True
>>> IP(‘192.168.1.0/24’) in IP(‘192.168.0.0/16’)
True
判断两个网段是否存在重叠(overlaps方法)

>>> IP(‘192.168.0.0/23’).overlaps(‘192.168.1.0/24’)
1
>>> IP(‘192.168.1.0/24’).overlaps(‘192.168.2.0/24’)
0

python nmap库用法

# -*- coding: utf8 -*-
from time import strftime, localtime
from datetime import timedelta, date
import time
import json
import datetime
import sqlite3
import os
import nmap
import sys
reload(sys)
sys.setdefaultencoding( "utf-8" )
def nmapscan(ip):
    nm = nmap.PortScanner()
    nm.scan(hosts=ip,ports='80,3306,9200')
    print nm.scaninfo()
    print nm.command_line() 
    for host in nm.all_hosts():
        for proto in nm[host].all_protocols():
            lport = nm[host][proto].keys()
            lport.sort()
            count_port = len(lport)
            i = 0
            while i < count_port:
                in_port = lport[i]
                if nm[host][proto][in_port]['state'] == 'open':
                    print host + ":" + str(in_port) + " => " + nm[host][proto][in_port]['name']
                i+=1
if __name__ == '__main__':
    start = time.time()
    nmapscan('10.59.0.116')
    end = time.time()
    print "程序执行时间:" + str(int(end - start)) + "s"

执行结果为:
[root@server120 tmp]# python thread.py
{‘tcp’: {‘services’: ‘80,3306,9200’, ‘method’: ‘syn’}}
nmap -oX – -p 80,3306,9200 -sV 10.59.0.116
10.59.0.116:80 => http
10.59.0.116:9200 => wap-wsp
程序执行时间:90s

默认的执行参数如下:
nmap -oX – -p 80,3306,9200 -sV 10.59.0.116
使用-sV会尝试探测端口的服务类型/具体版本等信息,速度慢一些,可以尝试修改nmap参数
nm.scan(hosts=ip,arguments=’-sS -p 80,3306,9200′)
执行结果:
[root@server120 tmp]# python thread.py
{‘tcp’: {‘services’: ‘80,3306,9200’, ‘method’: ‘syn’}}
nmap -oX – -sS -p 80,3306,9200 10.59.0.116
10.59.0.116:80 => http
10.59.0.116:9200 => wap-wsp
程序执行时间:5s
可以看出执行时间缩短了很多

Python使用socket多线程扫描端口

# -*- coding: utf8 -*-
import socket
import sys
import threading
import time
import Queue
TIMEOUT = 2
reload(sys)
sys.setdefaultencoding( "utf-8" )
def scan(ip, port, timeout=TIMEOUT):
    try:
        print "Scan " + ip + "  " + str(port)
        cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        cs.settimeout(float(timeout))
        address = (str(ip), int(port))
        status = cs.connect_ex((address))
        if status == 0:
            print "%s ==> %d" % (ip,port)
    except Exception, e:
        print "error:%s" % e
    finally:
        cs.close()
class Scan(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
    def run(self):
        while True:
            try:
                if queue.empty(): break
                queue_task = self.queue.get()
                ip = queue_task.split(':')[0]
                port = int(queue_task.split(':')[1])
                scan(ip,port)
            except:
                break
def get_ip_list(ip):
    ip_list = []
    iptonum = lambda x:sum([256**j*int(i) for j,i in enumerate(x.split('.')[::-1])])
    numtoip = lambda x: '.'.join([str(x/(256**i)%256) for i in range(3,-1,-1)])
    if '-' in ip:
        ip_range = ip.split('-')
        ip_start = long(iptonum(ip_range[0]))
        ip_end = long(iptonum(ip_range[1]))
        ip_count = ip_end - ip_start
        if ip_count >= 0 and ip_count <= 65536:
            for ip_num in range(ip_start,ip_end+1):
                ip_list.append(numtoip(ip_num))
        else:
            print 'ERROR'
    else:
        ip_split = ip.split('.')
        net = len(ip_split)
        if net == 2:
            for b in range(1, 255):
                for c in range(1, 255):
                    ip = "%s.%s.%d.%d" % (ip_split[0], ip_split[1], b, c)
                    ip_list.append(ip)
        elif net == 3:
            for c in range(1, 255):
                ip = "%s.%s.%s.%d" % (ip_split[0], ip_split[1], ip_split[2], c)
                ip_list.append(ip)
        elif net == 4:
            ip_list.append(ip)
        else:
            print "ERROR"
    return ip_list
if __name__ == '__main__':
    queue = Queue.Queue()
    port = ['80','9200','443','3306']
    ip = '192.168.190.1-192.168.190.50'
    for ipaddress in get_ip_list(ip):
        for p in port:
            queue.put(ipaddress+":"+p)
    threads = []
    num = 10 #设置线程数
    for x in xrange(0, num):
        t_scan = Scan(queue)
        threads.append(t_scan)
    for t in threads:
        t.start()
    for t in threads:
        t.join()

Python多线程中join与setDaemon

先看一段程序:


import threading,time
from time import sleep, ctime
def now() :
return str( time.strftime( '%Y-%m-%d %H:%M:%S' , time.localtime() ) )
def test(nloop, nsec):
print 'start loop', nloop, 'at:', now()
sleep(nsec)
print 'loop', nloop, 'done at:', now()
def main():
print 'starting at:',now()
threadpool=[]
for i in xrange(3):
th = threading.Thread(target= test,args= (i,2))
threadpool.append(th)
#th.setDaemon(True)
for th in threadpool:
th.start()
print 'all Done at:', now()
if __name__ == '__main__':
main()

如果不加join和setDaemon的话,输出如下:

123

如果添加join(),输出如下:
for th in threadpool :
threading.Thread.join( th )

123

可以看到主线程是最后才结束的,join() 的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
然后我们添加一下setDeamon
for i in xrange(3):
th = threading.Thread(target= test,args= (i,2))
threadpool.append(th)
th.setDaemon(True)

123

可以看到主线程结束之后,子线程也会结束。
我们可以添加锁试验一下:

import threading,time
from time import sleep, ctime
def now() :
return str( time.strftime( '%Y-%m-%d %H:%M:%S' , time.localtime() ) )
def test(nloop, nsec):
mutex.acquire()
print 'start loop', nloop, 'at:', now()
sleep(nsec)
print 'loop', nloop, 'done at:', now()
mutex.release()
def main():
print 'starting at:',now()
threadpool=[]
for i in xrange(3):
th = threading.Thread(target= test,args= (i,2))
threadpool.append(th)
#th.setDaemon(True)
for th in threadpool:
th.start()
for th in threadpool :
threading.Thread.join( th )
print 'all Done at:', now()
if __name__ == '__main__':
mutex = threading.Lock()
main()


输出如下:

123

Python threading类用法

Python的多线程是鸡肋,只能在单核上跑,在任意时间只有一个Python解释器在解释Python bytecode。但是Python的多线程也并非一无是处。
如果你的代码是CPU密集型,多个线程的代码很有可能是线性执行的。所以这种情况下多线程是鸡肋,效率可能还不如单线程因为有context switch。
如果你的代码是IO密集型,多线程可以明显提高效率。例如爬虫,因为绝大多数时间爬虫是在等待socket返回数据。
CPU密集型的代码可以选择使用multiprocessing库。

threading模块里面主要是对一些线程的操作对象化,创建Thread的class。一般来说,使用线程有两种模式:
A 创建线程要执行的函数,把这个函数传递进Thread对象里,让它来执行。
B 继承Thread类,创建一个新的class,将要执行的代码 写到run函数里面。

第一种 创建线程要执行的函数,把这个函数传递进Thread对象里,让它来执行。

import threading,time
from time import sleep, ctime
def now() :
    return str( time.strftime( '%Y-%m-%d %H:%M:%S' , time.localtime() ) )

def test(nloop, nsec):
    print 'start loop', nloop, 'at:', now()
    sleep(nsec)
    print 'loop', nloop, 'done at:', now()

def main():
    print 'starting at:',now()
    threadpool=[]

    for i in xrange(3):
        th = threading.Thread(target= test,args= (i,2))
        threadpool.append(th)

    for th in threadpool:
        th.start()

    for th in threadpool :
        threading.Thread.join( th )

    print 'all Done at:', now()

if __name__ == '__main__':
        main()

第二种:继承Thread类,创建一个新的class,将要执行的代码 写到run函数里面。

import threading ,time
from time import sleep, ctime
def now() :
    return str( time.strftime( '%Y-%m-%d %H:%M:%S' , time.localtime() ) )

class myThread (threading.Thread) :
      """docstring for myThread"""
      def __init__(self, nloop, nsec) :
          super(myThread, self).__init__()
          self.nloop = nloop
          self.nsec = nsec

      def run(self):
          print 'start loop', self.nloop, 'at:', ctime()
          sleep(self.nsec)
          print 'loop', self.nloop, 'done at:', ctime()
def main():
     thpool=[]
     print 'starting at:',now()
    
     for i in xrange(10):
         thpool.append(myThread(i,2))
       
     for th in thpool:
         th.start()
   
     for th in thpool:
         th.join()
    
     print 'all Done at:', now()

if __name__ == '__main__':
        main()