这段时间在自学Python的相关知识点,以下是根据廖雪峰的Python教程自学笔记.
整数运行永远是精确的?除法如何保证精确度?
整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的(除法难道也是精确的?是的!),而浮点数运算则可能会有四舍五入的误差。
转义字符需要用\表示,在字符串中如果不需要对内容进行转义可以用r”表示,此时”中的内容就不会进行转义
python没有变量类型的概念,同一变量可以进行多次不同类型数值的赋值, 因为Python是动态语言,它的变量本身类型不固定,与之对应的是静态语言.
name = 123
print name
name = 'ABC'
print name
理解Python变量在计算机内存中的表示方法:
当写:
a = “ABC”
时,python解释器做了两件事:
- 在内存中创建字符串 “ABC”
- 在内存中创建变量a,并将a指向字符串”ABC”
一个字节有8个比特(位),能表示256个数字(FF/11111111).
ASCII能表示127个字符,用一个字节就能表示.
由于ASCII不能完全表示汉字,所以国内出现GB2312编码将汉字编进去,通常汉字可以用两个字节表示(即65535个汉字),有些特殊汉字可能需要3-6个字节才能表示.
同理不同的语言采用的编码格式不同,如果多种语言出现在一个文本中,显示出来就会有乱码.于是出现unicode编码,将所有的文字都编进去,这样就不会产生乱码了.
Unicode编码一般通过两个字节表示一个字符,生僻字符可能需要四个字节表示.
如果文本中英文比较多,用Unicode编码比ASCII码需要多出一倍的存储空间,在储存和传输上就十分不划算.及产生”可变长编码”utf-8.
UTF-8编码把一个Unicode字符根据不同的数字大小编成1-6个字节,常用的英文字母被编成1个字节,汉字通常是3个字节,特殊生僻字符会被编成4-6个字节.如果存储英文文件可以节约很多的储存空间和传输速度.
格式化: 和C不同的是格式,Python中格式化的数据需要用%隔开,并且多个数据时用括号括起
'Hello, %s' % 'World'
'Hello, World'
>>> value = 'Hi, %s, you have $%d.'
>>> print value
>>> Hi, Michael, you have $100.
if判断条件的简写方式:
if x:
print "True"
只要x是非零数值 非空字符串 非空list 就判断为True, 否则为False
for循环的简单表示
names = ["Teemo", "Peter", "Vicky", "Peter"]
for name in names:
print "当前人名:" , name
for循环根据下标获取元素
names = ["Teemo", "Peter", "Vicky", "Peter"]
for index in range(len(names)):
print "当前人名:" ,names[index]
求解100以内的素数: 在代码中有个else不懂这个语法
prim = []
for value in range(2, 101):
for i in range(2, value):
if(value % i == 0):
break
else:
prim.append(value)
print prim
从raw_input()读出来的数据都是String类型的,如果需要相应类型的数据需要进行转换,如果数据不能转换成相应数据,会报类型转换异常.及Java中的ClassFormatException.
birth = int(raw_input('birth: '))
dict和set都是按键的形式进行存储,只是dict存储键值对,而set只存储键.dict和set中的键不存在重复,重复的键会被覆盖,且键都是不可变的.
dict: 字典的表现方式
>>> d = {"Teemo": 100, "Vicky": 101, "Peter": 102}
>>> d["Teemo"]
100
把数据放入dict的方法,除了初始化时制定外,还可以通过key放入:
>>> d["Michael": 103]
>>> d["Michael"]
103
>>> d["Michael": 104]
>>> d["Michael"]
104
如果从dict取不存的key值会报错.就如Java的Map报空指针
为了防止dict取空键,可以有两种方法
>>> "Michael" in b
True
>>> "Bob" in b
False
>>> d.get("Bob")
>>> d.get("Bob", -1)
-1
注意: 返回None的时候Python的交互式命令行不显示结果
删除dict中的一个key,用pop(key), 对应的value也会从dict中移除:
>>> d.pop("Michael")
103
因为dict的查找速度快,但是需要耗费大量的内存空间,所以dict可以用在需要高速查找的地方.
要创建一个set,需要提供一个list作为输入集合:
>>> s = set([1, 2, 3])
>>> s
set([1, 2, 3])
注意: 传入的参数 [1, 2, 3]是一个list, 而显示的set([1, 2, 3])只是告诉你这个set内部有1, 2, 3这三个元素,显示的[]不表示这是一个list
通过add(key)方法向set中增加内容,可以重复添加,但是没效果,因为set不存在重复的key
>>> s.add("Teemo")
>>> s
set([1, 2, 3, 'Teemo'])
>>> s.add("Teemo")
>>> s
set([1, 2, 3, 'Teemo'])
通过remove(key)方法移除元素:
>>> s.remove("Teemo")
>>> s
set([1, 2, 3])
set可以看做数学上无序和无重复元素的集合,所以,两个set可以做数学意义上的交集和并集
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
set([2, 3])
>>> s1 | s2
set([1, 2, 3, 4])
函数定义的形式
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad type')
if x >= 0:
return x, -x
else:
return -x, x
定义的函数返回多值其实是返回一个元组(tuple)
默认参数
默认参数可以简化函数调用,当参数内容是默认值时,就不需要传递.一个函数可以含有>=0个默认参数,在间隔传递默认参数时,需要指定参数名称,否则无法对应.
默认参数最好是确定值,如果默认参数是对象时,在处理是容易出错,所以对参数类型是对象的时候,默认参数最好是设置成None
def power(x, n = 2):
result = 1
while (n > 0):
n -= 1
result = result * x
return result
可变参数:
可变参数相当于Java中的 void math(String arg…), 参数的个数不确定.书写样式如下:
def calc(*numbers):
sum = 0;
for n in numbers:
sum = sum + n * n
return sum
可变参数其实是一个tuple,当有现成的list或tuple时,也可以直接传递给可变参数作为参数值
>>> nums = [1, 2, 3, 4]
>>> cals(*nums)
30
只需要在前面加一个*即可
关键字参数
可变参数允许传递0个或任意个参数,这些可变参数在函数调用时自动组装成一个tuple.而关键词参数允许传递0个或任意个含有参数名的参数,这些关键字参数在函数内部自动组装为一个字典(dict).
关键字参数可以扩展函数的功能.
def person(name, age, **kw):
print 'name:', name, 'age:', age, 'other:', kw
>>> person("Teemo", 25)
Teemo: name age: 25 other: {}
>>> person("Teemo", 25, job = "IT")
Teemo: name age: 25 other: {"job": "IT"}
>>> kw = {"city": "ShangHai", "Job": IT}
>>> person("Teemo", 25, **kw)
Teemo: name age: 25 other: {"city": "ShangHai", "job": "IT"}
组合参数
Python中可以同时使用必选参数 默认参数 可变参数 关键字参数,但是参数的顺序必须按照上面的方式进行排列.
def func(a, b, c=0, *args, **kw):
print 'a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw
>>> func(1, 2)
a = 1 b = 2 c = 0
>>> func(1, 2, c = 3)
a = 1 b = 2 c = 3
>>> func(1, 2, 3, 'a', x= 99)
a = 1 b = 2 c = 3 arg = ('a',) kw = {'x': 99}
>>> kw = {"city": "ShangHai"}
>>> func(1, 2, **kw)
a = 1 b = 2 c = 0 arg = () kw = {'city': 'ShangHai'}
可以通过一个tuple和dict,可以调用该函数
>>> args = (1, 2, 3, 4)
>>> kw = {'x': 99}
>>> func(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'x': 99}
所以,对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的.
切片
切片及截取,犹如Java中String的subString(int start, int end).
切片可以对Python中的list, tuple, String进行操作.
>>> l = range(10)
>>> l[0:3]
[1, 2, 3]
>>> 1[:3]
[1, 2, 3]
>>> l[-2:]
[8, 9]
>>> l[-2, -1]
[8]
>>> l[1:3]
[1, 2]
>>> l[::2]
[0, 2, 4, 6, 8]
>>> l[:4:2]
[0, 2]
>>> l[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
迭代
Python中迭代的表现形式采用 for xxx in xxx来完成的.for循环不仅可用在list tuplie上,还可以用在任何可迭代对象上.比如 dict, String.
>>> d = {'a': 1, 'b':2, 'c': 3}
>>> for key in d:
... print key
...
a
c
b
默认情况下dict迭代的是key, 如果要迭代value,可采用for value in d.itervalues():, 如果要同时迭代key和value,可采用 for key, value in d.iteritems():
迭代的要求是满足collection模块的Iterable:
>>> from collections import Iterable
>>> isinstance('abc', Iterable)
True
>>> isinstance([1, 2, 3], Iterable)
True
>>> isinstance(123, Iterable)
False
Python内置的enumerate函数可以把list变成索引-元素对,这样for循环就可以同时迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C'])
... print i, value
...
0 A
1 B
2 C
for循环里可以同时引用两个变量:
>>> for x, y in [(1, 1), (2, 4), (3, 6)]:
... print x, y
...
1 1
2 4
3 6
列表生成式
>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> [x*x for x in range(10)]
[1, 4, 9, 16, 25, 36, 47, 64, 81, 100]
>>> [x*x for x in range(10) if x % 2 == 0]
[4, 16, 64, 100]
>>> [m + n for m in 'AB' for n in 'XY']
['AX', 'AY', 'BX', 'BY']
生成式
通过列表生成式,可以直接创建一个列表.但是,受到内存限制,列表内容肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面的几个元素,那后面的绝大多数元素占用的空间就白白浪费了.
所以产生了generator(生成器),它能够根据算法推算下一个元素.这样就节约了很大的空间.
>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
at 0x104feab40>
>>> g.next()
0
>>> g.next()
1
>>> for n in g:
... print n
如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现.
斐波拉契数列
函数表示方法
def fib(max):
n, a, b = 0, 0 ,1
while n < max:
print b
a, b = b, a + b
n = n + 1
>>> fib(4)
1
1
2
3
把fib函数中的print b 换成 yield b 就可以生成generator. 当generator调用next()时, 遇到yield返回数据, 并停止在yield, 当再次执行时从上次返回的yield语句处继续执行.
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
map: 显示格式为map(func, list/tuplie), map的计算结果为list.它讲list/tuplie中的元素均个的放入func中进行计算,得到的结果放在list中.
>>> def my_func(n):
... return n * n
...
>>> map(myfunc, [1, 2, 3])
[1, 4, 9]
reduce
filter: 与map类似,参数也是两个值,分别是方法和参数集合,与map所不同的是filter中的方法对参数进行判断,方法返回True则保留,否则剔除.
>>> def prim(n):
... for x in range(2, n):
... if n % 2 == 0:
... return False
... else:
... return True
...
>>> filter(prim, (1, 2, 3, 4))
(1, 3)
sorted 排序算法,是一个高级函数(能够接受函数作为参数的函数)
>>> sorted([1, 3, 4, 5, 2])
[1, 2, 3, 4, 5]
自定义排序规则
>>> # 对list倒序排序
>>> def reversed_cmp(x, y):
... if x > y:
... return -1
... if x < y:
... return 1
... return 0
...
>>> sorted([1, 3, 4, 5, 2], reversed_cmp)
[5, 4, 3, 2, 1]
Python中,安装第三方模块,是通过setuptools工具完成的.Python有两个封装了setuptools的包管理工具.easy_install和pip.
pip 安装 python image library(PIL).
pip install pillow
由于PIL安装过程中经常会出现一些错误,所以改安装Pillow,而且Pillow对图片的支持更优于PIL.
如果在安装的过程中出现权限不够的错误,改用下面的命令:
sudo -H pip install pillow
简单使用PIL
>>> from PIL import Image
>>> img = Image.open("/Users/SinPingWu/Pictures/dog.png")
>>> print img.size
(2000, 2000)
使用future模块
__future__能够把新版本中的特性引入当前版本,比如说使用python3.x的字符串表示方法:
>>> # still running on python 2.7
>>> from __future__ import unicode_literals
>>> print '\'xxx\' is unicode?', isinstance('xxx', unicode)
True
在Python中通过class关键字定义类:
class Student(object):
pass
class定义类的关键字,Student作为类的类名,object作为Student的父类
创建类的构造方法:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
注意:init方法的第一个参数永远是self,表示创建的实例本身,因此,在init方法内部,就可以把各种属性绑定到self.
与Java类似,定义了有参的构造方法,创建类的对象是,就需要传递参数.不过Python中的第一个参数self不需要传递.
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
>>> temp = Student('Teemo', 90)
>>> temp.name
'Teemo'
>>> print temp.name
Teemo
访问权限
在Java中存在私有变量,而Python中同样存在私有变量.在上面的代码中temp可以直接访问name属性.定义私有属性如下所示:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print '%s: %s' % (self.__name, self.score)
私有变量通常以”__”开头,并且类中的方法定义时,需要添加self参数,该self参数代表该类.
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print '%s: %s' % (self.__name, self.score)
def getName(self):
return self.__name
继承和多态
与Java类似,Python也有继承和多态的概念,原理和Java也是类似的.在定义上,Java继承需要关键字extends而Python只需要在类的后面加上”(基类名)”.同样,Python的子类含有基类的公有方法和属性.
class Student(object):
def __init__(self, name, grade):
self.__name = name
self.grade = grade
def printName(self):
pass
def printGrade(self):
print "grade is %s" % self.grade
def __printAllMsg(self):
print "name is %s and grade is %s" % (self.__name, self.grade)
class StudentA(Student):
pass
StudentA继承了Student,拥有了Student的grades属性和printName方法及printGrade方法.由于__printAllMsg方法是私有的,所以StudentA无法继承.
获取对象信息
获取对象类型的方法有type() isinstance()
>>> type(123)
<type 'int'>
>>> type('str')
<type 'str'>
>>> type(None)
<type 'NoneType'>
>>> type(abs)
<type 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>
使用Python自带的类型TypeType简化判断
>>> import types
>>> type('abc') == types.StringType
True
>>> type(u'abc') == types.UnicodeType
True
>>> type(str) == types.TypeType
所有的类型本身都是TypeType,例如:
>>> type(int) == type(str) == types.TypeType
True
isinstance与Java中的instanceOf()很相似,只是python的isinstance可以判断一个参数是否为两个类中的一种,例如:
>>> isinstance(u'a', (str, unicode))
True
使用dir列出对象的所有属性和方法,返回的是一个包含字符串的list.
>>> dir('ABC')
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
使用getattr(), setattr(), hasattr()直接操作对象的状态
>>> class MyObject(object):
... def __init__(self):
... self.x = 9
... def power(self):
... return self.x * self.x
...
>>> obj = MyObject()
>>> hasattr(obj, 'x')# 是否有'x'属性
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有'y'属性吗
False
>>> setattr(obj, 'y', 19) # 设置属性'y'
>>> hasattr(obj, 'y')
True
>>> obj.y
19
>>> getattr(obj, 'z', 100)# 获取'z'属性,如果没有默认返回100
同样的可以判断是否有方法
>>> hasattr(obj, 'power')
True
>>> getattr(obj, 'power')
method MyObject.power of<__main__.MyObject object at 0x108ca12312>>
>>> fn = getattr(obj, 'power')
>>> fn()
81
使用slots 限定类的参数(该方法只对该方法有效,对其子类无效)
通常在静态语言中,类的功能和属性是被定义好的,不会改变.而在动态语言中,类的属性和方法是可以随时变化的,可以给类及类的对象绑定属性和方法.
>>> class Student(object):
... pass
...
>>> s = Student()
>>> s.name = "Michael" #动态给s实例对象绑定一个name属性
>>> print s.name
Michael
给对象绑定方法
>>> def set_age(self, age):
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s, Student) # 给实例对象s绑定一个方法
>>> s.set_age(22)
>>> s.age
22
给实例对象绑定了方法,是其他的实例是不起作用的,除非是给类绑定方法
>>> def set_score(self, score):
... self.score = score
...
>>> Student.set_score = MethodType(set_score, None, Student)
为了达到限制的目的,Python允许在定义class的时候,定义一个特使的slots变量,来限制该class能添加的属性:
>>> class Student(object):
... __slots = ('name', 'age')
使用@property简化类中方法的调用
Python可以给对象进行动态赋值,但是这样会缺失对参数的校验,使用@property可以做到像动态赋值一样简单的赋值操作切能进行参数校验
class Student(object):
@property
def score(self):
return self._name
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100')
self._score = value
在上段代码中的@score.setter代表着score的Setter方法.而仅仅用@property描述的score方法代表着score的Getter方法.
多继承
Python支持多继承,只要在类的括号中添加另一个类名即可.由于python中的类承担着Java中interface的功能.所以在python中对这种多继承的类有一个特殊的命名”混合(mixin)”
例如Python自带的TCPService和UDPServer这两个网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixin和ThreadMixin提供.
class MyTCPServer(TCPServer, ForkingMixin):
pass
定制类
定制类的过程就像实现类的默认函数.就如Java中对类重写父类的方法,并且这些方法都是object中的.
使用元类
type()函数可以查看一个类型或变量的类型.同时也可以通过type()来创建一个类
>>> def fn(self, name='World'):
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn))
>>> h = Hello()
>>> h.hello()
Hello, World.
>>> print(type(Hello))
'type'>
>>> print(type(h))
<class '__main__.Hello'>
使用type()创建class对象,需要传递的参数:
1. class的名称”Hello”
2. 继承的父类集合,采用tuple的方式传递”(object,)”
3. class的方法名称与函数的绑定,采用dict的方式传递”dict(hello=fn)”
try…except…finally语句.
Python中也有像Java中的try…catch…finally语句.用法也同理.
打印信息的方式
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print 10 / n
logging能够指定记录信息的级别,有debug info warning error.级别由大到小.指定了info, debug就不能显示,指定了warning, debug info就不能显示.
读写文件:
读写文件通过Python内置的open()函数完成,并传入操作符.
>>> file = open("/Users/Teemo/file/test.txt", "r")
>>> file.read()
'Hello, Teemo!'
>>> file.close()
其中’r’代表读操作.文件打开后,可以通过read()方法一次性读取文件中的所有内容到内存中,并以str对象表示.读写文件结束,都要进行文件的关闭操作.可以使用with语句来简化close()操作
with open("/Users/Teemo/file/test.txt", "r") as file:
for line in file.readlines():
print(line.strip())#把末尾的'\n'删掉
‘r’打开的文件是以ASCII码编码的文本文件,如果要读取二进制文件,比如图片或视频等,需要用’rb’模式打开文件.读取非ASCII编码的文本文件,就必须以二进制模式打开,再进行解码.
>>> f = open("/Users/Teemo/file/test.txt", "rb")
>>> f.read()
'\xff\xdr\xfe'
>>> content = f.read().decode('gbk')
>>> print content
测试
Python根据codecs模块进行自动解码
import codecs
with codecs.open('Users/Teemo/file/test.txt', 'r', 'gbk') as f:
f.read()
写文件只需要将操作符换成’w’即可
2.文件及文件夹操作
>文件及文件夹操作包含在os模块中.
操作命令 | 命令解释 |
---|---|
os.name | 操作系统名称 |
os.uname | 获取详细的系统信息 |
os.environ | 操作系统中定义的环境变量 |
os.getenv | 获取环境变量的值 |
os.path.abspath(‘.’) | 查看当前目录的绝对路径 |
os.path.join(‘parent’, ‘path’) | 拼接路径 |
os.mkdir(‘path’) | 创建目录 |
os.rmdir(‘path’) | 删除目录 |
os.path.split(‘path’) | 拆分路径,并得到路径和文件名 |
os.path.splitext(‘path’) | 拆分路径,并得到路径和扩展名 |
定义
把变量从内存中变成可存储或传输的过程称之为序列化.序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络进行传输.反之,把变量从序列化的对象重新读到内存中称之为反序列化.
序列化模块的导入
python提供两个模块来实现序列化.分别是C语言写的cPickle,以及Python语言写的pickle.
try:
import cPickle as pickle
except ImportError:
import pickle
序列化和反序列化
可以通过pickle.dumps() pickle.loads()进行序列化和反序列化.或者用pickle.dump() pickle.load()直接将对象序列化后写入一个file-like Object 以及从一个file-like Object中直接反序列化出对象.
序列化后的内容是str,从而可以写入文件.
>>> d = dict(name ='Teemo', age = 20, score = 80)
>>> pickle.dumps(d)
"(dp1\nS'age'\np2\nI20\nsS'score'\np3\nI80\nsS'name'\np4\nS'Teemo'\np5\ns."
>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()
>>>
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Teemo'}
json
在接口之间传递数据一般都采用json的格式进行传递.在python中由json模块提供Json格式的转换.与python中序列化模块(cPickle pickle)类似,json模块也有dumps() dump() loads() load()方法,切方法的作用是类似的.Json反序列化的结果都是unicode的字符串.
>>> import json
>>> d = {"name": 'Bob', 'age': 20, 'score': 90}
>>> print json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
>>>
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> josn.loads(json_str)
{u'age': 20, u'score':88, u'name': u'Bob'}
与Java一样,类进行序列化的时候,需要进行特殊操作.在Python中需要给类设置对应的序列化方法,否则json序列化的时候不知道对应关系,会导致序列化失败.
进程:
Unix/Linux操作系统提供fork()系统调用,返回进程的pid,但是特殊的是fork()方法会返回两次,因为操作系统自动把当前进程(父进程)复制一份(子进程),然后,分别在父进程和子进程内调用.子进程永远返回0,而父进程返回子进程的ID.这样做的理由是,一个父进程可以fork出很多的子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用os.getppid()就可以拿到父进程的ID.
# 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))
由于Windows没有fork()调用,所有上述代码无法在Windows上运行,但是Python中的multiprocessing模块提供一个Process类来代表进程对象.
# .py
from multiprocessing import Process
import os
# 子进程要执行的代码
def run_process(name):
print ('Run child process %s (%s)...' % (name, os.getpid()))
if __name == "__main__":
print "Parent process %s." % os.getpid()
p = Process(target=run_proccess, args=('test', ))
print "Process will start."
p.start()
p.join()
print "Process end."
join()方法可以等待子进程结束后再继续往下执行,通常用于进程间的同步.
多进程 Pool
multiprocessing模块提供Pool类以完成进程池的功能.
from multiprocessing import Pool
import os, time, random
def long_time_task(name):
print ('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print ('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__ == "__main__":
print "Parent process %s." % os.getpid()
p = Pool()
for i in range(5):
p.apply_async(long_time_task, args=(i, ))
print 'Waiting for all subprocess done...'
p.close()
p.join()
print 'All subprocess done.'
Pool对象调用join()方法会等待所有子进程执行完毕,但是在这之前需要调用close(),调用close()之后就不能再向Pool中添加新的Process了.
多进程间的通信
Python的multiprocessing模块包装了底层的通信机制,提供Queue Pipes等多种方式来交换数据.
# coding: utf-8
# 以Queue为例, 在父进程中创建两个子进程,一个往Queue中写数据,一个从Queue中读数据.
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码
def write(q):
for value in ['A', 'B', 'C', 'D']:
print "Put %s to queue..." % value
q.put(value)
time.sleep(random.rand())
# 读数据进程指定的代码
def read(q):
while True:
value = q.get(True)
print "Get %s from queue." % value
if __name__ == "__main__":
# 父进程创建Queue, 并传递给各个子进程
q = Queue()
pw = Process(target=write, args=(q, ))
pr = Process(target=read, args=(q, ))
# 启动子进程pw, 写入:
pw.start()
# 启动子进程pr, 读取:
pr.start()
# 等待pw结束
pw.join()
# 强制停止pr进程
pr.terminate()
父进程所有Python对象都必须通过pickle序列化再传到子进程中,所有, 如果multiprocessing在Windows下调用失败,先要考虑pickle是否失败
线程
Python提供thread和threading两个模块实现线程的功能.
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行
# coding: utf-8
import time, threading
# 新线程执行的代码:
def loop():
print ('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print 'thread %s ended.' % threading.current_thread().name
print 'thread %s is running...' % threading.current_thread().name
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print 'thread %s ended.' % threading.current_thread().name
线程同步锁
多线程和多进程的最大不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此线程之间共享数据最大的危险在于多个线程同时修改一个变量.
threading.Lock()提供锁,相当于Java中的Lock.
# coding: utf-8
import time, threading
balance = 0
lock = threading.Lock()
def change_it(n):
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(10000):
# 先获取锁
lock.acquire()
try:
change_it(n)
finally:
# 释放锁
lock.release()
t1 = threading.Thread(target=run_thread, args=(5, ))
t2 = threading.Thread(target=run_thread, args=(8, ))
t1.start()
t2.start()
t1.join()
t2.join()
print balance
线程局部变量
线程的局部变量能够保障变量的数据只在本线程内使用,不会产生多个线程同时修改同一数据的情况.
Python创建线程局部变量的方式通过threading模块的local()方法.
# coding: utf-8
import threading
# 创建全局ThreadLocal对象
local_school = threading.local()
def process_student():
print 'Hello, %s (int %s)' % (local_school.student, threading.current_thread().name)
def process_thread(name):
# 绑定ThreadLocal的Student
local_school.student = name
process_student()
t1 = thread.Thread(target= process_thread, args=('Teemo', ), name='Thread-A')
t2 = thread.Thread(target= process_thread, args=('Peter', ), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
分布式进程
在Thread和Process中,应当优选Process, 因为Process更加稳定,而且,Process可以分布到多态机器上,而Thread最多只能分布到同一台机器的多个CPU上.
Python的multiprocessing模块不但支持多进程,其中manager子模块还支持吧多个进程分布到多台机器上.
# taskmanager.py
import random, time, Queue
from multiprocessing.managers import BaseManager
# 发送任务的队列
task_queue = Queue.Queue()
# 接收结果的队列
result_queue = Queue.Queue()
# 从BaseManager继承的QueueManager
class QueueManager(BaseManager):
pass
# 把两个Queue都注册到网络上, callable参数关联了Queue对象
QueueManager.register('get_task_queue', callable=lambda: task_queue)
QueueManager.register('get_task_queue', callable=lambda: result_queue)
# 绑定端口5000, 设置验证码'abc'
manager = QueueManager(address=('', 5000), authkey='abc')
# 启动Queue
manager.start()
# 获得通过网络访问的Queue对象
task = manager.get_task_queue()
result = manager.get_task_queue()
# 将任务放进task队列
for i in reage(10):
n = random.randint(0, 1000)
print('Put task %d...' % n)
task.put(n)
# 从result队列读取结果
print('Try get results...')
for i in range(10):
r = result.get(timeout=10)
print('Result: %s' % r)
# 关闭
manager.shutdown()
在分布式多进程环境下,添加任务到Queue不可以直接对原始的task_queue进行操作,那样就绕过了QueueManage的封装,必须通过manager.get_task_queue()获得Queue接口添加.
# taskwork.py
import time, sys, Queue
from multiprocessing.managers import BaseManager
# 创建类似的QueueManager:
class QueueManager(BaseManager):
pass
# 由于这个QueueManager只从网络上获取Queue, 所以注册时只提供名字:
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')
# 连接到服务器, 也就是主进程的地址
server_address = '127.0.0.1'
print('Content to server %s...' % server_address)
# 端口和验证码注意保持与taskmanager.py设置的一致
m = QueueManager(address=(server_addr, 5000), authkey='abc')
# 从网络连接
m.connect()
# 获取Queue的对象
task = m.get_task_queue()
result = m.get_result_queue()
# 从task队列取任务,并把结果写入result队列
for i in range(10):
try:
n = task.get(timeout=1)
print('run task %d * %d...' % (n, n))
r = '%d * %d = %d' % (n, n, n*n)
time,sleep(1)
result.put(r)
except Queue.Empty:
print('task queue is empty.')
# 处理结束
print('worker exit.')
collections
collections模块中含有namedtuple deque defaultdict OrderedDict Counter功能.其中namedtuple类似于Java中的JavaBean, 只是namedtuple不需要构建类就能够完成;deque类似于Java中以ArrayList实现的双向链表;defaultdict类似于Map;OrderedDict类似于list
TCP编程
一个标准的TCP连接过程
# coding: utf-8
# 导入socket库
import socket
# 创建一个socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接
s.connect(('www.sina.com.cn', 80))
# 发送数据
s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
# 接收数据
buffer = []
while True:
# 每次最多接收1k字节
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = ''.join(buffer)
# 关闭连接
s.close()
header, html = data.split('\r\n\r\n', 1)
print header
# 把接收的数据写入文件
with open('\User\Teemo\file\test.txt', 'wb') as f:
f.write(html)
TCP服务器
创建一个服务器程序,需要接收多个客户端程序的连接,并为不同的客户端返回不同的数据,所以在创建服务端程序的时候会采用线程或进程的方式来实现多客户端的连接.
# coding: utf-8
import socket, time, threading
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置监听端口
s.bind(('127.0.0.1', 9999))
# 开始监听9999端口,传入的参数代表指定等待的连接的最大数量
s.listen(5)
print 'Waiting for connection...'
def tcplink(sock, addr):
print 'Accept new connection from %s:%s...' % addr
sock.send('Welcome!')
while True:
data = sock.recv(1024)
time.sleep(1)
if data = 'exit' or not data:
break
sock.send('Hello, %s!' % data)
sock.close()
print 'Connection from %s:%s closed.' % addr
while True:
# 接受一个新的连接
socket, addr = s.accept()
# 创建新线程来处理TCP连接
t = threading.Thread(target=tcplink, args=(socket, addr))
t.start()
TCP客户端程序
# coding: utf-8
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接
s.connect(('127.0.0.1', 9999))
# 接收欢迎消息
print s.recv(1024)
for data in ['Teemo', 'Peter', 'Bill']:
# 发送数据
s.send(data)
print s.recv(1024)
s.send('exit')
s.close()
UDP服务端
Tcp是建立可靠连接,并且通信双方都可以以流的形式发送数据.相对于TCP,UDP则是面向无连接的协议,所以UDP发送数据不能保证数据可靠到达;虽然UDP传输数据不可靠,但是比TCP快.
# coding: utf-8
import socket
# socket.SOCK_DGRAM指定UDP类型
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
s.bind(('127.0.0.1', 9999))
print 'Bind UDP on 9999...'
while True:
# 接收数据
data, addr = s.recvfrom(1024)
print 'Received from %s:%s.' % addr
s.sendto('Hello, %s!' % data, addr)
UDP客户端
# coding: utf-8
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in ['Teemo', 'Peter', 'Bill']:
# 发送数据
s.sendto(data, ('127.0.0.1', 9999))
# 接收数据
print s.recv(1024)
s.close()
WSGI接口:(Web Server Gateway Interface)
简单的Web Hello world!
# hello.py
# environ: 一个包含所有HTTP请求信息的dict对象;
# start_response: 一个发送HTTP响应的函数
def application(environ, start_response):
# start_response()函数只能调用一次,及只能发送一次Header
# start_response()接收两个参数,分别为:HTTP响应码; 一组list表示的HTTP Header,每个Header用一个包含两个str的tuple表示.
start_response('200 OK', [('Content-Type', 'text/html')])
# 返回内容作为HTTP的body返回.
return 'Hello, %s!
' % (environ['PATH_INFO'][1:] or 'web')
# server.py
# 从wsgiref模块导入
from wsgiref.simple_server import make_server
# 导入编写的application函数
from hello import application
# 创建一个服务器,IP地址为空,端口为8000,处理函数为application
htppd = make_server('', 8000, application)
print 'Serving HTTP on port 8000...'
# 开始监听HTTP请求
httpd.serve_forever()
在浏览器中输入 http://localhost:8000/Python 就会看到结果
flask框架
flask是一个Python的Web框架,还有:
# coding: utf-8
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return 'Home'
@app.route('/signin', methods=['GET'])
def signin_form():
return ''''''
@app.route('/signin', methods=['POST'])
def signin():
# 从表单读取数据
username = request.form['username']
password = request.form['password']
if username == 'admin' and password == '1234':
return 'Hello, admin!
'
return 'Bad username or password.
'
if __name__ == '__main__':
app.run()
Flask自带的Server在端口5000上监听
模板
将html代码提取出来,生成文档–模板.这样就做到了python代码和HTML代码的分离,也满足开发中的MVC模式.
form flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return render_template('home.html')
@app.route('/signin', methods=['GET'])
def signin_form():
return render_template('form.html')
@app.route('signin', methods=['POST'])
def signin():
username = request.form['username']
password = request.form['password']
if username == 'admin' and password == 'password':
return render_template('signin-ok.html', username=username)
return render_template('form.html', message='Bad username or password.', username=username)
if __name__ == '__main__':
app.run()
Python的Flask默认支持的模板是jinja2, Flask通过render_template()函数来实现模板的渲染.
<html>
<head>
<title>Hometitle>
head>
<body>
<h1 style="font-style:italic">Homeh1>
body>
html>
<html>
<head>
<title>Please Sign Intitle>
head>
<body>
{% if message %}
<p style="color:red">{{ message }}p>
{% endif %}
<form action="/signin" method="post">
<legend>Please sign in:legend>
<p><input name="username" placeholder="Username" value="{{ username }}">p>
<p><input name="password" placeholder="Password" type="password">p>
<p><button type="submit">Sign Inbutton>p>
form>
body>
html>
<html>
<head>
<title>Welcome, {{ username }}title>
head>
<body>
<p>Welcome, {{ username }}!p>
body>
html>
模板需要放在正确的templates目录下,templates和app.py在同级目录下