crossin课程 1-74

一个很弱智的猜数字小游戏

In [1]:
from random import randint

num = randint(1, 100)
print('guess what I think?')
answer = int(input())
while(answer!=num):
    if answer<num:
        print('too small?')
    elif answer>num:
        print('too big?')
    elif answer==num:
        break
    answer = int(input())
print('equal')
guess what I think?
50
too big?
25
too big?
10
too small?
15
too small?
17
too small?
19
equal

range 列表

In [2]:
sum = 0
for _ in range(1,101) :
    sum = sum + _
print(sum)
5050

print的简单用法

In [3]:
print("this is the\
 same line")

print('''
"What's your name?" I asked.
"I'm Han Meimei."
''')

num = 18
print ('My age is %d' % num)
print ('Price is %.2f' % 4.6765)
this is the same line

"What's your name?" I asked.
"I'm Han Meimei."

My age is 18
Price is 4.68

for嵌套

In [4]:
for i in range(0, 5):
    for j in range(0, i+1):
        print ('*',end=' ')
    print('\t')
* 	
* * 	
* * * 	
* * * * 	
* * * * * 	

字符串拼接 bool值

In [5]:
print ('%s is easy to %d' % ('Python',100))
bool(None)
Python is easy to 100
Out[5]:
False

函数定义

In [6]:
def sayHello(someone):
   print( someone + ' say hello world!')

sayHello('bzz')

def plus(num1, num2):
   print (num1+num2)

plus(1,5)
bzz say hello world!
6

函数实现弱智小游戏

In [7]:
def isEqual(num1, num2):
    if num1<num2:
        print ('too small')
        return False;
    elif num1>num2:
        print ('too big')
        return False;
    else :
        print ('bingo')
        return True

from random import randint

num = randint(1, 100)
print ('Guess what I think?')
bingo = False
while bingo == False:
    answer = int(input())
    bingo = isEqual(answer, num)
Guess what I think?
50
too small
80
too big
70
too big
65
too big
63
too big
55
too small
60
too big
58
too big
57
bingo

print(list(range(1,10))) 可以在python3shell下通过,但无法在jupyter notebook 中通过,目前无解

In [8]:
print(list(range(1,10)))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

调用numpy 显示list

In [9]:
import numpy as np
a=np.arange(1,10)
print(a)
[1 2 3 4 5 6 7 8 9]

操作list

In [10]:
l = [365, 'everyday', 0.618, True]
print (l[1])
#修改list中的元素
l[0] = 123
print (l)
#向list中添加元素
l.append(1024)
print (l)
#删除list中的元素
del l[0]
print (l)
everyday
[123, 'everyday', 0.618, True]
[123, 'everyday', 0.618, True, 1024]
['everyday', 0.618, True, 1024]

点球游戏1

In [11]:
from random import choice
print ('Choose one side to shoot:')
print ('left, center, right')
you = input()
print ('You kicked ' + you)
direction = ['left', 'center', 'right']
com = choice(direction)
print ('Computer saved :' + com)
if you != com:
   print ('Goal!')
else:
   print ('Oops...')
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved :right
Goal!

list切片

  • list有两类常用操作:索引(index)和切片(slice)。
  • 上是索引,现使用切片
  • 切片操作符是在[]内提供一对可选数字,用 : 分割。冒号前的数表示切片的开始位置,冒号后的数字表示切片到哪里结束。同样,计数从0开始。 注意,开始位置包含在切片中,而结束位置不包括。

切片的语法表达式为:[start_index : end_index : step],其中: start_index表示起始索引end_index表示结束索引 step表示步长,步长不能为0,且默认值为1 切片操作是指按照步长,截取从起始索引到结束索引,但不包含结束索引(也就是结束索引减1)的所有元素。

python3支持切片操作的数据类型有list、tuple、string、unicode、range 切片返回的结果类型与原对象类型一致 切片不会改变原对象,而是重新生成了一个新的对象

In [12]:
l = ['a', 'b', 'c', 'd', 'e', 'f']
print(l[-1]) # "-" 表示倒数
print(l[-4])
print(l[0:4])

print(l[1:])
print(l[:3])
print(l[:])
print(l[1:-1])
f
c
['a', 'b', 'c', 'd']
['b', 'c', 'd', 'e', 'f']
['a', 'b', 'c']
['a', 'b', 'c', 'd', 'e', 'f']
['b', 'c', 'd', 'e']

点球游戏2

In [13]:
from random import choice

score_you = 0
score_com = 0
direction = ['left', 'center', 'right']

for i in range(5):
    print ('==== Round %d - You Kick! ====' % (i+1))
    print ('选一边射门')#Choose one side to shoot:
    print ('left, center, right')
    you = input()
    print ('You kicked ' + you)
    com = choice(direction)
    print ('Computer saved ' + com)
    if you != com:
        print ('Goal!')
        score_you += 1
    else:
        print ('Oops...')
        print ('Score: %d(you) - %d(com)\n' % (score_you, score_com))

    print ('==== Round %d - You Save! ====' % (i+1))
    print ('选一边守门:')
    print ('left, center, right')
    you = input()
    print ('You saved ' + you)
    com = choice(direction)
    print ('Computer kicked ' + com)
    if you == com:
        print ('Saved!')
    else:
        print ('Oops...')
        score_com += 1
    print ('Score: %d(you) - %d(com)\n' % (score_you, score_com))
==== Round 1 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved right
Goal!
==== Round 1 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked center
Oops...
Score: 1(you) - 1(com)

==== Round 2 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved center
Goal!
==== Round 2 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked left
Saved!
Score: 2(you) - 1(com)

==== Round 3 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved right
Goal!
==== Round 3 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked center
Oops...
Score: 3(you) - 2(com)

==== Round 4 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 3(you) - 2(com)

==== Round 4 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked center
Oops...
Score: 3(you) - 3(com)

==== Round 5 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved right
Goal!
==== Round 5 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked left
Saved!
Score: 4(you) - 3(com)

字符串的分割

In [14]:
sentence = 'I am an Englist sentence'
print(sentence.split())

# 指定参数分割 
section = 'Hi! I am the one! Bye!'
print(section.split('!')) # ! 所有都被分割 空格也是

print('_a!a a .'.split('a'))
['I', 'am', 'an', 'Englist', 'sentence']
['Hi', ' I am the one', ' Bye', '']
['_', '!', ' ', ' .']

点球游戏 加上胜负判断

In [15]:
from random import choice

score = [0, 0]
direction = ['left', 'center', 'right']

def kick():
    print ('==== You Kick! ====')
    print ('Choose one side to shoot:')
    print ('left, center, right')
    you = input()
    print ('You kicked ' + you)
    com = choice(direction)
    print ('Computer saved ' + com)
    if you != com:
        print ('Goal!')
        score[0] += 1
    else:
        print ('Oops...')
    print ('Score: %d(you) - %d(com)\n' % (score[0], score[1]))
 
    print ('==== You Save! ====')
    print ('Choose one side to save:')
    print ('left, center, right')
    you = input()
    print ('You saved ' + you)
    com = choice(direction)
    print ('Computer kicked ' + com)
    if you == com:
        print ('Saved!')
    else:
        print ('Oops...')
        score[1] += 1
    print ('Score: %d(you) - %d(com)\n' % (score[0], score[1]))

for i in range(5):
    print ('==== Round %d ====' % (i+1))
    kick()

while(score[0] == score[1]):
    i += 1
    print ('==== Round %d ====' % (i+1))
    kick()

if score[0] > score[1]:
    print( 'You Win!')
else:
    print ('You Lose.')
==== Round 1 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 0(you) - 0(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 0(you) - 1(com)

==== Round 2 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved right
Goal!
Score: 1(you) - 1(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 1(you) - 2(com)

==== Round 3 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 1(you) - 2(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 1(you) - 3(com)

==== Round 4 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 1(you) - 3(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked left
Saved!
Score: 1(you) - 3(com)

==== Round 5 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved center
Goal!
Score: 2(you) - 3(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 2(you) - 4(com)

You Lose.

连接list

  • join和昨天说的split正好相反:split是把一个字符串分割成很多字符串组成的list,而join则是把一个list中的所有字符串连接成一个字符串。
  • join的格式有些奇怪,它不是list的方法,而是字符串的方法。首先你需要有一个字符串作为list中所有元素的连接符,然后再调用这个连接符的join方法,join的参数是被连接的list:
In [16]:
s = ';'
li = ['apple', 'pear', 'orange']
fruit = s.join(li)
print (fruit)
print(';'.join(['apple', 'pear', 'orange']))
print(''.join(['hello', 'world'])) # 无缝连接
apple;pear;orange
apple;pear;orange
helloworld

字符串的索引和切片

join方法也可以对字符串使用,作用就是用连接符把字符串中的每个字符重新连接成一个新字符串

In [17]:
word = 'helloworld'
for c in word:
    print (c,end='.')
print('\t')
print('0.1.2.3.4.5.6.7.8.9.')
print (word[0])
print (word[-2])

print (word[5:7])
print (word[:-5])
print (word[:])

newword = ','.join(word)
print(newword)
h.e.l.l.o.w.o.r.l.d.	
0.1.2.3.4.5.6.7.8.9.
h
l
wo
hello
helloworld
h,e,l,l,o,w,o,r,l,d

读文件

In [18]:
f = open('data.txt')
data1 = f.read()
f = open('data.txt')
data2 = f.readline()
data3 = f.readlines()
print(data1)
print('--------------')
print(data2)
print('--------------')
print(data3)
f.close #释放资源
sfd
affffffff
~?!
--------------
sfd

--------------
['affffffff\n', '~?!']
Out[18]:
In [21]:
f = open('output.txt')#, 'w'

data1 = f.read()
f = open('data.txt')
data2 = f.readline()
data3 = f.readlines()
print(data1)
print('--------------')
print(data2)
print('--------------')
print(data3)
f.close #释放资源
50 50 50

--------------
sfd

--------------
['affffffff\n', '~?!']
Out[21]:

处理文件中的数据

In [22]:
f = open('scores.txt')
lines = f.readlines()
f.close()
print(lines)
['刘备 23 35 44 47 51\n', '关羽 60 77 68\n', '张飞 97 99 89 91\n', '诸葛亮 100']
In [23]:
import os  # 调用os后, f = open('results.txt','w') , 有此文件则打开,否则新建

results = []
for line in lines:
    data = line.split()
    #print(data)
    sum = 0
    for score in data[1:]:
        sum += int(score)
    result = '%s\t: %d\n' % (data[0], sum)
    results.append(result)
    
f = open('result.txt','w')

f.writelines(results)
f.close()

break

如果在循环条件仍然满足或序列没有遍历完的时候,想要强行跳出循环,就需要用到break语句。

In [24]:
for i in range(10):
    a = input()
    if a == 'EOF':
        break
2154
EOF

continue

break是彻底地跳出循环,而continue只是略过本次循环的余下内容,直接进入下一次循环

In [25]:
f = open('scores.txt')
lines = f.readlines()
f.close()
print(lines)

import os  # 调用os后, f = open('results.txt','w') , 有此文件则打开,否则新建

results = []
for line in lines:
    data = line.split()
    #print(data)
    sum = 0
    for score in data[1:]:
        point = int(score)
        if point < 60:
            continue
        sum += point
    result = '%s\t: %d\n' % (data[0], sum)
    results.append(result)
    
f = open('result.txt','w')

f.writelines(results)
f.close()
['刘备 23 35 44 47 51\n', '关羽 60 77 68\n', '张飞 97 99 89 91\n', '诸葛亮 100']

异常处理

In [26]:
#print (int('0.5'))

import os
try:
    #f = open('non-exist.txt')
    f = open('scores.txt')
    lines = f.readlines()
    f.close()
    print("Open success!",end = '  ')
    print(lines)
except:
    print( 'File not exists.')
print( 'Done')
Open success!  ['刘备 23 35 44 47 51\n', '关羽 60 77 68\n', '张飞 97 99 89 91\n', '诸葛亮 100']
Done

查天气 网络模块

In [7]:
import urllib3
http = urllib3.PoolManager()
r = http.request('GET','http://www.baidu.com')
print(r.status)
print(r.data)
200
b'\r\n\r\n\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\xe7\x99\xbe\xe5\xba\xa6\xe4\xb8\x80\xe4\xb8\x8b\xef\xbc\x8c\xe4\xbd\xa0\xe5\xb0\xb1\xe7\x9f\xa5\xe9\x81\x93\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\r\n
\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n'

面向对象

In [4]:
s = 'how are you'
#s被赋值后就是一个字符串类型的对象
l = s.split()
#split是字符串的方法,这个方法返回一个list类型的对象
#l是一个list类型的对象
#通过dir()方法可以查看一个类/变量的所有属性:
print(dir(s))
print('---------')
print(dir(list))
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
---------
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
  • 关键字class加上类名用来创建一个类。之后缩进的代码块是这个类的内部。在这里,我们用pass语句,表示一个空的代码块
In [6]:
class MyClass:
    print('bzz')
    pass

mc = MyClass()
print (mc)

#mc是__main__模块中MyClass来的一个实例(instance)
bzz
<__main__.MyClass object at 0x0000025C1069EF98>
In [12]:
#  给这个类加上一些域:
#  给MyClass类增加了一个类变量name,并把它的值设为'Sam'。然后又增加了一个类方法sayHi
#  调用类变量的方法是“对象.变量名”。你可以得到它的值,也可以改变它的值。

#  类方法和我们之前定义的函数区别在于,第一个参数必须为self。
#  而在调用类方法的时候,通过“对象.方法名()”格式进行调用,
#  而不需要额外提供self这个参数的值。self在类方法中的值,就是你调用的这个对象本身
class MyClass:
    name = 'Sam'
    def sayHi(self):
        print ('Hello %s' % self.name)

mc = MyClass()
print (mc.name)
MyClass.name = 'Mike'
print(MyClass().name)

mc.name = 'Lily'
mc.sayHi()
Sam
Mike
Hello Lily
  • 假设我们有一辆汽车,我们知道它的速度(60km/h),以及A、B两地的距离(100km)。要算出开着这辆车从A地到B地花费的时间。
In [13]:
#面向过程的方法:
speed = 60.0
distance = 100.0
time = distance / speed
print (time)

#面向对象的方法:
class Car:
    speed = 0
    def drive(self, distance):
        time = distance / self.speed
        print (time)

car = Car()
car.speed = 60.0
car.drive(100.0)
1.6666666666666667
1.6666666666666667
  • 如果我们让题目再稍稍复杂一点。假设我们又有了一辆更好的跑车,它的速度是150km/h,然后我们除了想从A到B,还要从B到C(距离200km)。要求分别知道这两种车在这两段路上需要多少时间。
In [16]:
#面向过程的方法:
speed1 = 60.0
distance1 = 100.0
time1 = distance1 / speed1
print (time1)
distance2 = 200.0
time2 = distance2 / speed1
print (time2)
speed2 = 150.0
time3 = distance1 / speed2
print (time3)
time4 = distance2 / speed2
print (time4)
print('--------------')
#面向对象的方法:
class Car:
    speed = 0
    def drive(self, distance):
        time = distance / self.speed
        print (time)
        
car1 = Car()
car1.speed = 60.0
car1.drive(100.0)
car1.drive(200.0)
car2 = Car()
car2.speed = 150.0
car2.drive(100.0)
car2.drive(200.0)
1.6666666666666667
3.3333333333333335
0.6666666666666666
1.3333333333333333
--------------
1.6666666666666667
3.3333333333333335
0.6666666666666666
1.3333333333333333

仍然是从A地到B地,这次除了有汽车,我们还有了一辆自行车! 自行车和汽车有着相同的属性:速度(speed)。还有一个相同的方法(drive),来输出行驶/骑行一段距离所花的时间。但这次我们要给汽车增加一个属性:每公里油耗(fuel)。而在汽车行驶一段距离的方法中,除了要输出所花的时间外,还要输出所需的油量。

面向过程的方法,你可能需要写两个函数,然后把数据作为参数传递进去,在调用的时候要搞清应该使用哪个函数和哪些数据。有了面向对象,你可以把相关的数据和方法封装在一起,并且可以把不同类中的相同功能整合起来。这就需要用到面向对象中的另一个重要概念:继承。

我们要使用的方法是,创建一个叫做Vehicle的类,表示某种车,它包含了汽车和自行车所共有的东西:速度,行驶的方法。然后让Car类和Bike类都继承这个Vehicle类,即作为它的子类。在每个子类中,可以分别添加各自独有的属性。

In [20]:
# Vehicle类被称为基本类或超类,Car类和Bike类被成为导出类或子类
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)
c.drive(100)

'''
解释一下代码:

__init__函数会在类被创建的时候自动调用,用来初始化类。它的参数,要在创建类的时候提供。
于是我们通过提供一个数值来初始化speed的值。

注意:__init__是python的内置方法,类似的函数名前后是两个下英文划线,如果写错了,
则不会起到原本应有的作用。

class定义后面的括号里表示这个类继承于哪个类。
Bike(Vehicle)就是说Bike是继承自Vehicle中的子类。Vehicle中的属性和方法,Bike都会有。
因为Bike不需要有额外的功能,所以用pass在类中保留空块,什么都不用写。

Car类中,我们又重新定义了__init__和drive函数,这样会覆盖掉它继承自Vehicle的同名函数。
但我们依然可以通过“Vehicle.函数名”来调用它的超类方法。
以此来获得它作为Vehicle所具有的功能。注意,因为是通过类名调用方法,
而不是像之前一样通过对象来调用,所以这里必须提供self的参数值。
在调用超类的方法之后,我们又给Car增加了一个fuel属性,并且在drive中多输出一行信息。


'''
need 6.666667 hour(s)
need 1.250000 hour(s)
need 1.200000 fuels
Out[20]:
'\n解释一下代码:\n\n__init__函数会在类被创建的时候自动调用,用来初始化类。它的参数,要在创建类的时候提供。\n于是我们通过提供一个数值来初始化speed的值。\n\n注意:__init__是python的内置方法,类似的函数名前后是两个下英文划线,如果写错了,\n则不会起到原本应有的作用。\n\nclass定义后面的括号里表示这个类继承于哪个类。\nBike(Vehicle)就是说Bike是继承自Vehicle中的子类。Vehicle中的属性和方法,Bike都会有。\n因为Bike不需要有额外的功能,所以用pass在类中保留空块,什么都不用写。\n\nCar类中,我们又重新定义了__init__和drive函数,这样会覆盖掉它继承自Vehicle的同名函数。\n但我们依然可以通过“Vehicle.函数名”来调用它的超类方法。\n以此来获得它作为Vehicle所具有的功能。注意,因为是通过类名调用方法,\n而不是像之前一样通过对象来调用,所以这里必须提供self的参数值。\n在调用超类的方法之后,我们又给Car增加了一个fuel属性,并且在drive中多输出一行信息。\n\n\n'

and-or

In [21]:
a = "heaven"
b = "hell"
c = True and a or b
print (c)
d = False and a or b
print (d)
heaven
hell

结果很奇怪是不是?

表达式从左往右运算,1和"heaven"做and的结果是"heaven",再与"hell"做or的结果是"heaven";0和"heaven"做and的结果是0,再与"hell"做or的结果是"hell"。

抛开绕人的and和or的逻辑,你只需记住,在一个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会让你的代码更简单。但如果你觉得这个技巧带来的副作用已经让你头大了,没关系,用if-else可以做相同的事情。不过在python的某些情况下,你可能没法使用if语句,比如lambda函数中,这时候你可能就需要and-or的帮助了。

什么是lambda函数?呵呵,这是python的高阶玩法,暂且按住不表,以后有机会再说。

In [25]:
a = ""
b = "hell"
c = (True and [a] or [b])[0]
print (c)
 
         

元组

In [31]:
#上一次pygame的课中有这样一行代码:
#x, y = pygame.mouse.get_pos()

# 元组(tuple)也是一种序列,和我们用了很多次的list类似,
# 只是元组中的元素在创建之后就不能被修改。
# 如:

postion = (1, 2)
geeks = ('Sheldon', 'Leonard', 'Rajesh', 'Howard')

# 都是元组的实例。它有和list同样的索引、切片、遍历等操作(参见25~27课):
print (postion[0])
print('------------')

for _ in geeks:
    print (_)
print('------------')

print (geeks[1:3])

#其实我们之前一直在用元组,就是在print语句中:
print('------------')
print ('%s is %d years old' % ('Mike', 23))

#('Mike', 23)就是一个元组。这是元组最常见的用处。

#再来看一下元组作为函数返回值的例子:

def get_pos(n):
    return (n/2, n*2)

#得到这个函数的返回值有两种形式,一种是根据返回值元组中元素的个数提供变量:

x, y = get_pos(50)
print (x)
print (y)

print('------------')
#这就是我们在开头那句代码中使用的方式。
#还有一种方法是用一个变量记录返回的元组:

pos = get_pos(50)
print (pos[0])
print (pos[1])
1
------------
Sheldon
Leonard
Rajesh
Howard
------------
('Leonard', 'Rajesh')
------------
Mike is 23 years old
25.0
100
------------
25.0
100

数学运算

In [40]:
import math

print(math.pi)
print(math.e)

print(math.ceil(5.4))
print(math.floor(5.4))

print(math.pow(3,2))
print(math.log(4))
print(math.log(100,10))

print(math.sqrt(9))
print(math.fabs(-34))

#math.sin .cos .tan .asin .acos .atan    弧度单位

#math.degrees(x) 弧度转角度
#math.radians(x) 角度转弧度
3.141592653589793
2.718281828459045
6
5
9.0
1.3862943611198906
2.0
3.0
34.0

正则表达式

什么是正则表达式?在回答这个问题之前,先来看看为什么要有正则表达式。

在编程处理文本的过程中,经常会需要按照某种规则去查找一些特定的字符串。比如知道一个网页上的图片都是叫做'image/8554278135.jpg'之类的名字,只是那串数字不一样;又或者在一堆人员电子档案中,你要把他们的电话号码全部找出来,整理成通讯录。诸如此类工作,如果手工去做,当量大的时候那简直就是悲剧。但你知道这些字符信息有一定的规律,可不可以利用这些规律,让程序自动来做这些无聊的事情?答案是肯定的。这时候,你就需要一种描述这些规律的方法,正则表达式就是干这事的。

正则表达式就是记录文本规则的代码。

所以正则表达式并不是python中特有的功能,它是一种通用的方法。python中的正则表达式库,所做的事情是利用正则表达式来搜索文本。要使用它,你必须会自己用正则表达式来描述文本规则。之前多次有同学表示查找文本的事情经常会遇上,希望能介绍一下正则表达式。既然如此,我们就从正则表达式的基本规则开始说起。

1.

首先说一种最简单的正则表达式,它没有特殊的符号,只有基本的字母或数字。它满足的匹配规则就是完全匹配。例如:有个正则表达式是“hi”,那么它就可以匹配出文本中所有含有hi的字符。

来看如下的一段文字:

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

如果我们用“hi”这个正则表达式去匹配这段文字,将会得到两个结果。因为是完全匹配,所以每个结果都是“hi”。这两个“hi”分别来自“Shirley”和“his”。默认情况下正则表达式是严格区分大小写的,所以“Hi”和“Hilton”中的“Hi”被忽略了。

为了验证正则表达式匹配的结果,你可以用以下这段代码做实验:

In [6]:
import re

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

m = re.findall(r"[Hh]i", text)

if m:
    print (m)
else:
    print ('not match')
['Hi', 'hi', 'Hi', 'hi', 'hi']

暂时先不解释这其中代码的具体含义,你只要去更改text和findall中的字符串,就可以用它来检测正则表达式的实际效果。

2.

如果我们只想找到“hi”这个单词,而不把包含它的单词也算在内,那就可以使用“\bhi\b”这个正则表达式。在以前的字符串处理中,我们已经见过类似“\n”这种特殊字符。在正则表达式中,这种字符更多,以后足以让你眼花缭乱。

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

在前面那个例子里,“\bhi\b”匹配不到任何结果。但“\bhi”的话就可以匹配到1个“hi”,出自“his”。用这种方法,你可以找出一段话中所有单词“Hi”,想一下要怎么写。

3.

最后再说一下[]这个符号。在正则表达式中,[]表示满足括号中任一字符。比如“[hi]”,它就不是匹配“hi”了,而是匹配“h”或者“i”。

在前面例子中,如果把正则表达式改为“[Hh]i”,就可以既匹配“Hi”,又匹配“hi”了。

1.r"hi"

这里字符串前面加了r,是raw的意思,它表示对字符串不进行转义。为什么要加这个?你可以试试print "\bhi"和r"\bhi"的区别。

print "\bhi"

hi

print r"\bhi"

\bhi

可以看到,不加r的话,\b就没有了。因为python的字符串碰到“\”就会转义它后面的字符。如果你想在字符串里打“\”,则必须要打“\”。

print "\bhi"

\bhi

这样的话,我们的正则表达式里就会多出很多“\”,让本来就已经复杂的字符串混乱得像五仁月饼一般。但加上了“r”,就表示不要去转义字符串中的任何字符,保持它的原样。

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

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

3.今天主要说两个符号“.”和“*”,顺带说下“\S”和“?”。

“.”在正则表达式中表示除换行符以外的任意字符。在上节课提供的那段例子文本中:

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

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

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

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

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

4.在很多搜索中,会用“?”表示任意一个字符, 表示任意数量连续字符,这种被称为通配符。但在正则表达式中,任意字符是用 “ . ” 表示,而 则不是表示字符,而是表示数量:它表示前面的字符可以重复任意多次(包括0次),只要满足这样的条件,都会被表达式匹配上。

结合前面的“.”,用“I.e”去匹配,想一下会得到什么结果?

['I am Shirley Hilton. I am his wife']

是不是跟你想的有些不一样?也许你会以为是

['I am Shirle', 'I am his wife']

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

最后留一道习题:

从下面一段文本中,匹配出所有s开头,e结尾的单词。

site sea sue sweet see case sse ssee loses

先来公布上一课习题的答案:

\bs\S*e\b

有的同学给出的答案是"\bs.*?e\b"。测试一下就会发现,有奇怪的'sea sue'和'sweet see'混进来了。既然是单词,我们就不要空格,所以需要用"\S"而不是"."

昨天有位同学在论坛上说,用正则表达式匹配出了文件中的手机号。这样现学现用很不错。匹配的规则是"1.*?\n",在这个文件的条件下,是可行的。但这规则不够严格,且依赖于手机号结尾有换行符。今天我来讲讲其他的方法。

匹配手机号,其实就是找出一串连续的数字。更进一步,是11位,以1开头的数字。

还记得正则第1讲里提到的[]符号吗?它表示其中任意一个字符。所以要匹配数字,我们可以用

[0123456789]

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

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

\d

要表示任意长度的数字,就可以用

[0-9]*

或者

\d*

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

所以要匹配出所有的数字串,应当用

[0-9]+

或者

\d+

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

\d{11}

想要再把第一位限定为1,就在前面加上1,后面去掉一位:

1\d{10}

OK. 总结一下今天提到的符号:

[0-9]

\d

+

{}

现在你可以去一个混杂着各种数据的文件里,抓出里面的手机号,或是其他你感兴趣的数字了。

1.

我们已经了解了正则表达式中的一些特殊符号,如\b、\d、.、\S等等。这些具有特殊意义的专用字符被称作“元字符”。常用的元字符还有:

\w - 匹配字母或数字或下划线或汉字(我试验下了,发现3.x版本可以匹配汉字,但2.x版本不可以)

\s - 匹配任意的空白符

^ - 匹配字符串的开始

$ - 匹配字符串的结束

2.

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

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

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

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

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

3.

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

? - 重复零次或一次

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

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

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

^\w{4,12}$

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

\d{15,18}

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

^1\d*x?

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

另外再说一下之前提到的转义字符\。如果我们确实要匹配.或者字符本身,而不是要它们所代表的元字符,那就需要用.或\。\本身也需要用\。

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

留一道稍稍有难度的习题:

写一个正则表达式,能匹配出多种格式的电话号码,包括

(021)88776543

010-55667890

02584453362

0571 66345673

来说上次的习题:

(021)88776543

010-55667890

02584453362

0571 66345673

一个可以匹配出所有结果的表达式是

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

解释一下:

(?

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

0\d{2,3}

区号,0xx或者0xxx

[) -]?

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

\d{7,8}

7或8位的电话号码

可是,这个表达式虽然能匹配出所有正确的数据(一般情况下,这样已经足够),但理论上也会匹配到错误的数据。因为()应当是成对出现的,表达式中对于左右两个括号并没有做关联处理,例如(02188776543这样的数据也是符合条件的。

我们可以用正则表达式中的“|”符号解决这种问题。“|”相当于python中“or”的作用,它连接的两个表达式,只要满足其中之一,就会被算作匹配成功。

于是我们可以把()的情况单独分离出来:

(0\d{2,3})\d{7,8}

其他情况:

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

合并:

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

使用“|”时,要特别提醒注意的是不同条件之间的顺序。匹配时,会按照从左往右的顺序,一旦匹配成功就停止验证后面的规则。假设要匹配的电话号码还有可能是任意长度的数字(如一些特殊的服务号码),你应该把

|\d+

这个条件加在表达式的最后。如果放在最前面,某些数据就可能会被优先匹配为这一条件。你可以写个测试用例体会一下两种结果的不同。

关于正则表达式,我们已经讲了5篇,介绍了正则表达式最最皮毛的一些用法。接下来,这个话题要稍稍告一段落。推荐一篇叫做《正则表达式30分钟入门教程》的文章(直接百度一下就能找到,我也会转到论坛上),想要对正则表达式进一步学习的同学可以参考。这篇教程是个标题党,里面涉及了正则表达式较多的内容,30分钟绝对看不完。

random模块的作用是产生随机数。之前的小游戏中用到过random中的randint:

import random

num = random.randint(1,100)

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

a、b都必须是整数,且必须b≥a。当等于的时候,比如:

random.randint(3, 3)

的结果就永远是3

除了randint,random模块中比较常用的方法还有:

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

比如:

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中常见的几个方法。如果你在程序中需要其中某一个方法,也可以这样写:

from random import randint

randint(1, 10)

另外,有些编程基础的同学可能知道,在随机数中有个seed的概念,需要一个真实的随机数,比如此刻的时间、鼠标的位置等等,以此为基础产生伪随机数。在python中,默认用系统时间作为seed。你也可以手动调用random.seed(x)来指定seed。

Python中有一个time模块

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

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

Python中time模块的一个方法

time.time()

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

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

In [9]:
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)) 
start:1530929854.164775
0
1
2
3
4
5
6
7
8
9
end:1530929854.165777
total time:0.001002

顺便再说下time中的另一个很有用的方法:

time.sleep(secs)

它可以让程序暂停secs秒。例如:

In [12]:
import time

print (1)

time.sleep(2)

print (2)
1
2

今天我就来讲讲代码遇到问题时的一些简单处理方法。

  1. 读错误信息

来看如下一个例程:

import random

a = 0

for i in range(5):

b = random.choice(range(5))

a += i / b

print a

这个程序中,i从0循环到4,每次循环中,b是0到4中的一个随机数。把i/b的结果累加到a上,最后输出结果。

运行这段程序,有时候会输出结果,有时候却跳出错误信息:

Traceback (most recent call last):

File "C:\Users\Crossin\Desktop\py\test.py", line 5, in

a += i / b

ZeroDivisionError: integer division or modulo by zero

有些同学看见一段英文提示就慌了。其实没那么复杂,python的错误提示做得还是很标准的。

它告诉我们错误发生在test.py文件中的第6行

a += i / b

这一句上。

这个错误是“ZeroDivisionError”,也就是除零错。

“integer division or modulo by zero”,整数被0除或者被0模(取余数)。

因为0不能作为除数,所以当b随机到0的时候,就会引发这个错误。

知道了原因,就可以顺利地解决掉这个bug。

以后在写代码的时候,如果遇到了错误,先别急着去改代码。试着去读一读错误提示,看看里面都说了些啥。

  1. 输出调试信息

我们在所有课程的最开始就教了输出函数“print”。它是编程中最简单的调试手段。有的时候,仅从错误提示仍然无法判断出程序错误的原因,或者没有发生错误,但程序的结果就是不对。这种情况下,通过输出程序过程中的一些状态,可以帮助分析程序。

把前面那个程序改造一下,加入一些与程序功能无关的输出语句:

import random

a = 0

for i in range(5):

print 'i: %d' % i 

b = random.choice(range(5))

print 'b: %d' % b

a += i / b

print 'a: %d' % a

print

print a

运行后的输出结果(每次结果都会不一样):

i: 0

b: 3

a: 0

i: 1

b: 3

a: 0

i: 2

b: 3

a: 0

i: 3

b: 0

Traceback (most recent call last):

File "C:\Users\Crossin\Desktop\py\test.py", line 7, in

a += i / b

ZeroDivisionError: integer division or modulo by zero

当b的值为0时,发生了除零错。这次可以更清晰地看出程序出错时的状态。

在真实开发中,程序的结构可能会非常复杂。通过输出调试信息,可以有效地缩小范围、定位错误发生的位置,确认错误发生时的场景,进而找出错误原因。

序列化 pickle

  • python3 在字符方面和 python2 区别较大
In [30]:
import pickle

test_data = ['Save me!'.encode('utf8'), 123.456, True]

f = open('test.data', 'wb+')

pickle.dump(test_data, f)

f.close()
In [32]:
import pickle

f = open('test.data','rb')

test_data = pickle.load(f)

f.close()

print (test_data)
[b'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倍)。因此你可以把上述代码中的 pickle 全部替换为 cPickle,从而提高运行速度(尽管在这个小程序中影响微乎其微)。

列表解析(List Comprehension)

In [33]:
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]
In [34]:
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]

函数的参数传递

我们曾经讲过 Python 中函数的参数传递(见第21课)。最基本的方式是:

定义:

def func(arg1, arg2):

print arg1, arg2

调用:

func(3, 7)

我们把函数定义时的参数名(arg1、arg2)称为形参,调用时提供的参数(3、7)称为实参。

这种方式是根据调用时提供参数的位置进行匹配,要求实参与行参的数量相等,默认按位置匹配参数。调用时,少参数或者多参数都会引起错误。这是最常用的一种函数定义方式。

在调用时,也可以根据形参的名称指定实参。如:

func(arg2=3, arg1=7)

但同样,必须提供所有的参数。看看和func(3, 7)的运行结果有什么不同。

Python 语言还提供了其他一些更灵活的参数传递方式,如:

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

func3(*args)

func4(**kargs)

今天我们先说说func2这种方式。

这种方式可以理解为,在一般函数定义的基础上,增加了参数的默认值。这样定义的函数可以和原来一样使用,而当你没有提供足够的参数时,会用默认值作为参数的值。

例如:

定义

In [2]:
def func(arg1=1, arg2=2, arg3=3):

    print (arg1, arg2, arg3)
In [3]:
# 调用:

func(2, 3, 4)

func(5, 6)

func(7)
2 3 4
5 6 3
7 2 3

输出为

2 3 4

5 6 3

7 2 3

提供的参数会按顺序先匹配前面位置的参数,后面未匹配到的参数使用默认值。

也可以指定其中的部分参数,如:

In [4]:
func(arg2=8)

func(arg3=9, arg1=10)
1 8 3
10 2 9

输出为

1 8 3

10 2 9

In [5]:
#或者混合起来用:

func(11, arg3=12)
11 2 12

输出为

11 2 12

但要注意,没有指定参数名的参数必须在所有指定参数名的参数前面,且参数不能重复。以下的调用都是错误的:

func(arg1=13, 14)

func(15, arg1=16)

定义参数默认值的函数可以在调用时更加简洁。大量 Python 模块中的方法都运用了这一方式,让使用者在调用时可以提供尽可能少的参数。

接着上一次的内容,来介绍一种更加灵活的参数传递方式:

def func(*args)

这种方式的厉害之处在于,它可以接受任意数量的参数。来看具体例子:

In [7]:
def calcSum(*args):

    sum = 0

    for i in args:

        sum += i

    print (sum)
In [9]:
# 调用
calcSum(1,2,3)

calcSum(123,456)

calcSum()
6
579
0

输出:

6

579

0

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

不过有一点需要注意,tuple 是有序的,所以 args 中元素的顺序受到赋值时的影响。如:

In [14]:
def printAll(*args):

    for i in args:

        print (i,end=' ')

    print()
In [15]:
#调用:

printAll(1,2,3)

printAll(3,2,1)
1 2 3 
3 2 1 

输出:

1 2 3

3 2 1

虽然3个参数在总体上是相同的,但由于调用的顺序不一样,结果也是不同的。

还有一种参数传递方式,既可以按参数名传递参数,不受位置的限制,又可以像 tuple 传递一样不受数量限制。这个我将在下次课中做介绍。

今天来说说最为灵活的一种参数传递方式:

func(**kargs)

上次说的 func(*args) 方式是把参数作为 tuple 传入函数内部。而 func(**kargs) 则是把参数以键值对字典的形式传入。

示例:

In [16]:
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
b : 2
c : 3
x : 4
y : 5

输出:

a : 1

c : 3

b : 2

y : 5

x : 4

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

当然,这还不够。Python 的函数调用方式非常灵活,前面所说的几种参数调用方式,可以混合在一起使用。看下面这个例子:

In [18]:
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}

输出:

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 表达式

Python 是一门简洁的语言,lambda 表达式则充分体现了 Python 这一特点。

lambda 表达可以被看做是一种匿名函数。它可以让你快速定义一个极度简单的单行函数。譬如这样一个实现三个数相加的函数:

In [19]:
def sum(a, b, c):

    return a + b + c



print (sum(1, 2, 3))

print (sum(4, 5, 6))
6
15
In [20]:
# 如果使用 lambda 表达式来实现:

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

print (sum(1, 2, 3))

print (sum(4, 5, 6))
6
15

两种方法的结果是相同的。

lambda 表达式的语法格式:

lambda 参数列表: 表达式

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

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

lambda 表达式创建了一个函数对象,可以把这个对象赋值给一个变量进行调用,就像上面的例子中一样。

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

In [21]:
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 表达式其实只是一种编码风格,这种写法更加 pythonic。这并不意味着你一定要使用它。事实上,任何可以使用 lambda 表达式的地方,都可以通过普通的 def 函数定义来替代。在一些需要重复使用同一函数的地方,def 可以避免重复定义函数。况且 def 函数更加通用,某些情况可以带来更好地代码可读性。

而对于像 filter、sort 这种需要内嵌函数的方法,lambda 表达式就会显得比较合适。这个我以后会再单独介绍。

当然对于初学者来说,了解 lambda 表达式还有一个重要作用就是,看懂别人写的代码。

变量的作用域

在写代码的时候,免不了要使用变量。但程序中的一个变量并不一定是在哪里都可以被使用,根据情况不同,会有不同的“有效范围”。看这样一段代码:

In [22]:
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

变量 x 在函数内部被重新赋值。但在调用了函数之后,x 的值仍然是50。为什么?

这就得说一下变量的“作用域”:

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

要注意的是,作用域是从变量被定义的位置开始。像这样的写法是有问题的:

In [24]:
def func():

    print (y)

    y = 2

    print (y)

func()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
 in ()
      7     print (y)
      8 
----> 9 func()

 in func()
      1 def func():
      2 
----> 3     print (y)
      4 
      5     y = 2

UnboundLocalError: local variable 'y' referenced before assignment

因为在 y = 2 之前,y 并不存在,调用 y 的值就会出错。

回到开始那个例子:

在函数 func 外部,定义的变量 x,赋值为 50,作为参数传给了函数 func。而在函数 func 内部,变量 x 是形参,它的作用域是整个函数体内部。它与外面的那个 x 没有关系。只不过它的初始值是由外面那个 x 传递过来的。

所以,虽然函数体内部的 x 被重新赋值为 2,也不会影响外面那个 x 的值。

不过有时候,我们希望能够在函数内部去改变一些变量的值,并且这些变量在函数外部同样被使用到。怎么办?

一种方法是,用 return 把改变后的变量值作为函数返回值传递出来,赋值给对应的变量。比如开始的那个例子,可以在函数结尾加上

return x

然后把调用改为

x = func(x)

还有一种方法,就是使用“全局变量”。

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

In [25]:
#通过 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

函数 func 不再提供参数调用。而是通过 global x 告诉程序:这个 x 是一个全局变量。于是函数中的 x 和外部的 x 就成为了同一个变量。这一次,当 x 在函数 func 内部被重新赋值后,外部的 x 也随之改变。

前面讲的局部变量和全局变量是 Python 中函数作用域最基本的情况。实际上,还有一些略复杂的情况,比如:

In [26]:
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 成为一个局部变量,它的作用域从定义处开始,到函数体末尾结束。

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

map 函数

  • python2 和 python3 中 map返回值得类型不一样

来看两个问题:

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

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

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

In [27]:
lst_1 = [1,2,3,4,5,6]

lst_2 = []

for item in lst_1:

    lst_2.append(item * 2)

print (lst_2)
[2, 4, 6, 8, 10, 12]
In [29]:
#Python 程序员大概会这么写:

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

lst_2 = [i * 2 for i in lst_1]

print (lst_2)
[2, 4, 6, 8, 10, 12]

这是我在《【Python 第66课】列表综合》里说到的方法,微信中回复 66 可以查看。

今天来说另一种 Python 程序员常用的写法 -- map:

In [53]:
lst_1 = [1,2,3,4,5,6]

def double_func(x):

    return x * 2

lst_2 = map(double_func, lst_1)

print (list(lst_2))
[2, 4, 6, 8, 10, 12]

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

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

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

In [52]:
lst_1 = (1,2,3,4,5,6)

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

print (list(lst_2))
[2, 4, 6, 8, 10, 12]

这里原数据改为了元组,函数用 lambda 表达式替代。可参考《【Python 第70课】lambda 表达式》,微信中回复 70。

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

In [51]:
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 (list(lst_3))
[2, 5, 8, 11, 14, 17]

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

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

此外,当 map 中的函数为 None 时,结果将会直接返回参数组成的列表。如果只有一组序列,会返回元素相同的列表,如果有多组数列,将会返回每组数列中,对应元素构成的元组所组成的列表。听上去很绕口是不是……代码试试看就明白了:

In [50]:
def f(x, y):
    return (x, y)

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

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

lst_3 = list(map(f, lst_1, lst_2))

print (lst_3)
[(1, 1), (2, 3), (3, 5), (4, 7), (5, 9), (6, 11)]
In [40]:
def f(x, y): 
    return (x, y)
l1 = [ 0, 1, 2, 3, 4, 5, 6 ]
l2 = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
list(map(f, l1, l2))
Out[40]:
[(0, 'Sun'),
 (1, 'Mon'),
 (2, 'Tue'),
 (3, 'Wed'),
 (4, 'Thu'),
 (5, 'Fri'),
 (6, 'Sat')]

reduce 函数

  • python3中需要 from functools import reduce

上次说了 Python 中一个比较有意思的内置函数 map,今天再来介绍另一个类似的函数:reduce

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

上栗子。以前我们给过一个习题,求1累加到100的和。寻常的做法大概是这样:

In [56]:
sum = 0

for i in range(1, 101):

    sum += i

print (sum)
5050
In [59]:
#如果用 reduce 函数,就可以写成:
from functools import reduce

lst = range(1, 101)

def add(x, y):

    return x + y

print (reduce(add, lst))
5050

解释一下:

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 做到),可以选择使用 reduce 来实现。相对可以使代码更简洁。

我觉得,写代码的可读性是很重要的事情,简洁易懂的代码,既容易让别人看懂,也便于自己以后的维护。同时,较少的代码也意味着比较高的开发效率和较少的出错可能。应尽量避免写混乱冗长的代码。当然,也不用为了一味追求代码的精简,总是想方设法把代码写在一行里。那就又走了另一个极端,同样也缺乏可读性。而至于是否使用类似 map、reduce 这样的方法,也是根据需要和个人习惯,我认为并没有一定的规则限制。

多线程

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

Python3 多线程 http://www.runoob.com/python3/python3-multithreading.html

In [62]:
import urllib3, time


import urllib
time_start = time.time()

data = []

for i in range(30):

    print ('request movie:', i)

    id = 1764796 + i
    
    http = urllib3.PoolManager()
    
    r = http.request('url','https://api.douban.com/v2/movie/subject/%d' % id)

    data.append(r)

    print (i, time.time() - time_start)



print ('data:', len(data))
request movie: 0
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
0 1.0452296733856201
request movie: 1
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
1 1.4376976490020752
request movie: 2
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
2 2.036301851272583
request movie: 3
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
3 2.424480676651001
request movie: 4
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
4 3.1788370609283447
request movie: 5
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
5 3.515458583831787
request movie: 6
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
6 3.929321050643921
request movie: 7
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
7 4.4893105030059814
request movie: 8
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
8 4.876185178756714
request movie: 9
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
9 5.189667701721191
request movie: 10
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
10 5.549666404724121
request movie: 11
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
11 5.911073446273804
request movie: 12
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
12 6.593060493469238
request movie: 13
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
13 6.948549032211304
request movie: 14
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
14 7.268184423446655
request movie: 15
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
15 7.610193967819214
request movie: 16
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
16 8.079214572906494
request movie: 17
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
17 8.54995846748352
request movie: 18
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
18 8.875677585601807
request movie: 19
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
19 9.214396715164185
request movie: 20
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
20 9.786171436309814
request movie: 21
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
21 10.36324691772461
request movie: 22
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
22 10.938879013061523
request movie: 23
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
23 11.297513961791992
request movie: 24
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
24 11.634414911270142
request movie: 25
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
25 11.952378273010254
request movie: 26
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
26 12.278428792953491
request movie: 27
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
27 12.637410163879395
request movie: 28
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
28 12.983117580413818
request movie: 29
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
29 13.632150173187256
data: 30

程序里用了 time.time() 来计算抓取花费的时间。运行一遍,大约需要十几秒(根据网络情况会有差异)。

如果我们想用这套代码抓取几万部电影,就算中间不出什么状况,估计也得花上好几个小时。

然而想一下,我们抓一部电影信息的过程是独立,并不依赖于其他电影的结果。因此没必要排好队一部一部地按顺序来。那么有没有什么办法可以同时抓取好几部电影?

答案就是:多线程。

来说一种简单的多线程方法:

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

start_new_thread(function, args[, kwargs])

function 是开发者定义的线程函数,

args 是传递给线程函数的参数,必须是tuple类型,

kwargs 是可选参数。

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

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

In [65]:
import urllib3, time, _thread

def get_content(i):

    id = 1764796 + i
    
    http = urllib3.PoolManager()
    
    r = http.request('url','https://api.douban.com/v2/movie/subject/%d' % id)

    data.append(r)

    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,))



input('press ENTER to exit...\n')
request movie: 0
request movie: 1
request movie: 2
request movie: 3
request movie: 4
request movie: 5
request movie: 6
request movie: 7
request movie: 8
request movie: 9
request movie: 10
request movie: 11
request movie: 12
request movie: 13
request movie: 14
request movie: 15
request movie: 16
request movie: 17
request movie: 18
request movie: 19
request movie: 20
request movie: 21
request movie: 22
request movie: 23
request movie: 24
request movie: 25
request movie: 26
request movie: 27
request movie: 28
request movie: 29
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
5 0.7017581462860107
data: 1
10 0.7559306621551514
data: 2
24 0.7629213333129883
data: 3
1 0.7649266719818115
data: 4
6 0.7935028076171875
data: 5
14 0.803107500076294
data: 6
23 0.8040831089019775
data: 7
15 0.8114683628082275
data: 8
253 0.8258225917816162
data: 10 0.826324462890625
data: 10

261328 0.8268251419067383
data: 13
 0.8273270130157471
data: 13
 0.8268251419067383
data: 13
811 0.845090389251709
data: 15
 0.8446097373962402
data: 15
1718 16 0.883171558380127
data: 0.8815431594848633
data: 18
18
 0.880507230758667
data: 18
21 0.9177229404449463
data: 19
2 0.9450147151947021
data: 20
4 0.9600613117218018
data: 21
0 0.9701051712036133
data: 22
20 0.9744563102722168
data: 23
27 0.9754600524902344
data: 24
79 1.0218162536621094
data: 26
 1.0213446617126465
data: 26
19 1.0371334552764893
data: 27
12 1.0552606582641602
data: 28
29 1.0806910991668701
data: 29
22 1.1721751689910889
data: 30
press ENTER to exit...

F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
Out[65]:
''

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

从输出结果可以看出:

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

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

总共用时两秒多

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

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

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

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

一个很弱智的猜数字小游戏

In [1]:
from random import randint

num = randint(1, 100)
print('guess what I think?')
answer = int(input())
while(answer!=num):
    if answer<num:
        print('too small?')
    elif answer>num:
        print('too big?')
    elif answer==num:
        break
    answer = int(input())
print('equal')
guess what I think?
50
too big?
25
too big?
10
too small?
15
too small?
17
too small?
19
equal

range 列表

In [2]:
sum = 0
for _ in range(1,101) :
    sum = sum + _
print(sum)
5050

print的简单用法

In [3]:
print("this is the\
 same line")

print('''
"What's your name?" I asked.
"I'm Han Meimei."
''')

num = 18
print ('My age is %d' % num)
print ('Price is %.2f' % 4.6765)
this is the same line

"What's your name?" I asked.
"I'm Han Meimei."

My age is 18
Price is 4.68

for嵌套

In [4]:
for i in range(0, 5):
    for j in range(0, i+1):
        print ('*',end=' ')
    print('\t')
* 	
* * 	
* * * 	
* * * * 	
* * * * * 	

字符串拼接 bool值

In [5]:
print ('%s is easy to %d' % ('Python',100))
bool(None)
Python is easy to 100
Out[5]:
False

函数定义

In [6]:
def sayHello(someone):
   print( someone + ' say hello world!')

sayHello('bzz')

def plus(num1, num2):
   print (num1+num2)

plus(1,5)
bzz say hello world!
6

函数实现弱智小游戏

In [7]:
def isEqual(num1, num2):
    if num1<num2:
        print ('too small')
        return False;
    elif num1>num2:
        print ('too big')
        return False;
    else :
        print ('bingo')
        return True

from random import randint

num = randint(1, 100)
print ('Guess what I think?')
bingo = False
while bingo == False:
    answer = int(input())
    bingo = isEqual(answer, num)
Guess what I think?
50
too small
80
too big
70
too big
65
too big
63
too big
55
too small
60
too big
58
too big
57
bingo

print(list(range(1,10))) 可以在python3shell下通过,但无法在jupyter notebook 中通过,目前无解

In [8]:
print(list(range(1,10)))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

调用numpy 显示list

In [9]:
import numpy as np
a=np.arange(1,10)
print(a)
[1 2 3 4 5 6 7 8 9]

操作list

In [10]:
l = [365, 'everyday', 0.618, True]
print (l[1])
#修改list中的元素
l[0] = 123
print (l)
#向list中添加元素
l.append(1024)
print (l)
#删除list中的元素
del l[0]
print (l)
everyday
[123, 'everyday', 0.618, True]
[123, 'everyday', 0.618, True, 1024]
['everyday', 0.618, True, 1024]

点球游戏1

In [11]:
from random import choice
print ('Choose one side to shoot:')
print ('left, center, right')
you = input()
print ('You kicked ' + you)
direction = ['left', 'center', 'right']
com = choice(direction)
print ('Computer saved :' + com)
if you != com:
   print ('Goal!')
else:
   print ('Oops...')
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved :right
Goal!

list切片

  • list有两类常用操作:索引(index)和切片(slice)。
  • 上是索引,现使用切片
  • 切片操作符是在[]内提供一对可选数字,用 : 分割。冒号前的数表示切片的开始位置,冒号后的数字表示切片到哪里结束。同样,计数从0开始。 注意,开始位置包含在切片中,而结束位置不包括。

切片的语法表达式为:[start_index : end_index : step],其中: start_index表示起始索引end_index表示结束索引 step表示步长,步长不能为0,且默认值为1 切片操作是指按照步长,截取从起始索引到结束索引,但不包含结束索引(也就是结束索引减1)的所有元素。

python3支持切片操作的数据类型有list、tuple、string、unicode、range 切片返回的结果类型与原对象类型一致 切片不会改变原对象,而是重新生成了一个新的对象

In [12]:
l = ['a', 'b', 'c', 'd', 'e', 'f']
print(l[-1]) # "-" 表示倒数
print(l[-4])
print(l[0:4])

print(l[1:])
print(l[:3])
print(l[:])
print(l[1:-1])
f
c
['a', 'b', 'c', 'd']
['b', 'c', 'd', 'e', 'f']
['a', 'b', 'c']
['a', 'b', 'c', 'd', 'e', 'f']
['b', 'c', 'd', 'e']

点球游戏2

In [13]:
from random import choice

score_you = 0
score_com = 0
direction = ['left', 'center', 'right']

for i in range(5):
    print ('==== Round %d - You Kick! ====' % (i+1))
    print ('选一边射门')#Choose one side to shoot:
    print ('left, center, right')
    you = input()
    print ('You kicked ' + you)
    com = choice(direction)
    print ('Computer saved ' + com)
    if you != com:
        print ('Goal!')
        score_you += 1
    else:
        print ('Oops...')
        print ('Score: %d(you) - %d(com)\n' % (score_you, score_com))

    print ('==== Round %d - You Save! ====' % (i+1))
    print ('选一边守门:')
    print ('left, center, right')
    you = input()
    print ('You saved ' + you)
    com = choice(direction)
    print ('Computer kicked ' + com)
    if you == com:
        print ('Saved!')
    else:
        print ('Oops...')
        score_com += 1
    print ('Score: %d(you) - %d(com)\n' % (score_you, score_com))
==== Round 1 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved right
Goal!
==== Round 1 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked center
Oops...
Score: 1(you) - 1(com)

==== Round 2 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved center
Goal!
==== Round 2 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked left
Saved!
Score: 2(you) - 1(com)

==== Round 3 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved right
Goal!
==== Round 3 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked center
Oops...
Score: 3(you) - 2(com)

==== Round 4 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 3(you) - 2(com)

==== Round 4 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked center
Oops...
Score: 3(you) - 3(com)

==== Round 5 - You Kick! ====
选一边射门
left, center, right
left
You kicked left
Computer saved right
Goal!
==== Round 5 - You Save! ====
选一边守门:
left, center, right
left
You saved left
Computer kicked left
Saved!
Score: 4(you) - 3(com)

字符串的分割

In [14]:
sentence = 'I am an Englist sentence'
print(sentence.split())

# 指定参数分割 
section = 'Hi! I am the one! Bye!'
print(section.split('!')) # ! 所有都被分割 空格也是

print('_a!a a .'.split('a'))
['I', 'am', 'an', 'Englist', 'sentence']
['Hi', ' I am the one', ' Bye', '']
['_', '!', ' ', ' .']

点球游戏 加上胜负判断

In [15]:
from random import choice

score = [0, 0]
direction = ['left', 'center', 'right']

def kick():
    print ('==== You Kick! ====')
    print ('Choose one side to shoot:')
    print ('left, center, right')
    you = input()
    print ('You kicked ' + you)
    com = choice(direction)
    print ('Computer saved ' + com)
    if you != com:
        print ('Goal!')
        score[0] += 1
    else:
        print ('Oops...')
    print ('Score: %d(you) - %d(com)\n' % (score[0], score[1]))
 
    print ('==== You Save! ====')
    print ('Choose one side to save:')
    print ('left, center, right')
    you = input()
    print ('You saved ' + you)
    com = choice(direction)
    print ('Computer kicked ' + com)
    if you == com:
        print ('Saved!')
    else:
        print ('Oops...')
        score[1] += 1
    print ('Score: %d(you) - %d(com)\n' % (score[0], score[1]))

for i in range(5):
    print ('==== Round %d ====' % (i+1))
    kick()

while(score[0] == score[1]):
    i += 1
    print ('==== Round %d ====' % (i+1))
    kick()

if score[0] > score[1]:
    print( 'You Win!')
else:
    print ('You Lose.')
==== Round 1 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 0(you) - 0(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 0(you) - 1(com)

==== Round 2 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved right
Goal!
Score: 1(you) - 1(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 1(you) - 2(com)

==== Round 3 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 1(you) - 2(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 1(you) - 3(com)

==== Round 4 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved left
Oops...
Score: 1(you) - 3(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked left
Saved!
Score: 1(you) - 3(com)

==== Round 5 ====
==== You Kick! ====
Choose one side to shoot:
left, center, right
left
You kicked left
Computer saved center
Goal!
Score: 2(you) - 3(com)

==== You Save! ====
Choose one side to save:
left, center, right
left
You saved left
Computer kicked right
Oops...
Score: 2(you) - 4(com)

You Lose.

连接list

  • join和昨天说的split正好相反:split是把一个字符串分割成很多字符串组成的list,而join则是把一个list中的所有字符串连接成一个字符串。
  • join的格式有些奇怪,它不是list的方法,而是字符串的方法。首先你需要有一个字符串作为list中所有元素的连接符,然后再调用这个连接符的join方法,join的参数是被连接的list:
In [16]:
s = ';'
li = ['apple', 'pear', 'orange']
fruit = s.join(li)
print (fruit)
print(';'.join(['apple', 'pear', 'orange']))
print(''.join(['hello', 'world'])) # 无缝连接
apple;pear;orange
apple;pear;orange
helloworld

字符串的索引和切片

join方法也可以对字符串使用,作用就是用连接符把字符串中的每个字符重新连接成一个新字符串

In [17]:
word = 'helloworld'
for c in word:
    print (c,end='.')
print('\t')
print('0.1.2.3.4.5.6.7.8.9.')
print (word[0])
print (word[-2])

print (word[5:7])
print (word[:-5])
print (word[:])

newword = ','.join(word)
print(newword)
h.e.l.l.o.w.o.r.l.d.	
0.1.2.3.4.5.6.7.8.9.
h
l
wo
hello
helloworld
h,e,l,l,o,w,o,r,l,d

读文件

In [18]:
f = open('data.txt')
data1 = f.read()
f = open('data.txt')
data2 = f.readline()
data3 = f.readlines()
print(data1)
print('--------------')
print(data2)
print('--------------')
print(data3)
f.close #释放资源
sfd
affffffff
~?!
--------------
sfd

--------------
['affffffff\n', '~?!']
Out[18]:
In [21]:
f = open('output.txt')#, 'w'

data1 = f.read()
f = open('data.txt')
data2 = f.readline()
data3 = f.readlines()
print(data1)
print('--------------')
print(data2)
print('--------------')
print(data3)
f.close #释放资源
50 50 50

--------------
sfd

--------------
['affffffff\n', '~?!']
Out[21]:

处理文件中的数据

In [22]:
f = open('scores.txt')
lines = f.readlines()
f.close()
print(lines)
['刘备 23 35 44 47 51\n', '关羽 60 77 68\n', '张飞 97 99 89 91\n', '诸葛亮 100']
In [23]:
import os  # 调用os后, f = open('results.txt','w') , 有此文件则打开,否则新建

results = []
for line in lines:
    data = line.split()
    #print(data)
    sum = 0
    for score in data[1:]:
        sum += int(score)
    result = '%s\t: %d\n' % (data[0], sum)
    results.append(result)
    
f = open('result.txt','w')

f.writelines(results)
f.close()

break

如果在循环条件仍然满足或序列没有遍历完的时候,想要强行跳出循环,就需要用到break语句。

In [24]:
for i in range(10):
    a = input()
    if a == 'EOF':
        break
2154
EOF

continue

break是彻底地跳出循环,而continue只是略过本次循环的余下内容,直接进入下一次循环

In [25]:
f = open('scores.txt')
lines = f.readlines()
f.close()
print(lines)

import os  # 调用os后, f = open('results.txt','w') , 有此文件则打开,否则新建

results = []
for line in lines:
    data = line.split()
    #print(data)
    sum = 0
    for score in data[1:]:
        point = int(score)
        if point < 60:
            continue
        sum += point
    result = '%s\t: %d\n' % (data[0], sum)
    results.append(result)
    
f = open('result.txt','w')

f.writelines(results)
f.close()
['刘备 23 35 44 47 51\n', '关羽 60 77 68\n', '张飞 97 99 89 91\n', '诸葛亮 100']

异常处理

In [26]:
#print (int('0.5'))

import os
try:
    #f = open('non-exist.txt')
    f = open('scores.txt')
    lines = f.readlines()
    f.close()
    print("Open success!",end = '  ')
    print(lines)
except:
    print( 'File not exists.')
print( 'Done')
Open success!  ['刘备 23 35 44 47 51\n', '关羽 60 77 68\n', '张飞 97 99 89 91\n', '诸葛亮 100']
Done

查天气 网络模块

In [7]:
import urllib3
http = urllib3.PoolManager()
r = http.request('GET','http://www.baidu.com')
print(r.status)
print(r.data)
200
b'\r\n\r\n\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\xe7\x99\xbe\xe5\xba\xa6\xe4\xb8\x80\xe4\xb8\x8b\xef\xbc\x8c\xe4\xbd\xa0\xe5\xb0\xb1\xe7\x9f\xa5\xe9\x81\x93\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\t\r\n\r\n
\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n'

面向对象

In [4]:
s = 'how are you'
#s被赋值后就是一个字符串类型的对象
l = s.split()
#split是字符串的方法,这个方法返回一个list类型的对象
#l是一个list类型的对象
#通过dir()方法可以查看一个类/变量的所有属性:
print(dir(s))
print('---------')
print(dir(list))
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
---------
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
  • 关键字class加上类名用来创建一个类。之后缩进的代码块是这个类的内部。在这里,我们用pass语句,表示一个空的代码块
In [6]:
class MyClass:
    print('bzz')
    pass

mc = MyClass()
print (mc)

#mc是__main__模块中MyClass来的一个实例(instance)
bzz
<__main__.MyClass object at 0x0000025C1069EF98>
In [12]:
#  给这个类加上一些域:
#  给MyClass类增加了一个类变量name,并把它的值设为'Sam'。然后又增加了一个类方法sayHi
#  调用类变量的方法是“对象.变量名”。你可以得到它的值,也可以改变它的值。

#  类方法和我们之前定义的函数区别在于,第一个参数必须为self。
#  而在调用类方法的时候,通过“对象.方法名()”格式进行调用,
#  而不需要额外提供self这个参数的值。self在类方法中的值,就是你调用的这个对象本身
class MyClass:
    name = 'Sam'
    def sayHi(self):
        print ('Hello %s' % self.name)

mc = MyClass()
print (mc.name)
MyClass.name = 'Mike'
print(MyClass().name)

mc.name = 'Lily'
mc.sayHi()
Sam
Mike
Hello Lily
  • 假设我们有一辆汽车,我们知道它的速度(60km/h),以及A、B两地的距离(100km)。要算出开着这辆车从A地到B地花费的时间。
In [13]:
#面向过程的方法:
speed = 60.0
distance = 100.0
time = distance / speed
print (time)

#面向对象的方法:
class Car:
    speed = 0
    def drive(self, distance):
        time = distance / self.speed
        print (time)

car = Car()
car.speed = 60.0
car.drive(100.0)
1.6666666666666667
1.6666666666666667
  • 如果我们让题目再稍稍复杂一点。假设我们又有了一辆更好的跑车,它的速度是150km/h,然后我们除了想从A到B,还要从B到C(距离200km)。要求分别知道这两种车在这两段路上需要多少时间。
In [16]:
#面向过程的方法:
speed1 = 60.0
distance1 = 100.0
time1 = distance1 / speed1
print (time1)
distance2 = 200.0
time2 = distance2 / speed1
print (time2)
speed2 = 150.0
time3 = distance1 / speed2
print (time3)
time4 = distance2 / speed2
print (time4)
print('--------------')
#面向对象的方法:
class Car:
    speed = 0
    def drive(self, distance):
        time = distance / self.speed
        print (time)
        
car1 = Car()
car1.speed = 60.0
car1.drive(100.0)
car1.drive(200.0)
car2 = Car()
car2.speed = 150.0
car2.drive(100.0)
car2.drive(200.0)
1.6666666666666667
3.3333333333333335
0.6666666666666666
1.3333333333333333
--------------
1.6666666666666667
3.3333333333333335
0.6666666666666666
1.3333333333333333

仍然是从A地到B地,这次除了有汽车,我们还有了一辆自行车! 自行车和汽车有着相同的属性:速度(speed)。还有一个相同的方法(drive),来输出行驶/骑行一段距离所花的时间。但这次我们要给汽车增加一个属性:每公里油耗(fuel)。而在汽车行驶一段距离的方法中,除了要输出所花的时间外,还要输出所需的油量。

面向过程的方法,你可能需要写两个函数,然后把数据作为参数传递进去,在调用的时候要搞清应该使用哪个函数和哪些数据。有了面向对象,你可以把相关的数据和方法封装在一起,并且可以把不同类中的相同功能整合起来。这就需要用到面向对象中的另一个重要概念:继承。

我们要使用的方法是,创建一个叫做Vehicle的类,表示某种车,它包含了汽车和自行车所共有的东西:速度,行驶的方法。然后让Car类和Bike类都继承这个Vehicle类,即作为它的子类。在每个子类中,可以分别添加各自独有的属性。

In [20]:
# Vehicle类被称为基本类或超类,Car类和Bike类被成为导出类或子类
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)
c.drive(100)

'''
解释一下代码:

__init__函数会在类被创建的时候自动调用,用来初始化类。它的参数,要在创建类的时候提供。
于是我们通过提供一个数值来初始化speed的值。

注意:__init__是python的内置方法,类似的函数名前后是两个下英文划线,如果写错了,
则不会起到原本应有的作用。

class定义后面的括号里表示这个类继承于哪个类。
Bike(Vehicle)就是说Bike是继承自Vehicle中的子类。Vehicle中的属性和方法,Bike都会有。
因为Bike不需要有额外的功能,所以用pass在类中保留空块,什么都不用写。

Car类中,我们又重新定义了__init__和drive函数,这样会覆盖掉它继承自Vehicle的同名函数。
但我们依然可以通过“Vehicle.函数名”来调用它的超类方法。
以此来获得它作为Vehicle所具有的功能。注意,因为是通过类名调用方法,
而不是像之前一样通过对象来调用,所以这里必须提供self的参数值。
在调用超类的方法之后,我们又给Car增加了一个fuel属性,并且在drive中多输出一行信息。


'''
need 6.666667 hour(s)
need 1.250000 hour(s)
need 1.200000 fuels
Out[20]:
'\n解释一下代码:\n\n__init__函数会在类被创建的时候自动调用,用来初始化类。它的参数,要在创建类的时候提供。\n于是我们通过提供一个数值来初始化speed的值。\n\n注意:__init__是python的内置方法,类似的函数名前后是两个下英文划线,如果写错了,\n则不会起到原本应有的作用。\n\nclass定义后面的括号里表示这个类继承于哪个类。\nBike(Vehicle)就是说Bike是继承自Vehicle中的子类。Vehicle中的属性和方法,Bike都会有。\n因为Bike不需要有额外的功能,所以用pass在类中保留空块,什么都不用写。\n\nCar类中,我们又重新定义了__init__和drive函数,这样会覆盖掉它继承自Vehicle的同名函数。\n但我们依然可以通过“Vehicle.函数名”来调用它的超类方法。\n以此来获得它作为Vehicle所具有的功能。注意,因为是通过类名调用方法,\n而不是像之前一样通过对象来调用,所以这里必须提供self的参数值。\n在调用超类的方法之后,我们又给Car增加了一个fuel属性,并且在drive中多输出一行信息。\n\n\n'

and-or

In [21]:
a = "heaven"
b = "hell"
c = True and a or b
print (c)
d = False and a or b
print (d)
heaven
hell

结果很奇怪是不是?

表达式从左往右运算,1和"heaven"做and的结果是"heaven",再与"hell"做or的结果是"heaven";0和"heaven"做and的结果是0,再与"hell"做or的结果是"hell"。

抛开绕人的and和or的逻辑,你只需记住,在一个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会让你的代码更简单。但如果你觉得这个技巧带来的副作用已经让你头大了,没关系,用if-else可以做相同的事情。不过在python的某些情况下,你可能没法使用if语句,比如lambda函数中,这时候你可能就需要and-or的帮助了。

什么是lambda函数?呵呵,这是python的高阶玩法,暂且按住不表,以后有机会再说。

In [25]:
a = ""
b = "hell"
c = (True and [a] or [b])[0]
print (c)
 
         

元组

In [31]:
#上一次pygame的课中有这样一行代码:
#x, y = pygame.mouse.get_pos()

# 元组(tuple)也是一种序列,和我们用了很多次的list类似,
# 只是元组中的元素在创建之后就不能被修改。
# 如:

postion = (1, 2)
geeks = ('Sheldon', 'Leonard', 'Rajesh', 'Howard')

# 都是元组的实例。它有和list同样的索引、切片、遍历等操作(参见25~27课):
print (postion[0])
print('------------')

for _ in geeks:
    print (_)
print('------------')

print (geeks[1:3])

#其实我们之前一直在用元组,就是在print语句中:
print('------------')
print ('%s is %d years old' % ('Mike', 23))

#('Mike', 23)就是一个元组。这是元组最常见的用处。

#再来看一下元组作为函数返回值的例子:

def get_pos(n):
    return (n/2, n*2)

#得到这个函数的返回值有两种形式,一种是根据返回值元组中元素的个数提供变量:

x, y = get_pos(50)
print (x)
print (y)

print('------------')
#这就是我们在开头那句代码中使用的方式。
#还有一种方法是用一个变量记录返回的元组:

pos = get_pos(50)
print (pos[0])
print (pos[1])
1
------------
Sheldon
Leonard
Rajesh
Howard
------------
('Leonard', 'Rajesh')
------------
Mike is 23 years old
25.0
100
------------
25.0
100

数学运算

In [40]:
import math

print(math.pi)
print(math.e)

print(math.ceil(5.4))
print(math.floor(5.4))

print(math.pow(3,2))
print(math.log(4))
print(math.log(100,10))

print(math.sqrt(9))
print(math.fabs(-34))

#math.sin .cos .tan .asin .acos .atan    弧度单位

#math.degrees(x) 弧度转角度
#math.radians(x) 角度转弧度
3.141592653589793
2.718281828459045
6
5
9.0
1.3862943611198906
2.0
3.0
34.0

正则表达式

什么是正则表达式?在回答这个问题之前,先来看看为什么要有正则表达式。

在编程处理文本的过程中,经常会需要按照某种规则去查找一些特定的字符串。比如知道一个网页上的图片都是叫做'image/8554278135.jpg'之类的名字,只是那串数字不一样;又或者在一堆人员电子档案中,你要把他们的电话号码全部找出来,整理成通讯录。诸如此类工作,如果手工去做,当量大的时候那简直就是悲剧。但你知道这些字符信息有一定的规律,可不可以利用这些规律,让程序自动来做这些无聊的事情?答案是肯定的。这时候,你就需要一种描述这些规律的方法,正则表达式就是干这事的。

正则表达式就是记录文本规则的代码。

所以正则表达式并不是python中特有的功能,它是一种通用的方法。python中的正则表达式库,所做的事情是利用正则表达式来搜索文本。要使用它,你必须会自己用正则表达式来描述文本规则。之前多次有同学表示查找文本的事情经常会遇上,希望能介绍一下正则表达式。既然如此,我们就从正则表达式的基本规则开始说起。

1.

首先说一种最简单的正则表达式,它没有特殊的符号,只有基本的字母或数字。它满足的匹配规则就是完全匹配。例如:有个正则表达式是“hi”,那么它就可以匹配出文本中所有含有hi的字符。

来看如下的一段文字:

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

如果我们用“hi”这个正则表达式去匹配这段文字,将会得到两个结果。因为是完全匹配,所以每个结果都是“hi”。这两个“hi”分别来自“Shirley”和“his”。默认情况下正则表达式是严格区分大小写的,所以“Hi”和“Hilton”中的“Hi”被忽略了。

为了验证正则表达式匹配的结果,你可以用以下这段代码做实验:

In [6]:
import re

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

m = re.findall(r"[Hh]i", text)

if m:
    print (m)
else:
    print ('not match')
['Hi', 'hi', 'Hi', 'hi', 'hi']

暂时先不解释这其中代码的具体含义,你只要去更改text和findall中的字符串,就可以用它来检测正则表达式的实际效果。

2.

如果我们只想找到“hi”这个单词,而不把包含它的单词也算在内,那就可以使用“\bhi\b”这个正则表达式。在以前的字符串处理中,我们已经见过类似“\n”这种特殊字符。在正则表达式中,这种字符更多,以后足以让你眼花缭乱。

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

在前面那个例子里,“\bhi\b”匹配不到任何结果。但“\bhi”的话就可以匹配到1个“hi”,出自“his”。用这种方法,你可以找出一段话中所有单词“Hi”,想一下要怎么写。

3.

最后再说一下[]这个符号。在正则表达式中,[]表示满足括号中任一字符。比如“[hi]”,它就不是匹配“hi”了,而是匹配“h”或者“i”。

在前面例子中,如果把正则表达式改为“[Hh]i”,就可以既匹配“Hi”,又匹配“hi”了。

1.r"hi"

这里字符串前面加了r,是raw的意思,它表示对字符串不进行转义。为什么要加这个?你可以试试print "\bhi"和r"\bhi"的区别。

print "\bhi"

hi

print r"\bhi"

\bhi

可以看到,不加r的话,\b就没有了。因为python的字符串碰到“\”就会转义它后面的字符。如果你想在字符串里打“\”,则必须要打“\”。

print "\bhi"

\bhi

这样的话,我们的正则表达式里就会多出很多“\”,让本来就已经复杂的字符串混乱得像五仁月饼一般。但加上了“r”,就表示不要去转义字符串中的任何字符,保持它的原样。

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

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

3.今天主要说两个符号“.”和“*”,顺带说下“\S”和“?”。

“.”在正则表达式中表示除换行符以外的任意字符。在上节课提供的那段例子文本中:

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

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

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

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

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

4.在很多搜索中,会用“?”表示任意一个字符, 表示任意数量连续字符,这种被称为通配符。但在正则表达式中,任意字符是用 “ . ” 表示,而 则不是表示字符,而是表示数量:它表示前面的字符可以重复任意多次(包括0次),只要满足这样的条件,都会被表达式匹配上。

结合前面的“.”,用“I.e”去匹配,想一下会得到什么结果?

['I am Shirley Hilton. I am his wife']

是不是跟你想的有些不一样?也许你会以为是

['I am Shirle', 'I am his wife']

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

最后留一道习题:

从下面一段文本中,匹配出所有s开头,e结尾的单词。

site sea sue sweet see case sse ssee loses

先来公布上一课习题的答案:

\bs\S*e\b

有的同学给出的答案是"\bs.*?e\b"。测试一下就会发现,有奇怪的'sea sue'和'sweet see'混进来了。既然是单词,我们就不要空格,所以需要用"\S"而不是"."

昨天有位同学在论坛上说,用正则表达式匹配出了文件中的手机号。这样现学现用很不错。匹配的规则是"1.*?\n",在这个文件的条件下,是可行的。但这规则不够严格,且依赖于手机号结尾有换行符。今天我来讲讲其他的方法。

匹配手机号,其实就是找出一串连续的数字。更进一步,是11位,以1开头的数字。

还记得正则第1讲里提到的[]符号吗?它表示其中任意一个字符。所以要匹配数字,我们可以用

[0123456789]

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

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

\d

要表示任意长度的数字,就可以用

[0-9]*

或者

\d*

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

所以要匹配出所有的数字串,应当用

[0-9]+

或者

\d+

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

\d{11}

想要再把第一位限定为1,就在前面加上1,后面去掉一位:

1\d{10}

OK. 总结一下今天提到的符号:

[0-9]

\d

+

{}

现在你可以去一个混杂着各种数据的文件里,抓出里面的手机号,或是其他你感兴趣的数字了。

1.

我们已经了解了正则表达式中的一些特殊符号,如\b、\d、.、\S等等。这些具有特殊意义的专用字符被称作“元字符”。常用的元字符还有:

\w - 匹配字母或数字或下划线或汉字(我试验下了,发现3.x版本可以匹配汉字,但2.x版本不可以)

\s - 匹配任意的空白符

^ - 匹配字符串的开始

$ - 匹配字符串的结束

2.

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

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

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

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

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

3.

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

? - 重复零次或一次

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

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

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

^\w{4,12}$

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

\d{15,18}

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

^1\d*x?

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

另外再说一下之前提到的转义字符\。如果我们确实要匹配.或者字符本身,而不是要它们所代表的元字符,那就需要用.或\。\本身也需要用\。

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

留一道稍稍有难度的习题:

写一个正则表达式,能匹配出多种格式的电话号码,包括

(021)88776543

010-55667890

02584453362

0571 66345673

来说上次的习题:

(021)88776543

010-55667890

02584453362

0571 66345673

一个可以匹配出所有结果的表达式是

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

解释一下:

(?

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

0\d{2,3}

区号,0xx或者0xxx

[) -]?

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

\d{7,8}

7或8位的电话号码

可是,这个表达式虽然能匹配出所有正确的数据(一般情况下,这样已经足够),但理论上也会匹配到错误的数据。因为()应当是成对出现的,表达式中对于左右两个括号并没有做关联处理,例如(02188776543这样的数据也是符合条件的。

我们可以用正则表达式中的“|”符号解决这种问题。“|”相当于python中“or”的作用,它连接的两个表达式,只要满足其中之一,就会被算作匹配成功。

于是我们可以把()的情况单独分离出来:

(0\d{2,3})\d{7,8}

其他情况:

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

合并:

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

使用“|”时,要特别提醒注意的是不同条件之间的顺序。匹配时,会按照从左往右的顺序,一旦匹配成功就停止验证后面的规则。假设要匹配的电话号码还有可能是任意长度的数字(如一些特殊的服务号码),你应该把

|\d+

这个条件加在表达式的最后。如果放在最前面,某些数据就可能会被优先匹配为这一条件。你可以写个测试用例体会一下两种结果的不同。

关于正则表达式,我们已经讲了5篇,介绍了正则表达式最最皮毛的一些用法。接下来,这个话题要稍稍告一段落。推荐一篇叫做《正则表达式30分钟入门教程》的文章(直接百度一下就能找到,我也会转到论坛上),想要对正则表达式进一步学习的同学可以参考。这篇教程是个标题党,里面涉及了正则表达式较多的内容,30分钟绝对看不完。

random模块的作用是产生随机数。之前的小游戏中用到过random中的randint:

import random

num = random.randint(1,100)

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

a、b都必须是整数,且必须b≥a。当等于的时候,比如:

random.randint(3, 3)

的结果就永远是3

除了randint,random模块中比较常用的方法还有:

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

比如:

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中常见的几个方法。如果你在程序中需要其中某一个方法,也可以这样写:

from random import randint

randint(1, 10)

另外,有些编程基础的同学可能知道,在随机数中有个seed的概念,需要一个真实的随机数,比如此刻的时间、鼠标的位置等等,以此为基础产生伪随机数。在python中,默认用系统时间作为seed。你也可以手动调用random.seed(x)来指定seed。

Python中有一个time模块

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

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

Python中time模块的一个方法

time.time()

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

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

In [9]:
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)) 
start:1530929854.164775
0
1
2
3
4
5
6
7
8
9
end:1530929854.165777
total time:0.001002

顺便再说下time中的另一个很有用的方法:

time.sleep(secs)

它可以让程序暂停secs秒。例如:

In [12]:
import time

print (1)

time.sleep(2)

print (2)
1
2

今天我就来讲讲代码遇到问题时的一些简单处理方法。

  1. 读错误信息

来看如下一个例程:

import random

a = 0

for i in range(5):

b = random.choice(range(5))

a += i / b

print a

这个程序中,i从0循环到4,每次循环中,b是0到4中的一个随机数。把i/b的结果累加到a上,最后输出结果。

运行这段程序,有时候会输出结果,有时候却跳出错误信息:

Traceback (most recent call last):

File "C:\Users\Crossin\Desktop\py\test.py", line 5, in

a += i / b

ZeroDivisionError: integer division or modulo by zero

有些同学看见一段英文提示就慌了。其实没那么复杂,python的错误提示做得还是很标准的。

它告诉我们错误发生在test.py文件中的第6行

a += i / b

这一句上。

这个错误是“ZeroDivisionError”,也就是除零错。

“integer division or modulo by zero”,整数被0除或者被0模(取余数)。

因为0不能作为除数,所以当b随机到0的时候,就会引发这个错误。

知道了原因,就可以顺利地解决掉这个bug。

以后在写代码的时候,如果遇到了错误,先别急着去改代码。试着去读一读错误提示,看看里面都说了些啥。

  1. 输出调试信息

我们在所有课程的最开始就教了输出函数“print”。它是编程中最简单的调试手段。有的时候,仅从错误提示仍然无法判断出程序错误的原因,或者没有发生错误,但程序的结果就是不对。这种情况下,通过输出程序过程中的一些状态,可以帮助分析程序。

把前面那个程序改造一下,加入一些与程序功能无关的输出语句:

import random

a = 0

for i in range(5):

print 'i: %d' % i 

b = random.choice(range(5))

print 'b: %d' % b

a += i / b

print 'a: %d' % a

print

print a

运行后的输出结果(每次结果都会不一样):

i: 0

b: 3

a: 0

i: 1

b: 3

a: 0

i: 2

b: 3

a: 0

i: 3

b: 0

Traceback (most recent call last):

File "C:\Users\Crossin\Desktop\py\test.py", line 7, in

a += i / b

ZeroDivisionError: integer division or modulo by zero

当b的值为0时,发生了除零错。这次可以更清晰地看出程序出错时的状态。

在真实开发中,程序的结构可能会非常复杂。通过输出调试信息,可以有效地缩小范围、定位错误发生的位置,确认错误发生时的场景,进而找出错误原因。

序列化 pickle

  • python3 在字符方面和 python2 区别较大
In [30]:
import pickle

test_data = ['Save me!'.encode('utf8'), 123.456, True]

f = open('test.data', 'wb+')

pickle.dump(test_data, f)

f.close()
In [32]:
import pickle

f = open('test.data','rb')

test_data = pickle.load(f)

f.close()

print (test_data)
[b'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倍)。因此你可以把上述代码中的 pickle 全部替换为 cPickle,从而提高运行速度(尽管在这个小程序中影响微乎其微)。

列表解析(List Comprehension)

In [33]:
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]
In [34]:
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]

函数的参数传递

我们曾经讲过 Python 中函数的参数传递(见第21课)。最基本的方式是:

定义:

def func(arg1, arg2):

print arg1, arg2

调用:

func(3, 7)

我们把函数定义时的参数名(arg1、arg2)称为形参,调用时提供的参数(3、7)称为实参。

这种方式是根据调用时提供参数的位置进行匹配,要求实参与行参的数量相等,默认按位置匹配参数。调用时,少参数或者多参数都会引起错误。这是最常用的一种函数定义方式。

在调用时,也可以根据形参的名称指定实参。如:

func(arg2=3, arg1=7)

但同样,必须提供所有的参数。看看和func(3, 7)的运行结果有什么不同。

Python 语言还提供了其他一些更灵活的参数传递方式,如:

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

func3(*args)

func4(**kargs)

今天我们先说说func2这种方式。

这种方式可以理解为,在一般函数定义的基础上,增加了参数的默认值。这样定义的函数可以和原来一样使用,而当你没有提供足够的参数时,会用默认值作为参数的值。

例如:

定义

In [2]:
def func(arg1=1, arg2=2, arg3=3):

    print (arg1, arg2, arg3)
In [3]:
# 调用:

func(2, 3, 4)

func(5, 6)

func(7)
2 3 4
5 6 3
7 2 3

输出为

2 3 4

5 6 3

7 2 3

提供的参数会按顺序先匹配前面位置的参数,后面未匹配到的参数使用默认值。

也可以指定其中的部分参数,如:

In [4]:
func(arg2=8)

func(arg3=9, arg1=10)
1 8 3
10 2 9

输出为

1 8 3

10 2 9

In [5]:
#或者混合起来用:

func(11, arg3=12)
11 2 12

输出为

11 2 12

但要注意,没有指定参数名的参数必须在所有指定参数名的参数前面,且参数不能重复。以下的调用都是错误的:

func(arg1=13, 14)

func(15, arg1=16)

定义参数默认值的函数可以在调用时更加简洁。大量 Python 模块中的方法都运用了这一方式,让使用者在调用时可以提供尽可能少的参数。

接着上一次的内容,来介绍一种更加灵活的参数传递方式:

def func(*args)

这种方式的厉害之处在于,它可以接受任意数量的参数。来看具体例子:

In [7]:
def calcSum(*args):

    sum = 0

    for i in args:

        sum += i

    print (sum)
In [9]:
# 调用
calcSum(1,2,3)

calcSum(123,456)

calcSum()
6
579
0

输出:

6

579

0

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

不过有一点需要注意,tuple 是有序的,所以 args 中元素的顺序受到赋值时的影响。如:

In [14]:
def printAll(*args):

    for i in args:

        print (i,end=' ')

    print()
In [15]:
#调用:

printAll(1,2,3)

printAll(3,2,1)
1 2 3 
3 2 1 

输出:

1 2 3

3 2 1

虽然3个参数在总体上是相同的,但由于调用的顺序不一样,结果也是不同的。

还有一种参数传递方式,既可以按参数名传递参数,不受位置的限制,又可以像 tuple 传递一样不受数量限制。这个我将在下次课中做介绍。

今天来说说最为灵活的一种参数传递方式:

func(**kargs)

上次说的 func(*args) 方式是把参数作为 tuple 传入函数内部。而 func(**kargs) 则是把参数以键值对字典的形式传入。

示例:

In [16]:
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
b : 2
c : 3
x : 4
y : 5

输出:

a : 1

c : 3

b : 2

y : 5

x : 4

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

当然,这还不够。Python 的函数调用方式非常灵活,前面所说的几种参数调用方式,可以混合在一起使用。看下面这个例子:

In [18]:
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}

输出:

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 表达式

Python 是一门简洁的语言,lambda 表达式则充分体现了 Python 这一特点。

lambda 表达可以被看做是一种匿名函数。它可以让你快速定义一个极度简单的单行函数。譬如这样一个实现三个数相加的函数:

In [19]:
def sum(a, b, c):

    return a + b + c



print (sum(1, 2, 3))

print (sum(4, 5, 6))
6
15
In [20]:
# 如果使用 lambda 表达式来实现:

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

print (sum(1, 2, 3))

print (sum(4, 5, 6))
6
15

两种方法的结果是相同的。

lambda 表达式的语法格式:

lambda 参数列表: 表达式

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

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

lambda 表达式创建了一个函数对象,可以把这个对象赋值给一个变量进行调用,就像上面的例子中一样。

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

In [21]:
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 表达式其实只是一种编码风格,这种写法更加 pythonic。这并不意味着你一定要使用它。事实上,任何可以使用 lambda 表达式的地方,都可以通过普通的 def 函数定义来替代。在一些需要重复使用同一函数的地方,def 可以避免重复定义函数。况且 def 函数更加通用,某些情况可以带来更好地代码可读性。

而对于像 filter、sort 这种需要内嵌函数的方法,lambda 表达式就会显得比较合适。这个我以后会再单独介绍。

当然对于初学者来说,了解 lambda 表达式还有一个重要作用就是,看懂别人写的代码。

变量的作用域

在写代码的时候,免不了要使用变量。但程序中的一个变量并不一定是在哪里都可以被使用,根据情况不同,会有不同的“有效范围”。看这样一段代码:

In [22]:
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

变量 x 在函数内部被重新赋值。但在调用了函数之后,x 的值仍然是50。为什么?

这就得说一下变量的“作用域”:

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

要注意的是,作用域是从变量被定义的位置开始。像这样的写法是有问题的:

In [24]:
def func():

    print (y)

    y = 2

    print (y)

func()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
 in ()
      7     print (y)
      8 
----> 9 func()

 in func()
      1 def func():
      2 
----> 3     print (y)
      4 
      5     y = 2

UnboundLocalError: local variable 'y' referenced before assignment

因为在 y = 2 之前,y 并不存在,调用 y 的值就会出错。

回到开始那个例子:

在函数 func 外部,定义的变量 x,赋值为 50,作为参数传给了函数 func。而在函数 func 内部,变量 x 是形参,它的作用域是整个函数体内部。它与外面的那个 x 没有关系。只不过它的初始值是由外面那个 x 传递过来的。

所以,虽然函数体内部的 x 被重新赋值为 2,也不会影响外面那个 x 的值。

不过有时候,我们希望能够在函数内部去改变一些变量的值,并且这些变量在函数外部同样被使用到。怎么办?

一种方法是,用 return 把改变后的变量值作为函数返回值传递出来,赋值给对应的变量。比如开始的那个例子,可以在函数结尾加上

return x

然后把调用改为

x = func(x)

还有一种方法,就是使用“全局变量”。

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

In [25]:
#通过 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

函数 func 不再提供参数调用。而是通过 global x 告诉程序:这个 x 是一个全局变量。于是函数中的 x 和外部的 x 就成为了同一个变量。这一次,当 x 在函数 func 内部被重新赋值后,外部的 x 也随之改变。

前面讲的局部变量和全局变量是 Python 中函数作用域最基本的情况。实际上,还有一些略复杂的情况,比如:

In [26]:
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 成为一个局部变量,它的作用域从定义处开始,到函数体末尾结束。

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

map 函数

  • python2 和 python3 中 map返回值得类型不一样

来看两个问题:

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

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

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

In [27]:
lst_1 = [1,2,3,4,5,6]

lst_2 = []

for item in lst_1:

    lst_2.append(item * 2)

print (lst_2)
[2, 4, 6, 8, 10, 12]
In [29]:
#Python 程序员大概会这么写:

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

lst_2 = [i * 2 for i in lst_1]

print (lst_2)
[2, 4, 6, 8, 10, 12]

这是我在《【Python 第66课】列表综合》里说到的方法,微信中回复 66 可以查看。

今天来说另一种 Python 程序员常用的写法 -- map:

In [53]:
lst_1 = [1,2,3,4,5,6]

def double_func(x):

    return x * 2

lst_2 = map(double_func, lst_1)

print (list(lst_2))
[2, 4, 6, 8, 10, 12]

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

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

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

In [52]:
lst_1 = (1,2,3,4,5,6)

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

print (list(lst_2))
[2, 4, 6, 8, 10, 12]

这里原数据改为了元组,函数用 lambda 表达式替代。可参考《【Python 第70课】lambda 表达式》,微信中回复 70。

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

In [51]:
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 (list(lst_3))
[2, 5, 8, 11, 14, 17]

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

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

此外,当 map 中的函数为 None 时,结果将会直接返回参数组成的列表。如果只有一组序列,会返回元素相同的列表,如果有多组数列,将会返回每组数列中,对应元素构成的元组所组成的列表。听上去很绕口是不是……代码试试看就明白了:

In [50]:
def f(x, y):
    return (x, y)

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

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

lst_3 = list(map(f, lst_1, lst_2))

print (lst_3)
[(1, 1), (2, 3), (3, 5), (4, 7), (5, 9), (6, 11)]
In [40]:
def f(x, y): 
    return (x, y)
l1 = [ 0, 1, 2, 3, 4, 5, 6 ]
l2 = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
list(map(f, l1, l2))
Out[40]:
[(0, 'Sun'),
 (1, 'Mon'),
 (2, 'Tue'),
 (3, 'Wed'),
 (4, 'Thu'),
 (5, 'Fri'),
 (6, 'Sat')]

reduce 函数

  • python3中需要 from functools import reduce

上次说了 Python 中一个比较有意思的内置函数 map,今天再来介绍另一个类似的函数:reduce

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

上栗子。以前我们给过一个习题,求1累加到100的和。寻常的做法大概是这样:

In [56]:
sum = 0

for i in range(1, 101):

    sum += i

print (sum)
5050
In [59]:
#如果用 reduce 函数,就可以写成:
from functools import reduce

lst = range(1, 101)

def add(x, y):

    return x + y

print (reduce(add, lst))
5050

解释一下:

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 做到),可以选择使用 reduce 来实现。相对可以使代码更简洁。

我觉得,写代码的可读性是很重要的事情,简洁易懂的代码,既容易让别人看懂,也便于自己以后的维护。同时,较少的代码也意味着比较高的开发效率和较少的出错可能。应尽量避免写混乱冗长的代码。当然,也不用为了一味追求代码的精简,总是想方设法把代码写在一行里。那就又走了另一个极端,同样也缺乏可读性。而至于是否使用类似 map、reduce 这样的方法,也是根据需要和个人习惯,我认为并没有一定的规则限制。

多线程

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

Python3 多线程 http://www.runoob.com/python3/python3-multithreading.html

In [62]:
import urllib3, time


import urllib
time_start = time.time()

data = []

for i in range(30):

    print ('request movie:', i)

    id = 1764796 + i
    
    http = urllib3.PoolManager()
    
    r = http.request('url','https://api.douban.com/v2/movie/subject/%d' % id)

    data.append(r)

    print (i, time.time() - time_start)



print ('data:', len(data))
request movie: 0
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
0 1.0452296733856201
request movie: 1
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
1 1.4376976490020752
request movie: 2
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
2 2.036301851272583
request movie: 3
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
3 2.424480676651001
request movie: 4
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
4 3.1788370609283447
request movie: 5
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
5 3.515458583831787
request movie: 6
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
6 3.929321050643921
request movie: 7
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
7 4.4893105030059814
request movie: 8
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
8 4.876185178756714
request movie: 9
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
9 5.189667701721191
request movie: 10
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
10 5.549666404724121
request movie: 11
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
11 5.911073446273804
request movie: 12
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
12 6.593060493469238
request movie: 13
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
13 6.948549032211304
request movie: 14
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
14 7.268184423446655
request movie: 15
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
15 7.610193967819214
request movie: 16
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
16 8.079214572906494
request movie: 17
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
17 8.54995846748352
request movie: 18
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
18 8.875677585601807
request movie: 19
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
19 9.214396715164185
request movie: 20
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
20 9.786171436309814
request movie: 21
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
21 10.36324691772461
request movie: 22
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
22 10.938879013061523
request movie: 23
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
23 11.297513961791992
request movie: 24
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
24 11.634414911270142
request movie: 25
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
25 11.952378273010254
request movie: 26
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
26 12.278428792953491
request movie: 27
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
27 12.637410163879395
request movie: 28
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
28 12.983117580413818
request movie: 29
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
29 13.632150173187256
data: 30

程序里用了 time.time() 来计算抓取花费的时间。运行一遍,大约需要十几秒(根据网络情况会有差异)。

如果我们想用这套代码抓取几万部电影,就算中间不出什么状况,估计也得花上好几个小时。

然而想一下,我们抓一部电影信息的过程是独立,并不依赖于其他电影的结果。因此没必要排好队一部一部地按顺序来。那么有没有什么办法可以同时抓取好几部电影?

答案就是:多线程。

来说一种简单的多线程方法:

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

start_new_thread(function, args[, kwargs])

function 是开发者定义的线程函数,

args 是传递给线程函数的参数,必须是tuple类型,

kwargs 是可选参数。

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

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

In [65]:
import urllib3, time, _thread

def get_content(i):

    id = 1764796 + i
    
    http = urllib3.PoolManager()
    
    r = http.request('url','https://api.douban.com/v2/movie/subject/%d' % id)

    data.append(r)

    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,))



input('press ENTER to exit...\n')
request movie: 0
request movie: 1
request movie: 2
request movie: 3
request movie: 4
request movie: 5
request movie: 6
request movie: 7
request movie: 8
request movie: 9
request movie: 10
request movie: 11
request movie: 12
request movie: 13
request movie: 14
request movie: 15
request movie: 16
request movie: 17
request movie: 18
request movie: 19
request movie: 20
request movie: 21
request movie: 22
request movie: 23
request movie: 24
request movie: 25
request movie: 26
request movie: 27
request movie: 28
request movie: 29
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
5 0.7017581462860107
data: 1
10 0.7559306621551514
data: 2
24 0.7629213333129883
data: 3
1 0.7649266719818115
data: 4
6 0.7935028076171875
data: 5
14 0.803107500076294
data: 6
23 0.8040831089019775
data: 7
15 0.8114683628082275
data: 8
253 0.8258225917816162
data: 10 0.826324462890625
data: 10

261328 0.8268251419067383
data: 13
 0.8273270130157471
data: 13
 0.8268251419067383
data: 13
811 0.845090389251709
data: 15
 0.8446097373962402
data: 15
1718 16 0.883171558380127
data: 0.8815431594848633
data: 18
18
 0.880507230758667
data: 18
21 0.9177229404449463
data: 19
2 0.9450147151947021
data: 20
4 0.9600613117218018
data: 21
0 0.9701051712036133
data: 22
20 0.9744563102722168
data: 23
27 0.9754600524902344
data: 24
79 1.0218162536621094
data: 26
 1.0213446617126465
data: 26
19 1.0371334552764893
data: 27
12 1.0552606582641602
data: 28
29 1.0806910991668701
data: 29
22 1.1721751689910889
data: 30
press ENTER to exit...

F:\Anaconda\envs\TensorFlow\lib\site-packages\urllib3\connectionpool.py:857: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
Out[65]:
''

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

从输出结果可以看出:

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

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

总共用时两秒多

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

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

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

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


你可能感兴趣的:(crossin课程 1-74)