zoukankan      html  css  js  c++  java
  • Python:基础知识(二)


    常用模块

    urllib2 :用于发送网络请求,获取数据

    (Pyhton2中的urllib2工具包,在Python3中分拆成了urllib.request和urllib.error两个包。)

    json:用于解析获得的数据 

    模块安装

    在cmd下执行:python -m  pip install 模块名

     

    pip升级

    升级命令: python -m pip install --upgrade pip


     urllib2 :用于发送网络请求,获取数据

    import urllib2

    web = urllib2.urlopen('http://www.baidu.com')

    content = web.read()

    print content


     json:解析json数据 

     如:

    {

       "weatherinfo": {

           "city": "南京",

           "cityid": "101190101",

           "temp1": "37℃",

           "temp2": "28℃",

           "weather": "多云",

           "img1": "d1.gif",

           "img2": "n1.gif",

           "ptime": "11:00"

       }

    }

    可以看出,它像是一个字典的结构,但是有两层。最外层只有一个key--“weatherinfo”,它的value是另一个字典,里面包含了好几项天气信息。

    虽然看上去像字典,但它对于程序来说,仍然是一个字符串,只不过是一个满足json格式的字符串。用python中json模块提供的loads方法,把它转成一个真正的字典。

    import json

    data = json.loads(content)

    data已经是一个字典,在控制台中输出它,看上去和content没什么区别,只是编码上有些不同:

    {u'weatherinfo': {u'city': u'u5357u4eac', u'ptime': u'11:00', u'cityid': u'101190101', u'temp2': u'28u2103', u'temp1': u'37u2103', u'weather': u'u591au4e91', u'img2': u'n1.gif', u'img1': u'd1.gif'}}


    面向对象

    类的属性:域(变量,包括类变量和属性变量)和方法

    python是一种高度面向对象的语言,它其中的所有东西其实都是对象。比如,字符串即是一个对象

    dir()方法可以查看一个类/变量的所有属性:s='a' ;dir(s);

    关键字class用于创建一个类。pass语句表示一个空的代码块。类名()表示创建对象。

    class MyClass:  

        pass

    mc = MyClass()

    print mc

    调用类变量的方法是“对象.变量名”。

    注意:类方法和函数的区别在于,类方法第一个参数必须为self。而在调用类方法的时候,通过“对象.方法名()”格式进行调用,而不需要额外提供self这个参数的值。self在类方法中的值,就是你调用的这个对象本身。

    class MyClass:

        name = 'Sam'

        def sayHi(self):

            print 'Hello %s' % self.name

    mc = MyClass()

    print mc.name

    mc.name = 'Lily'

    mc.sayHi()


    类的继承

    class Vehicle:  ----------基类

        def __init__(self, speed): -----__init__是python内置方法(函数名前后有__),在类创建时会自动调用以初始化类,其参数要在创建类时提供。

            self.speed = speed

        def drive(self, distance):

            print 'need %f hour(s)' % (distance / self.speed)

    class Bike(Vehicle):-----------类名后的括号内参数代表父类(继承关系)

        pass  ------不需要有额外的功能

    class Car(Vehicle): 

        def __init__(self, speed, fuel):-----------扩展自己的初始化函数

            Vehicle.__init__(self, speed)----------首先调用父类的初始化方法,注意此处要加上self

            self.fuel = fuel

        def drive(self, distance):

            Vehicle.drive(self, distance)

            print 'need %f fuels' % (distance * self.fuel)

    b = Bike(15.0)--------初始化类时的参数是__init__的参数

    c = Car(80.0, 0.012)

    b.drive(100.0)

    c.drive(100.0)


     and-or 技巧

    看下面这段代码:

    a = "heaven"

    b = "hell"

    c = True and a or b

    print c

    d = False and a or b

    print d

    输出:

    heaven

    hell

    bool and a or b语句中,当bool条件为真时,结果是a;当bool条件为假时,结果是b。这和c/c++等语言的bool?a:b表达式很像。

    因此,一个if-else语句表述的逻辑:

    if a > 0:

    print "big"

    else:

    print "small"

    就可以直接写成:print (a > 0) and "big" or "small"

    但是,和c语言中的?:表达式不同,这里的and or语句是利用了python中的逻辑运算实现的。当a本身是个假值(如0,"")时,结果就不同了:

    a = ""

    b = "hell"

    c = True and a or b

    print c

    得到的结果不是""而是"hell"。因为""和"hell"做or的结果是"hell"。

    所以,and-or真正的技巧在于,确保a的值不会为假。最常用的方式是使 a 成为 [a] 、 b 成为 [b],然后使用返回值列表的第一个元素

    a = ""

    b = "hell"

    c = (True and [a] or [b])[0]

    print c

    由于[a]是一个非空列表,所以它决不会为假。即使a是0或者''或者其它假值,列表[a]也为真,因为它有一个元素。

    在两个常量值进行选择时,and-or会让你的代码更简单。常用于lambda表达式。


    python自带数学运算模块 math 

     import math

    math包里有两个常量:

    1)math.pi  圆周率π:3.141592...

    2)math.e  自然常数:2.718281...

    数值运算:

    math.ceil(x)  对x向上取整,比如x=1.2,返回2.0(py3返回2)

    math.floor(x)  对x向下取整,比如x=1.2,返回1.0(py3返回1)

    math.pow(x,y)  指数运算,得到x的y次方

    math.log(x)  对数,默认基底为e。可以使用第二个参数,来改变对数的基底。比如math.log(100, 10)

    math.sqrt(x)  平方根

    math.fabs(x)  绝对值

    三角函数: 

    math.sin(x)

    math.cos(x)

    math.tan(x)

    math.asin(x)

    math.acos(x)

    math.atan(x)

    注意:这里的x是以弧度为单位,所以计算角度的话,需要先换算

    角度和弧度互换: 

    math.degrees(x)   弧度转角度

    math.radians(x)  角度转弧度


      正则表达式

    正则表达式就是记录文本规则的代码。python中的正则表达式库,所做的事情是利用正则表达式来搜索文本。或者用来判断输入的文本是否符合规范,或进行分类

    示例:

    import re

    text = "Hi, I am Shirley Hilton. I am his wife."

    m = re.findall(r"hi", text)

    if m:

        print m

    else:

        print 'not match'

    re模块

    re是python里的正则表达式模块。findall是其中一个方法,用来按照提供的正则表达式,去匹配文本中的所有符合条件的字符串。返回结果是一个包含所有匹配的list。

    规则: 

    

    表示单词的开头或结尾,空格、标点、换行都算是单词的分割。“”自身又不会匹配任何字符,它代表的只是一个位置。所以单词前后的空格标点之类不会出现在结果里。

    B

    B - 匹配不是单词开头或结束的位置

    []

    []表示满足括号中任一字符,比如“[hi]”,它就不是匹配“hi”了,而是匹配“h”或者“i”。如果把正则表达式改为“[Hh]i”,就可以既匹配“Hi”,又匹配“hi”了。

    r  

    r,是raw的意思,它表示对字符串不进行转义。\也表示不转义,但是如果有很多个\会显得比较乱,这样直接在字符串前加个r就能解决。

    >>> print "hi"

    hi

    >>> print r"hi"

    hi

    >>> print "\bhi" 

    hi

    .

    “.”在正则表达式中表示除换行符以外的任意字符

    S

    “S”表示任意非空白字符。注意是大写字符S。

    s

    s - 匹配任意的空白符,小写s,与大写S相反

     1)、^匹配字符串的开始位置。^1d*x?表示以1开头的数字,结尾可能有x

     2)、^与[]合用,表示非。[a]的反义是[^a],表示除a以外的任意字符。[^abcd]就是除abcd以外的任意字符。

    $

    $ - 匹配字符串的结束

    ?

    “?”表示任意一个字符

    *

    “*”表示其前面字符重复任意次数(0个或多个)。“*”不是表示字符,而是表示数量:它表示前面的字符可以重复任意多次(包括0次),只要满足这样的条件,都会被表达式匹配上。

    +

    与*类似,+表示1个或多个

    表示重复0次或1次

    {n,}

     重复n次或更多次

    {n,m}

    重复n到m次

    d

    d表示数字,即等价于【0-9】。表示任意长度的数字,可以用[0-9]*或者d*。

    D

    D - 匹配任意非数字的字符

    {}  {数字}

    比如11位的数字:d{11}

    w

    w - 匹配字母或数字或下划线或汉字(3.x版本可以匹配汉字,但2.x版本不可以)

     W

    W - 匹配任意不是字母,数字,下划线,汉字的字符

    “|”

    “|”相当于python中“or”的作用,它连接的两个表达式,只要满足其中之一,就会被算作匹配成功。使用“|”时,要特别提醒注意的是不同条件之间的顺序。匹配时,会按照从左往右的顺序,一旦匹配成功就停止验证后面的规则。

    正则表达式不只是用来从一大段文字中抓取信息,很多时候也被用来判断输入的文本是否符合规范,或进行分类。例子:

    ^w{4,12}$

    这个表示一段4到12位的字符,包括字母或数字或下划线或汉字,可以用来作为用户注册时检测用户名的规则。(但汉字在python2.x里面可能会有问题)

    d{15,18}

    表示15到18位的数字,可以用来检测身份证号码

    ^1d*x?

    以1开头的一串数字,数字结尾有字母x,也可以没有。有的话就带上x。

     转义字符

    如果我们确实要匹配.或者*字符本身,而不是要它们所代表的元字符,那就需要用.或*。本身也需要用\。

    比如"d+.d+"可以匹配出123.456这样的结果。


     random模块

    from random import randint

    randint(1, 10)

    随机数

     random模块的作用是产生随机数。

    random.randint(a, b)可以生成一个a到b间的随机整数,包括a和b。

    a、b都必须是整数,且必须b≥a。当等于的时候,比如:random.randint(3, 3)的结果就永远是3

    random.random()生成一个0到1之间的随机浮点数,包括0但不包括1,也就是[0.0, 1.0)。

    random.uniform(a, b)生成a、b之间的随机浮点数。不过与randint不同的是,a、b无需是整数,也不用考虑大小。

    random.uniform(1.5, 3),random.uniform(3, 1.5),这两种参数都是可行的。random.uniform(1.5, 1.5)永远得到1.5。

    random.choice(seq)从序列中随机选取一个元素。seq需要是一个序列,比如list、元组、字符串。

    random.choice([1, 2, 3, 5, 8, 13]) #list

    random.choice('hello') #字符串

    random.choice(['hello', 'world']) #字符串组成的list

    random.choice((1, 2, 3)) #元组

    random.randrange(start, stop, step)生成一个从start到stop(不包括stop),间隔为step的一个随机数。start、stop、step都要为整数,且start<stop。start和step都可以不提供参数,默认是从0开始,间隔为1。但如果需要指定step,则必须指定start。

    比如:

    random.randrange(1, 9, 2)

    就是从[1, 3, 5, 7]中随机选取一个。

    start和step都可以不提供参数,默认是从0开始,间隔为1。但如果需要指定step,则必须指定start。

    random.randrange(4) #[0, 1, 2, 3]

    random.randrange(1, 4) #[1, 2, 3]

    random.randrange(start, stop, step)其实在效果上等同于random.choice(range(start, stop, step))

    random.sample(population, k)从population序列中,随机获取k个元素,生成一个新序列。sample不改变原来序列。

    random.shuffle(x)把序列x中的元素顺序打乱。shuffle直接改变原有的序列。

    random.seed(x) 设置生成随机数用的整数起始值。调用任何其他random模块函数之前调用这个函数。

    参数x 是下一个随机数的种子。如果省略,则需要系统时间,以产生下一个随机数。


     time模块提供了一些与时间相关的方法。利用time,可以简单地计算出程序运行的时间。对于一些比较复杂、耗时较多的程序,可以通过这种方法了解程序中哪里是效率的瓶颈,从而有针对性地进行优化。

    计时

     在计算机领域有一个特殊的时间,叫做epoch,它表示的时间是1970-01-01 00:00:00 UTC。

    time模块的一个方法time.time(),返回的就是从epoch到当前的秒数(不考虑闰秒)。这个值被称为unix时间戳。

    用这个方法得到程序开始和结束所用的时间,进而算出运行的时间:

    import time  

    starttime = time.time()

    print 'start:%f' % starttime

    for i in range(10):

        print i

    endtime = time.time()  

    print 'end:%f' % endtime

    print 'total time:%f' % (endtime-starttime) 

    time.sleep(secs),可以让程序暂停secs秒。

    在抓取网页的时候,适当让程序sleep一下,可以减少短时间内的请求,提高请求的成功率。


    Python Shell

    一般来说,有两种运行 python 代码的方法:

    1. 使用交互式的带提示符的解释器

    2. 使用源文件

    第一种方法,所谓“交互式的带提示符的解释器”,也被称做 python shell。当你安装好 python,并正确配置系统变量 PATH 后(linux 和 mac 上通常都预装并配置好了 python),在命令行里输入 python,会看到诸如以下的提示:

    $ python

    Python 2.7.5 (default, Aug 25 2013, 00:04:04)

    [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin

    Type "help", "copyright", "credits" or "license" for more information.

    >>>

    python shell 里写好的代码也很难保存(至少我目前还不知道有什么可行的方法)。所以一般并不会用它来“真正地”写代码。当你需要写一个相对完整的 python 程序时,你需要写在一个“源文件”中。这就是运行 python 的第二种方法

    用一个文本编辑器新建一个文件,在里面输入:print "hello world"

    保存这个文件为 hello.py。注意,不要命名为 print.py。不要以任何 python 的内置方法或者你会使用到的模块名来命名你自己的代码文件。

    然后在命令行中,进入到这个文件所在的文件夹,输入 python hello.py。你会看到:

    $python hello.py

    hello world

    $

    这时候不会进入 python shell,而是直接输出了程序的结果。换句话说,python 执行了写在源文件 hello.py 中的代码。

    python 自带了一个叫做 IDLE 的编辑器。如果要编辑源文件,则需要在菜单栏中选择 File -> New File。这时打开的新窗口就是源文件窗口。在里面写好你的 python 代码后,点击菜单栏上的 Run -> Run Module,即可执行


    对象的序列化与反序列化 :pickle模块

    pickle可以把任何 Python 对象存储在文件中,再把它原样取出来。

    例:

    import pickle

    test_data = ['Save me!', 123.456, True]

    f = file('test.data', 'w')

    pickle.dump(test_data, f)

    f.close()

    这样就把 test_data 这个 list 存储在了文件 test.data 中。你可以用文本编辑器打开 test.data 查看里面的内容:

    (lp0

    S'Save me!'

    p1

    aF123.456

    aI01

    a.

    取对象:

    import pickle 

    f = file('test.data')

    test_data = pickle.load(f)

    f.close()

    print test_data

    控制台的输出:['Save me!', 123.456, True],和存储前的数据是一致的。

    如果要保存多个对象一种方法是把这些对象先全部放在一个序列中,在对这个序列进行存储

    a = 123

    b = "hello"

    c = 0.618

    data = (a, b, c)

    ...

    pickle.dump(data, f)

    另一种方法就是依次保存和提取:

    ...

    pickle.dump(a, f)

    pickle.dump(b, f)

    pickle.dump(c, f)

    ...

    x = pickle.load(f)

    y = pickle.load(f)

    z = pickle.load(f)

    dump 方法可以增加一个可选的参数,来指定用二进制来存储:pickle.dump(data, f, True)

    而 load 方法会自动检测数据是二进制还是文本格式,无需手动指定。

    【特别说明】python3中,通过pickle对数据进行存储时,必须用二进制(b)模式读写文件。

    Python 还提供了另一个模块 cPickle,它的功能及用法和 pickle 模块完全相同,只不过它是用C语言编写的,因此要快得多(比pickle快1000倍)。


    列表表达式List Comprehension

    所谓列表表达式(也有翻译成列表解析列表综合),就是通过一个已有的列表生成一个新的列表。例:

    假设有一个由数字组成的 list,现在需要把其中的偶数项取出来,组成一个新的 list。一种比较“正常”的方法是:

    list_1 = [1, 2, 3, 5, 8, 13, 22]

    list_2 = []

    for i in list_1:

      if i % 2 == 0:

        list_2.append(i)

    print list_2

    输出:[2, 8, 22]

    使用列表解析实现同样的效果:

    list_1 = [1, 2, 3, 5, 8, 13, 22]

    list_2 = [i for i in list_1 if i % 2 == 0]

    print list_2

    输出:[2, 8, 22]

    [i for i in list_1] 会把 list_1 中的每一个元素都取出来,构成一个新的列表。

    如果需要对其中的元素进行筛选,就在后面加上判断条件 if。所以 [i for i in list_1 if i % 2 == 0] 就是把 list_1 中满足 i % 2 == 0 的元素取出来组成新列表。

    在构建新列表时,还可以对取出的元素做操作。比如,对于原列表中的偶数项,取出后要除以2,则可以通过 [i / 2 for i in list_1 if i % 2 == 0] 来实现。输出为 [1, 4, 11]。


    lambda 表达式

     lambda 表达可以被看做是一种匿名函数。可以快速定义一个极度简单的单行函数。

    lambda 表达式的语法格式lambda 参数列表: 表达式

    定义 lambda 表达式时,参数列表周围没有括号,返回值前没有 return 关键字,也没有函数名称。

    它的写法比 def 更加简洁。但是,它的主体只能是一个表达式,不可以是代码块,甚至不能是命令(print 不能用在 lambda 表达式中)。所以 lambda 表达式能表达的逻辑很有限。

    lambda 表达式创建了一个函数对象,可以把这个对象赋值给一个变量进行调用。

    譬如这样一个实现三个数相加的函数:

    def sum(a, b, c):

      return a + b + c

    调用: 

    print sum(1, 2, 3)

    print sum(4, 5, 6)

    如果使用 lambda 表达式来实现:

    sum = lambda a, b, c: a + b + c

    调用:

    print sum(1, 2, 3)

    print sum(4, 5, 6)

    看一个复杂一点的例子,把 lambda 表达式用在 def 函数定义中

    def fn(x):

      return lambda y: x + y

    调用:

    a = fn(2)

    print a(3)

    输出:

    5

    fn 函数的返回值是一个 lambda 表达式,也就等于是一个函数对象。当以参数2来调用 fn 时,得到的结果就是:

    lambda y: 2 + y

    a = fn(2) 就相当于:a = lambda y: 2 + y

    所以 a(3) 的结果就是5。

    任何可以使用 lambda 表达式的地方,都可以通过普通的 def 函数定义来替代。在一些需要重复使用同一函数的地方,def 可以避免重复定义函数。而对于像 filter、sort 这种需要内嵌函数的方法,lambda 表达式就会显得比较合适。


    变量的作用域

    当函数内部定义了一个变量,无论是作为函数的形参,或是另外定义的变量,它都只在这个函数的内部起作用,这样在函数内部定义的变量被称为“局部变量”。这个函数体就是这个变量的作用域。函数外即使有和它名称相同的变量,也没有什么关联。

     如:

    def func(x):

      print 'X in the beginning of func(x): ', x

      x = 2

      print 'X in the end of func(x): ', x

     调用:

    x = 50

    func(x)

    print 'X after calling func(x): ', x

    输出:

    X in the beginning of func(x):  50

    X in the end of func(x):  2

    X after calling func(x):  50

    如果期望在函数 内部改变外部变量的值,有两个方法:

    1、在函数内部用return把改变后的变量值作为函数返回值传递出来,再赋值给变量。

    return x

    x = func(x)

    2、使用全局变量global

    在 Python 的函数定义中,可以给变量名前加上 global 关键字,这样其作用域就不再局限在函数块中,而是全局的作用域。

    例:

    def func():

      global x

      print 'X in the beginning of func(x): ', x

      x = 2

      print 'X in the end of func(x): ', x

    调用:

    x = 50

    func()

    print 'X after calling func(x): ', x

    输出:

    X in the beginning of func(x):  50

    X in the end of func(x):  2

    X after calling func(x):  2

    建议在写代码的过程中,显式地通过 global 来使用全局变量,避免在函数中直接使用外部变量。

    比如:

    def func():

      print 'X in the beginning of func(x): ', x

      # x = 2

      print 'X in the end of func(x): ', x

    调用:

    x = 50

    func()

    print 'X after calling func(x): ', x

    输出:

    X in the beginning of func(x):  50

    X in the end of func(x):  50

    X after calling func(x):  50

    程序可以正常运行。虽然没有指明 global,函数内部还是使用到了外部定义的变量。然而一旦加上x = 2这句,程序就会报错。因为这时候,x 成为一个局部变量,它的作用域从定义处开始,到函数体末尾结束。


    map 函数

    来看两个问题:

    1. 假设有一个数列,如何把其中每一个元素都翻倍?

    2. 假设有两个数列,如何求和?

    第一个问题,普通程序员大概会这么写:

    lst_1 = [1,2,3,4,5,6]

    lst_2 = []

    for item in lst_1:

      lst_2.append(item * 2)

    print lst_2

    Python 程序员大概会这么写:

    lst_1 = [1,2,3,4,5,6]

    lst_2 = [i * 2 for i in lst_1]

    print lst_2

    也可以用map来实现:

    lst_1 = (1,2,3,4,5,6)    #数据改为了元组

    lst_2 = map(lambda x: x * 2, lst_1)   #函数用 lambda 表达式替代

    print lst_2

    map 是 Python 自带的内置函数,它的作用是把一个函数应用在一个(或多个)序列上,把列表中的每一项作为函数输入进行计算,再把计算的结果以列表的形式返回。

    map 的第一个参数是一个函数,之后的参数是序列,可以是 list、tuple。如:

    lst_1 = [1,2,3,4,5,6]

    def double_func(x):

      return x * 2

    lst_2 = map(double_func, lst_1)

    print lst_2

    map 中的函数可以对多个序列进行操作。最开始提出的第二个问题,除了通常的 for 循环写法,如果用列表综合的方法比较难实现,但用 map 就比较方便:

    lst_1 = [1,2,3,4,5,6]

    lst_2 = [1,3,5,7,9,11]

    lst_3 = map(lambda x, y: x + y, lst_1, lst_2)

    print lst_3

    map 中的函数会从对应的列表中依次取出元素,作为参数使用,同样将结果以列表的形式返回。所以要注意的是,函数的参数个数要与 map 中提供的序列组数相同,即函数有几个参数,就得有几组数据。

    对于每组数据中的元素个数,如果有某组数据少于其他组,map 会以 None 来补全这组参数

    此外,当 map 中的函数为 None 时,结果将会直接返回参数组成的列表。如果只有一组序列,会返回元素相同的列表,如果有多组数列,将会返回每组数列中,对应元素构成的元组所组成的列表。如:

    lst_1 = [1,2,3,4,5,6]

    lst_2 = [1,3,5,7,9,11]

    lst_3 = map(None, lst_1)

    print lst_3

    lst_4 = map(None, lst_1, lst_2)

    print lst_4

    结果:

    [1, 2, 3, 4, 5, 6]
    [(1, 1), (2, 3), (3, 5), (4, 7), (5, 9), (6, 11)]


    reduce 函数

     map 可以看作是把一个序列根据某种规则,映射到另一个序列。reduce 做的事情就是把一个序列根据某种规则,归纳为一个输出。

    例:求1累加到100的和,

    寻常的做法大概是这样:

    sum = 0

    for i in xrange(1, 101):

      sum += i

    print sum

    如果用 reduce 函数,就可以写成:

    lst = xrange(1, 101)

    def add(x, y):

       return x + y

    print reduce(add, lst)

    函数:reduce(function, iterable[, initializer])

    第一个参数是作用在序列上的方法,第二个参数是被作用的序列,这与 map 一致。另外有一个可选参数,是初始值。

    function 需要是一个接收2个参数,并有返回值的函数。它会从序列 iterable 里从左到右依次取出元素,进行计算。每次计算的结果,会作为下次计算的第一个参数。

    提供初始值 initializer 时,它会作为第一次计算的第一个参数。否则,就先计算序列中的前两个值。

    如果把刚才的 lst 换成 [1,2,3,4,5],那 reduce(add, lst) 就相当于 ((((1+2)+3)+4)+5)。

    同样,可以用 lambda 函数:reduce((lambda x, y: x + y), xrange(1, 101))

    在对于一个序列进行某种统计操作的时候,比如求和,或者诸如统计序列中元素的出现个数等,可以选择使用 reduce 来实现。相对可以使代码更简洁。

    Python3 里,reduce已经被移出内置函数,使用 reduce 需要先通过 from functools import reduce 引入。


    多线程

    python 里有一个 thread 模块,其中提供了一个函数:

    start_new_thread(function, args[, kwargs])

    function 是开发者定义的线程函数,args 是传递给线程函数的参数,必须是tuple类型,kwargs 是可选参数。

    调用 start_new_thread 之后,会创建一个新的线程,来执行 function 函数。而代码原本的主线程将继续往下执行,不再等待 function 的返回。通常情况,线程在 function 执行完毕后结束。

    例:用 python 编写“爬虫”程序,抓取网上的数据。通过豆瓣的 API 抓取 30 部影片的信息:

       单线程写法:

    import urllib, time

    time_start = time.time()

    data = []

    for i in range(30):

        print 'request movie:', i

        id = 1764796 + i

        url = 'https://api.douban.com/v2/movie/subject/%d' % id

        d = urllib.urlopen(url).read()

        data.append(d)

        print i, time.time() - time_start

    print 'data:', len(data)

      参考输出结果:用了 time.time() 来计算抓取花费的时间。运行一遍,大约需要十几秒

    > python test.py

    request movie: 0

    0 0.741228103638

    request movie: 1

    1 1.96586918831

    ...

    request movie: 28

    28 12.0225770473

    request movie: 29

    29 12.4063940048

    data: 30

      

      多线程写法:

    import urllib, time, thread

    def get_content(i):

        id = 1764796 + i

        url = 'https://api.douban.com/v2/movie/subject/%d' % id

        d = urllib.urlopen(url).read()

        data.append(d)

        print i, time.time() - time_start

        print 'data:', len(data)

    time_start = time.time()

    data = []

    for i in range(30):

        print 'request movie:', i

        thread.start_new_thread(get_content, (i,))

    raw_input('press ENTER to exit... ') #因为主线程不在等待函数返回结果,所以在代码最后,增加了 raw_input,避免程序提前退出。

      参考输出结果:

    > python test.py

    request movie: 0

    request movie: 1

    ...

    request movie: 28

    request movie: 29

    press ENTER to exit...

    1 0.39500784874

    data: 1

    9 0.428859949112

    data: 2

    ...

    data: 28

    21 1.03756284714

    data: 29

    8 2.66121602058

    data: 30

      从输出结果可以看出:

    • 在程序刚开始运行时,已经发送所有请求

    • 收到的请求并不是按发送顺序,先收到就先显示

    • 总共用时两秒多

    • data 里同样记录了所有30条结果

    对于这种耗时长,但又独立的任务,使用多线程可以大大提高运行效率。但在代码层面,可能额外需要做一些处理,保证结果正确。如上例中,如果需要电影信息按 id 排列,就要另行排序。

    多线程通常会用在网络收发数据、文件读写、用户交互等待之类的操作上,以避免程序阻塞,提升用户体验或提高执行效率。

    多线程的实现方法不止这一种。另外多线程也会带来一些单线程程序中不会出现的问题。这里只是简单地开个头。


  • 相关阅读:
    [翻译]关于堆和堆栈
    sql 字符+数值 混合排序 lcs
    证明DataReader分页的可行性 lcs
    谈谈我对小公司、大公司及个人成长的见解 lcs
    sina 通用js代码说明 lcs
    Linux系统下生成证书 https证书
    【转】51单片机外部中断的C51编程
    【转】如何建立个人网站
    【转】关于C51的中断编程[原创]
    【转】毫不费力:破解加密PDF文档就使用这两三招
  • 原文地址:https://www.cnblogs.com/xianhan/p/9021392.html
Copyright © 2011-2022 走看看