入门(四)

一. 字符串格式化

print("%s's score is %d" % ('Mike', 87))

或者借助元组

name = 'Lily'

score = 95

print("%s's score is %d" % (name, score))

二. 文件读取

1. 读文件

f = open('data.txt')

data=f.read()

#readline()#读取一行内容

#readlines()#把内容按行读取至一个列表中

print(data)

f.close()

2. 写文件

f = open('output.txt')#只读模式

f = open('output.txt','w')#可写模式

f = open('output.txt','a')#append写入不覆盖之前内容

data = 'I will be in a file.\nSo cool!'

out = open('output.txt', 'w')

out.write(data)

out.close()

三. 处理文件中的数据

#打开文件

f = file('scores.txt')

lines = f.readlines()

#print lines

f.close()


#处理数据

results = []

for line in lines:

   #print(line)

   data = line.split()

   #print(data)

   sum = 0

   for score in data[1:]:

       sum += int(score)#score是一个字符串,为了做计算,需要转成整数值int

   result = '%s \t: %d\n' % (data[0], sum)

   #print(result)

   results.append(result)

#print(results)


#写入文件

output = file('result.txt', 'w')

output.writelines(results)

output.close()

四. 字典

基本格式:d = { key1: value1, key2: value2 }

1. 键必须唯一

2. 键只能是简单对象,比如字符串、整数、浮点数、bool值

3. 键无序,无法通过索引访问,只能通过键访问

4. 删除某一项:del 字典名[键]

五. 函数默认参数必须在末尾

六. 类方法第一个参数必须为self

七. 继承

class Vehicle:

    def __init__(self, speed):

        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.fuel = fuel

    def drive(self, distance):

        Vehicle.drive(self, distance)

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

b = Bike(15.0)

c = Car(80.0, 0.012)

b.drive(100.0)

c.drive(100.0)

八. 数学运算

import math

print(math.ceil(math.e))#上界

print(math.floor(math.e))#下界

print(math.degrees(math.pi/2))#弧度转角度

print(math.radians(90))#角度转弧度

九. 随机数

1. random.randint(a, b)生成一个a到b间的随机整数,包括a和b。a、b都必须是整数,且必须b≥a。

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

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

4. 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)) #元组

都是可行的用法。

5. random.randrange(start, stop, step)生成一个从start到stop(不包括stop),间隔为step的一个随机数。start、stop、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))

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

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

十. 时间

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

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

1.于是我们可以用这个方法得到程序开始和结束所用的时间,进而算出运行的时间:

import time

starttime = time.time()

print('start:%f' % starttime)

#代码

endtime = time.time() 

print('end:%f' % endtime)

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

2.time.sleep(secs)可以让程序暂停secs秒。在抓取网页的时候,适当让程序sleep一下,可以减少短时间内的请求,提高请求的成功率。

十一. 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.

这就是经 pickle 序列化后的数据,隐约可以看到之前对象的影子。你可能无法看出这个文件的规律,这没关系,Python 能看懂就可以了。

下面取存储的过程:

import pickle

f = file('test.data')

test_data = pickle.load(f)

f.close()

print test_data

控制台的输出:

['Save me!', 123.456, True]

和存储前的数据是一致的。

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

dump 方法可以增加一个可选的参数,来指定用二进制来存储:

pickle.dump(data, f, True)

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

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

Python 还提供了另一个模块 cPickle,它的功能及用法和 pickle 模块完全相同,只不过它是用C语言编写的,因此要快得多(比pickle快1000倍)。因此你可以把上述代码中的 pickle 全部替换为 cPickle,从而提高运行速度(尽管在这个小程序中影响微乎其微)。

十二. 函数的参数传递

1.

def calcSum(*args):

    sum = 0

    for i in args:

        sum += i

    print sum

调用:

calcSum(1,2,3)

calcSum(123,456)

calcSum()

输出:

6

579

0

在变量前加上星号前缀(*),调用时的参数会存储在一个 tuple(元组)对象中,赋值给形参。在函数内部,需要对参数进行处理时,只要对这个 tuple 类型的形参(这里是 args)进行操作就可以了。因此,函数在定义时并不需要指明参数个数,就可以处理任意参数个数的情况。tuple 是有序的,所以 args 中元素的顺序受到赋值时的影响。

2. 

def printAll(**kargs):

    for k in kargs:

        print(k, ':', kargs[k])

printAll(a=1, b=2, c=3)

printAll(x=4, y=5)

输出:

a : 1

c : 3

b : 2

y : 5

x : 4

字典是无序的,所以在输出的时候,并不一定按照提供参数的顺序。同样在调用时,参数的顺序无所谓,只要对应合适的形参名就可以了。于是,采用这种参数传递的方法,可以不受参数数量、位置的限制。

def func(x, y=5, *a, **b):

    print(x, y, a, b)


func(1)

func(1,2)

func(1,2,3)

func(1,2,3,4)

func(x=1)

func(x=1,y=1)

func(x=1,y=1,a=1)

func(x=1,y=1,a=1,b=1)

func(1,y=1)

func(1,2,3,4,a=1)

func(1,2,3,4,k=1,t=2,o=3)

输出:

1 5 () {}

1 2 () {}

1 2 (3,) {}

1 2 (3, 4) {}

1 5 () {}

1 1 () {}

1 1 () {'a': 1}

1 1 () {'a': 1, 'b': 1}

1 1 () {}

1 2 (3, 4) {'a': 1}

1 2 (3, 4) {'k': 1, 't': 2, 'o': 3}

在混合使用时,首先要注意函数的写法,必须遵守:

带有默认值的形参(arg=)须在无默认值的形参(arg)之后;

元组参数(*args)须在带有默认值的形参(arg=)之后;

字典参数(**kargs)须在元组参数(*args)之后。

可以省略某种类型的参数,但仍需保证此顺序规则。

调用时也需要遵守:

指定参数名称的参数要在无指定参数名称的参数之后;

不可以重复传递,即按顺序提供某参数之后,又指定名称传递。

而在函数被调用时,参数的传递过程为:

1.按顺序把无指定参数的实参赋值给形参;

2.把指定参数名称(arg=v)的实参赋值给对应的形参;

3.将多余的无指定参数的实参打包成一个 tuple 传递给元组参数(*args);

4.将多余的指定参数名的实参打包成一个 dict 传递给字典参数(**kargs)。

十三. lambda表达式

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

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

十五. map函数

来看两个问题:

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

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]

def double_func(x):

    return x * 2

lst_2 = map(double_func, lst_1)

print lst_2

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

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

所以刚刚那个问题也可以写成:

lst_1 = (1,2,3,4,5,6)

lst_2 = map(lambda x: x * 2, lst_1)

print lst_2

这里原数据改为了元组,函数用 lambda 表达式替代。

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

十六. reduce函数

from functools import reduce

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

lst = range(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), range(1, 101))

十八. 多线程

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

start_new_thread(function, args[, kwargs])

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

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

改写一下前面的代码,将抓取的部分放在一个函数中:

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...\n')

参考输出结果:

> 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

因为主线程不在等待函数返回结果,所以在代码最后,增加了 raw_input,避免程序提前退出。

从输出结果可以看出:

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

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

总共用时两秒多

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

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

十九. 正则表达式

1. 正则表达式严格区分大小写

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

3. 在正则表达式中,[]表示满足括号中任一字符。“[Hh]i”,既匹配“Hi”,又匹配“hi”。

4. r"hi",字符串前面加了r,是raw的意思,它表示对字符串不进行转义。

5. re.findall(r"hi", text)

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

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

Hi, I am Shirley Hilton. I am his wife.

如果我们用“i.”去匹配,就会得到

['i,', 'ir', 'il', 'is', 'if']

你若是暴力一点,也可以直接用“.”去匹配,看看会得到什么。

与“.”类似的一个符号是“\S”,它表示的是不是空白符的任意字符。注意是大写字符S。

7. 在正则表达式中,任意字符是用“.”表示,“*”表示数量:它表示前面的字符可以重复任意多次(包括0次),只要满足这样的条件,都会被表达式匹配上。

import re

print(re.findall('I.*e','I am Shirley Hilton. I am his wife'))#['I am Shirley Hilton. I am his wife']

print(re.findall('I.*?e','I am Shirley Hilton. I am his wife'))#['I am Shirle', 'I am his wife']

这是因为“*”在匹配时,会匹配尽可能长的结果。如果你想让他匹配到最短的就停止,需要用“.*?”。如“I.*?e”,就会得到第二种结果。这种匹配方式被称为懒惰匹配,而原本尽可能长的方式被称为贪婪匹配。

8. 要匹配数字,我们可以用[0123456789]

由于它们是连续的字符,有一种简化的写法:[0-9]。类似的还有[a-zA-Z]的用法。

还有另一种表示数字的方法:\d

要表示任意长度的数字,就可以用[0-9]*或者\d*

但要注意的是,*表示的任意长度包括0,也就是没有数字的空字符也会被匹配出来。一个与*类似的符号+,表示的则是1个或更长。

所以要匹配出所有的数字串,应当用[0-9]+或者\d+

如果要限定长度,就用{}代替+,大括号里写上你想要的长度。比如11位的数字:\d{11}

9. \b、\d、.、\S这些具有特殊意义的专用字符被称作“元字符”。常用的元字符还有:

\w - 匹配字母或数字或下划线或汉字

\s - 匹配任意的空白符

^ - 匹配字符串的开始

$ - 匹配字符串的结束

10. \S其实就是\s的反义,任意不是空白符的字符。同理,还有:

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

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

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

[a]的反义是[^a],表示除a以外的任意字符。[^abcd]就是除abcd以外的任意字符。

11. 之前我们用过*、+、{}来表示字符的重复。其他重复的方式还有:

? - 重复零次或一次

{n,} - 重复n次或更多次

{n,m} - 重复n到m次

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

^\w{4,12}$表示一段4到12位的字符,包括字母或数字或下划线或汉字,可以用来作为用户注册时检测用户名的规则。

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

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

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

\(?0\d{2,3}[) -]?\d{7,8}

()在正则表达式里也有着特殊的含义,所以要匹配字符"(",需要用"\("。?表示这个括号是可有可无的。

0\d{2,3}区号,0xx或者0xxx

[) -]?在区号之后跟着的可能是")"、" "、"-",也可能什么也没有。

\d{7,8}7或8位的电话号码

使用“|”时,要特别提醒注意的是不同条件之间的顺序。匹配时,会按照从左往右的顺序,一旦匹配成功就停止验证后面的规则。假设要匹配的电话号码还有可能是任意长度的数字(如一些特殊的服务号码),你应该把|\d+这个条件加在表达式的最后。如果放在最前面,某些数据就可能会被优先匹配为这一条件。

你可能感兴趣的:(入门(四))