一. 字符串格式化
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+这个条件加在表达式的最后。如果放在最前面,某些数据就可能会被优先匹配为这一条件。