Fork me on GitHub

Python2Tutorial

之前学了一些Python3,所以未来还是把重心放在Python3上。此处简要了解Python2的特性,方便阅读开源代码。

In [ ]:
print 'hello,python2'

函数

函数参数

  • 必选参数
  • 默认参数: age = 2
  • 可变参数: *args
  • 关键字参数:**kw

高级特性

切片

迭代

  • isinstance

列表生成式

In [1]:
[x * x for x in range(1,11) if x % 2 == 0]
Out[1]:
[4, 16, 36, 64, 100]
In [2]:
[m + n for m in 'ABC' for n in 'XYZ']
Out[2]:
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
In [3]:
# 列出当前目录下所有文件名
import os
[d for d in os.listdir('.')]
Out[3]:
['.jupyter',
 '\xe4\xb8\xad\xe6\x96\x87\xe7\xae\x80\xe5\x8e\x86.ipynb',
 '.viminfo',
 'JupyterAdvancedTip.ipynb',
 '.vim',
 '.cache',
 'LearnPythonHardWay.ipynb',
 '.sudo_as_admin_successful',
 '.local',
 '\xe5\xbb\x96\xe9\x9b\xaa\xe5\xb3\xb0Python2Toturial.ipynb',
 '.ssh',
 '.profile',
 '.python_history',
 '.ipython',
 '.bashrc',
 'notebook.tex',
 '.bash_history',
 '.nano',
 '.config',
 '.Xauthority',
 '.bash_logout',
 '.linuxbrew',
 '.ipynb_checkpoints']
In [4]:
# 用dict的iteritems同时迭代key和value
d = {'x': 'A', 'Y': 'B', 'z': 'C'}
for k,v in d.iteritems():
    print k, '=' ,v
Y = B
x = A
z = C
In [6]:
d = {'x': 'A', 'Y': 'B', 'z': 'C'}
[k + '=' + v  for k,v in d.iteritems()]
Out[6]:
['Y=B', 'x=A', 'z=C']
In [7]:
L = ['Hello','World','IBM','Apple']
[s.lower() for s in L]
Out[7]:
['hello', 'world', 'ibm', 'apple']

生成器

一边循环,一边计算但机制,称为生成器Generator。可以节省大量的内存空间。

  • 创建生成器的方式之一,是将列表生成式的[]改成(),就创建了一个generator
In [13]:
L = [x * x for x in range(10)]
print L
g = (x * x for x in range(10))
print g

print g.next()
print g.next()
print g.next()
print g.next()
print g.next()
print g.next()
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 at 0x7fdd6c365370>
0
1
4
9
16
25
In [14]:
# 通常用for循环来迭代,而不用next()
g = (x * x for x in range(10))
for n in g:
    print n
0
1
4
9
16
25
36
49
64
81
  • 定义generator的另一种方法: 如果一个函数定义中包含yield关键字,这个函数就不是一个普通函数,而是一个generator。
In [15]:
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a+b
        n = n + 1
In [16]:
fib(6)
Out[16]:

generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或最后一行就返回。 变成generator后,在每次调用next()的时候执行,遇到yield的时候返回,再次执行时从上次返回的yield语句处继续执行。要给生成器适当的结束条件。

In [17]:
def odd():
    print 'step1'
    yield 1
    print 'step2'
    yield 2
    print 'step3'
    yield 3

o = odd()
print o.next()
print o.next()
print o.next()
print o.next()
step1
1
step2
2
step3
3

StopIterationTraceback (most recent call last)
 in ()
     11 print o.next()
     12 print o.next()
---> 13 print o.next()

StopIteration: 

函数式编程

map/reduce/sorted/filter

In [2]:
def f(x):
    return x*x

map(f,range(1,10))
Out[2]:
[1, 4, 9, 16, 25, 36, 49, 64, 81]
In [7]:
# 对一个序列求和
def add(x,y):
    return x + y

reduce(add,[2 * x-1 for x in range(1,6)])
Out[7]:
25

reduce把一个函数作用在一个序列[x1,x2,x3,x4]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算 其效果是reduce(f,[x1,x2,x3,x4]) = f(f(f(x1,x2),x3),x4)

In [9]:
# 把序列变为整数
def fn(x,y):
    return x * 10 + y

reduce(fn,[2*x+1 for x in range(5)])
Out[9]:
13579
In [6]:
# str转换为int
def char2num(s):
    return {'0':0, '1':1, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9}[s]

def str2int(s):
    return reduce(lambda x,y: x*10+y, map(char2num, s))

s = '12348765'
str2int(s)
12348765

filter:接收一个函数和一个序列,filter把传入的函数依次作用于每个元素,然后更具返回值是True或False决定保留还是丢弃该元素。 使用filter关键在于实现好筛选函数。

In [12]:
def is_odd(n):
    return n % 2 == 1


filter(is_odd,[1,3,5,7,3,8,9])
Out[12]:
[1, 3, 5, 7, 3, 9]
In [13]:
def not_empty(s):
    return s and s.strip()

filter(not_empty, ['A', '', 'B', None, 'C', ' '])
Out[13]:
['A', 'B', 'C']
In [15]:
# sorted函数可以对list进行排序,此外,sorted也可以接收一个比较函数实现自定义排序。
def reversed_cmp(x,y):
    if x > y:
        return -1
    if x < y:
        return 1    #换位
    return 0

print sorted([36, 5, 12, 9, 21])
print sorted([36, 5, 12, 9, 21], reversed_cmp)
[5, 9, 12, 21, 36]
[36, 21, 12, 9, 5]
In [16]:
def cmp_ignore_case(s1, s2):
    u1 = s1.upper()
    u2 = s2.upper()
    if u1 < u2:
        return -1
    if u1 > u2:
        return 1
    return 0


print sorted(['bob','about','Zoo','Credit'])
print sorted(['bob','about','Zoo','Credit'],cmp_ignore_case)
['Credit', 'Zoo', 'about', 'bob']
['about', 'bob', 'Credit', 'Zoo']

函数作为返回值

高阶函数除了可以接收函数作为参数,还可以把函数作为结果值返回。

In [18]:
# 返回求和函数
def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

f = lazy_sum(1,3,5,7,9)
print f
f()

Out[18]:
25

闭包

在上边的例子里,内部函数sum可以引用外部函数lazy_sum的参数和局部变量;
当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为闭包(Closure)的程序结构拥有极大威力。

每次调用lazy_sum函数,都会返回一个新的函数,即使传入相同的参数

In [19]:
f1 = lazy_sum(1,3,5,7,9)
f2 = lazy_sum(1,3,5,7,9)
f1 == f2  
# f1和f2的调用结果互不影响
Out[19]:
False

当一个函数返回一个函数后,其内部的局部变量还被新函数引用,所以,实现闭包很复杂,需要牢记:返回函数不要引用任何循环变量,或者后续会发生的变量。
若一定要引用循环变量,方法是创建一个函数,用该函数的参数绑定循环变量当前的值,无论循环变量后续如何更改,已绑定到函数参数的值不变。

In [23]:
def count():
    fs = []
    for i in range(1,4):
        def f():
            return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
# 等三个函数都返回时,他们引用的变量i已经变为了3
print f1()
print f2()
print f3()
9
9
9
In [21]:
def count():
    fs = []
    for i in range(1,4):
        def f(j):
            def g():
                return j*j
            return g
        fs.append(f(i))
    return fs

f1, f2, f3 = count()
print f1()
print f2()
print f3()
1
4
9

匿名函数lambda

lambda x:x * x

装饰器

在代码运行期间动态增加功能的方式,称为装饰器Decorator
本质上,装饰器就是一个返回函数的高阶函数。

In [25]:
# 接收一个函数作为参数,返回一个函数
def log(func):
    def wrapper(*args, **kw):
        print 'call %s()' % func.__name__
        return func(*args, **kw)
    return wrapper

@log
def now():
    print '2018-3-23'
    
now()
call now()
2018-3-23

@log放到now()函数的定义处,相当于执行了now = log(now) 由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper函数。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。

In [27]:
def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print '%s %s():' % (text,func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute')
def now():
    print '2018-3-23'

print now
now()

execute now():
2018-3-23

三层嵌套的装饰器效果是now = log('execute')(now)

以上装饰器定义还差最后一步,就是把原始函数的__name__复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
python内置的functools.wraps就是干这个的。

In [28]:
import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print '%s %s():' % (text,func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute')
def now():
    print '2018-3-23'
 

print now
now()

execute now():
2018-3-23

偏函数

当函数的参数个数太多,需要简化时,使用Python的functools.partial可以创建一个新的函数,这个新的函数可以固定原函数的部分参数,简化调用。

模块

__inti__.py定义了一个目录是python的一个包(Packag)

面向对象编程

面向对象高级编程

错误、调试和测试

IO编程

本章IO均属于同步IO

In [ ]:
# 打开文件
try:
    f = open('/path/to/file', 'r')
    print f.read()
finally:
    if f:
        f.close()

# 推荐用with               
with open('/path/to/file', 'r') as f:
    print f.read()

序列化

我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

JSON

方便在不同语言之间传递数据

In [2]:
# 序列化
import json
d = dict(name='bob', age=20, score=88)
json.dumps(d)
Out[2]:
'{"age": 20, "score": 88, "name": "bob"}'
In [3]:
# 反序列化
json_str= '{"age": 20, "score": 88, "name": "bob"}'
json.loads(json_str)
Out[3]:
{u'age': 20, u'name': u'bob', u'score': 88}
In [4]:
import json
# 将类实例序列化
class Student(object):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score

s = Student('Bob', 20, 88)
# Student的转换函数,告诉dumps方法如何将Student实例变为Json对象
def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }

print(json.dumps(s, default=student2dict))
{"age": 20, "score": 88, "name": "Bob"}

将任何class的实例变为dict

print(json.dumps(s, default=lambda obj: obj.__dict__))

因为通常class的实例都有一个dict属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了slots的class。

进程和线程

多任务的实现有3种方式:

  • 多进程模式;
  • 多线程模式;
  • 多进程+多线程模式。
In [7]:
# multiprocessing.py
import os

print 'Process (%s) start...' % os.getpid()
pid = os.fork()
if pid==0:
    print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())
else:
    print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)
Process (8293) start...
I (8293) just created a child process (11871).
Process (8293) start...
I am child process (11871) and my parent is 8293.
In [8]:
from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__=='__main__':
    print 'Parent process %s.' % os.getpid()
    p = Process(target=run_proc, args=('test',))
    print 'Process will start.'
    p.start()
    p.join()
    print 'Process end.'
Parent process 8293.
Process will start.
Run child process test (12192)...
Process end.

进程池Pool

进程间通信

Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

常用内建模块

collection模块

colletction模块提供一些有用但集合类,如:

  • namedtuple 用属性而不是索引来引用tuple
  • deque 高效实现插入和删除操作但双向列表,适用队列和栈
  • defaultdict 当引用dict的key不存在时,返回一个默认值
  • OrderedDict 实现一个FIFO的dict,可对dict有序迭代
  • Counter 一个简单但计数器

base64模块

二进制到文本字符串进行编码,常用于URL、cookie、网页中传输少量二进制数据。

struct模块

解决str和其他二进制数据类型的转换

hashlib

摘要算法,也称哈希算法、散列算法,不能用来加密,只能用来防篡改。但是它但单向特性可以在不存储明文口令但情况下验证用户口令。 它通过一个函数,将任意长度数据转换为一个长度固定但数据串(通常用16进制字符串表示)

常见算法有:

  • MD5
  • SHA1
  • SHA256
  • SHA512

更安全但方法是在摘要算法基础上加盐,比如将用户的用户名作为盐。可以避免黑客反推口令。

In [1]:
import hashlib

sha1 = hashlib.sha1()
sha1.update('how to use hash1 in')
sha1.update(' python hashlib?')
print sha1.hexdigest()
8da78fec3af84660c63d3cc30aa0c6eca69e4d85

itertools

提供了很多处理复杂迭代功能的函数,返回一个迭代对象。可以在for循环中迭代。

XML

HTMLParser

PIL模块

Python Imaging Libraay,已经是Python平台事实上的图像处理标准库。

图形界面

Python支持的第三方图形界面有:

  • Tk
  • wxWidgets
  • Qt
  • GTK

网络编程

电子邮件

发件人 -> MUA ->MTA -> MTA ->……->MTA -> MDA <- MUA <- 收件人

  • SMTP发送邮件
  • POP3收取邮件

访问数据库

为了便于程序保存和读取数据,而且,能直接通过条件快速查询到指定但数据,就出现来数据库这种专门用于集中存储和查询但软件。

使用SQLite

In [1]:
import sqlite3

# 若文件不存在,在当前目录创建
conn = sqlite3.connect('test.db')
# 创建一个Cursor
cursor = sqlite3.connect('test.db')
# 执行一条SQL语句,创建user表
cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
Out[1]:
In [2]:
# 继续执行一条语句,插入一条记录
cursor.execute('insert into user (id, name) values (\'1\', \'Michael\')')
Out[2]:
In [3]:
# 关闭Cursor
cursor.commit()
cursor.close()
In [4]:
# 提交事务
conn.commit()
# 关闭connection
conn.close()
In [5]:
conn = sqlite3.connect('test.db')
cursor = conn.cursor()
# 执行查询语句
cursor.execute('select * from user where id =?',('1',))
Out[5]:
In [6]:
values = cursor.fetchall()
values
Out[6]:
[(u'1', u'Michael')]
In [ ]:
cursor.close()
conn.close()
# 使用过程需要Connection和Cursor正确的关闭,使用try……except……finally

使用MySQL

SQLite的特点是轻量级】可嵌入,但不能承受高并发访问,适合桌面和移动应用; 而MySQL是为服务器设计但数据库,高并发的同时占用大量内存。

使用SQLAlchemy

ORM框架Object-Relationship Mapping可以把关系型数据库的表结构映射到对象上。 可以把数据库表但一行记录与一个对象互相做自动转换。

Web开发

HTTP协议简介

HTTP请求过程

  1. 浏览器向服务器发送HTTP请求,请求包括:

    • 方法:GET还是POST
    • 路径:/full/url/path
    • 域名:由Host头指定
    • 其他相关Header,若是POST,请求还包含一个Body,包含用户数据。
  2. 服务器向浏览器返回HTTP响应,包括:

    • 响应代码:
      • 200成功
      • 3xx重定向
      • 4xx客户端请求有错误
      • 5xx服务器端处理时出错
    • 响应类型:由Content-Type指定
    • 其他相关Header
    • 注:通常服务器HTTP响应会携带内容,也就是一个Body,包含响应但内容,网页HTML源码就在Body里。
  3. 若浏览器还需要继续想服务器请求其他资源,比如图片,就再次发出HTTP请求,重复步骤1、2。

HTTP格式

  1. GET请求格式:
     GET /path HTTP/1.1
     Header1:Value1
     Header2:Value2
    注:每个Header一行一个
  2. POST请求格式:
     POST /path HTTP/1.1
     Header1:Value1
     Header2:Value2
     \r\n\r\n
     Body
    注:通过\r\n\r\n来分隔Body部分,Body的数据类型由Content-Type来确定,Content-Encoding表示Body数据是被压缩的。

WSGI接口

WSGI:Web Server Gateway Interface,无论多复杂的Web应用程序,入口都是一个WSGI处理函数。HTTP请求但输入信息都可以通过environ获得,HTTP响应的输出都可通过start_response()加上啊还是农户返回值作为Body。

最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。Apache、Nginx、Lighttpd等这些常见的静态服务器就是干这件事情的。 如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。

In [1]:
# hello.py WSGI处理函数
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '

Hello, %s

'
% (environ['PATH_INFO'][1:] or 'web')
  • environ:包含HTTP请求的dict对象
  • start_response:发送HTTP响应的函数,参数:
    • 一个是响应码
    • 一个是HTTP Header的list
  • return部分作为HTTP响应的Body

有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。

In [ ]:
# server.py
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server
# 导入我们自己编写的application函数:
#from hello import application

# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print "Serving HTTP on port 8000..."
# 开始监听HTTP请求:
httpd.serve_forever()
Serving HTTP on port 8000...
36.149.194.117 - - [17/Mar/2018 21:13:26] "GET /niuhe HTTP/1.1" 200 21

复杂的Web应用程序,光靠一个WSGI函数来处理还是太底层了,我们需要在WSGI之上再抽象出Web框架,进一步简化Web开发。

协程

Comments