python学习笔记
声明:学习笔记主要是根据廖雪峰官方网站python学习学习的,另外根据自己平时的积累进行修正和完善,仅作个人笔记所用,无商业用途。
如需获取更好的体验,请移步python学习笔记
python基础
dict
字典(dict)使用key和value的形式方便进行查找。
- 创建dict
>>> D = {'a' : 1,'b' : 2,'c' : 3}
>>> D1 = dict(name='Bob', age=20, score=88)
>>> D2 = dict([('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)])
>>> D
{'a': 1, 'b': 2, 'c': 3}
>>> D1
{'name': 'Bob', 'age': 20, 'score': 88}
>>>
>>> D2
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
- 判断字典是否存在
d = dict()
for i in xrange(100):
key = i % 10
if key in d:
d[key] += 1
else:
d[key] = 1
- 给字典默认value
d = dict()
for i in xrange(100):
key = i % 10
d[key] = d.get(key, 0) + 1
- 给所有key赋默认value
from collections import defaultdict
d = defaultdict(lambda: 0)
for i in xrange(100):
d[i % 10] += 1
str
判断字符串是否包含子字符串
- find()
>>> str = 'hello world'
>>> str.find('world')
6
>>> str.find('xx')
-1
>>> str.find('rld',4,len(str))
8
>>>
- in
>>> str = 'hello world'
>>> 'he'in str
True
>>> 'xx'in str
False
>>>
切片
Python的切片功能可以对list,tuple,str等进行元素截取操作。
L = list(range(10))#定义一个10个元素的list
a = L[2:5]#截取从位置2到5位置的元素,包括位置2不包括5位置的元素
b = L[:3]#从位置0到位置3的元素
c = L[8:]#从位置8到位置末尾的元素
d = L[-4:-1]#从倒数位置4到倒数位置1的元素
e = L[1:6:2]#从位置1到位置6间隔为2的元素
f = L[:]#复制list
迭代
在python中,str,dict,list,tuple等可迭代对象都可以通过迭代遍历元素,我们可以通过collections模块中的Iterable类型判断是不是可迭代对象。
- 迭代对象判断
from collections import Iterable
isinstance('abc',Iterable)
isinstance(('a','b','c'),Iterable)
isinstance(['a','b','c'],Iterable)
- list迭代
1、只输出元素:
for i in ['a','b','c']:
print i
2、输出位置和元素:
for i,value in enumerate(['a','b','c']):
print i,value
- dict迭代
1、输出key:
D = {1:'a',2:'b',3:'c'}
for key in D:
print key
2、输出value:
D = {1:'a',2:'b',3:'c'}
for value in D.values():
print value
3、同时输出key和value:
D = {1:'a',2:'b',3:'c'}
for key,value in D.items():
print key,value
小结
从以上可以看出,python中可以用几个变量迭代,如:
for x,y in ([1,'a'],[2,'b'],[3,'c']):
print x,y
只要满足迭代条件,用Iterable判断,就可以用for语句进行迭代,也包括自定义的类型。
补充
可迭代对象(Iterable)和迭代器(Iterator)是不一样的,可迭代对象可以是list,dict ,set,tuple,可以使用for循环进行遍历,对象里面的元素是固定的,所以可以得到可迭代对象所占的空间和大小;迭代器不能计算大小,只能通过next()函数获取下一个元素而不能使用for循环。也就是凡是可作用于for循环的对象都是Iterable类型;凡是可作用于next()函数的对象都是Iterator类型,使用Iterator函数进行判断:
from collections import Iterator
isinstance((x for x in range(10)), Iterator)
它们表示一个惰性计算的序列;可以使用iter()函数把可迭代对象变成迭代器:iter(['a','b'])
列表生成式
python中可以使用列表生成式(List Comprehensions)生成具有一定规则的列表,如需要生成list:L = [1,4,9,16,25,36,49,64,81],使用一条语句即可生成:
[x*x for x in range(1,10)]
在两个列表中做两层循环运算:
[x + y for x in ['a','b','c'] for y in ['d','e','f']]
输出:
['ad', 'ae', 'af', 'bd', 'be', 'bf', 'cd', 'ce', 'cf']
生成器
列表生成式可以生成一个完整的列表,有时会因为列表过大而造成存储空间的浪费。生成器不会一次性生成整个列表,而是在需要的时候从生成器总取出,它会记录上一次生成的数据。
创建生成器的方式一般有2种:
- 把列表生成式的[]改成():
D = (x*x for x in range(1,10))
- 用函数实现:
def getList():
for x in range(1,10):
yield x*x
取出生成器元素的方式一般也有2种,一种是使用函数next(),另一种是通过for循环。
- 通过next():
next(getList())#取出第一个
next(getList())#再次调用取出第二个
值得注意的是当没有更多元素时,会抛出StopIteration的错误。
- 使用for循环:
for i in getList():
print i
通过for循环不需要关心StopIteration的错误。在python3中,如果需要取出函数的返回值,需要捕获StopIteration错误,返回值包含在StopIteration的value中:
while True:
try:
for i in getList():
print i
except StopIteration as e:
print('Generator return value:', e.value)
break
小结
取出生成器元素一般使用for循环。
map/reduce
map
map接收一个函数(带一个参数)和一个iterable,返回把iterable作用于这个函数的iterator。
def multiply(x):
return x*x
list(map(multiply,[1,3,5,7]))
返回:[1, 9, 25, 49]
reduce
reduce是functools模块中的函数,接收两个参数,一个是函数(带两个参数),另一个是iterable。函数会依次与iterable里的上一个元素的运算结果返回同下一个元素进行运算,最终返回一个运算结果。
from functools import reduce
def multiply2(x,y):
return x * y
reduce(multiply2,[1,3,5,7])
返回:105
reduce(multiply2,[1,3,5,7])的运算可以等价为:
reduce(multiply2,[1,3,5,7]) = multiply2(multiply2(multiply2(1, 3), 5), 7)
filter
filter可以过滤一个iterable中不需要的元素。filter接收两个参数,一个是函数,另一个是iterable,返回一个iterator。函数会把iterable中的元素当做参数,返回为True的元素组成iterator。
def isOdd(x):
return x%2
list(filter(isOdd,range(10)))
返回:[1, 3, 5, 7, 9]
sorted
sorted函数可对iterable进行排序。#sorted(iterable[, cmp[, key[, reverse]]])
- 对数字排序默认从小到大:
sorted([2,3,1,6,4])
返回:[1, 2, 3, 4, 6]
可以反向排序:
sorted([2,3,1,6,4],reverse = True)
返回:[6, 4, 3, 2, 1]
- 对str排序按照ASCII:
sorted(['Hello','world','google'])
返回:['Hello', 'google', 'world']
忽略大小写:
sorted(['Hello','world','google'],key = str.lower)
返回:['google', 'Hello', 'world']
错误,调试,测试
异常处理(try...except...finally...)
- 捕获异常
写程序难免遇到错误,如把0作为除数,内存溢出,用户输入的不规范等。python中可以利用try...except...finally...
处理这些错误。如:
try:
a = input('Please input a integer:')
b = int(a) #字符转数字
print('you input is:%d' % b)
except ValueError as e:
print('except:',e)
finally:
print('Finally...')
分别输入数字和字符测试:
Please input a integer:3
you input is:3
Finally...
Please input a integer:a
except: invalid literal for int() with base 10: 'a'
Finally...
输入数字3不会进入except ,会进入finally。
输入字符a会进入except,也会进入finally,但会在执行b = int(a) #字符转数字
这行后,直接跳入except,不会打印you input is:
。当然,异常有很多种,except也可以捕获多种异常。如果要捕获所有异常则写成:except Exception as e:
。
- 抛出异常
抛出异常有raise
唯一的标识符确定:
raise NameError('HiThere')
异常也是类,可以抛出自带的异常,也可以抛出自己编写的一个异常建议使用自带异常。
- 打印异常信息
异常信息可以有logging模块打印出来。
import logging
try:
raise NameError('HiThere')
except Exception as e:
logging.exception(e)
调试
当程序的运行达不到自己想要的效果时,就需要调试来发现程序的错误了。python总常用的调试方式有种:
- print()
- assert
- logging
- pdb
print()
熟悉的打印语句,简单实用。但是需要再调试完成后删除。
assert
assert语法格式:
assert Expression[, Arguments]
Expression为True时,则不打印,为False时,Pyhotn会报一个AssertionError 异常。
如:
def KelvinToFahrenheit(Temperature):
assert (Temperature >= 0),"Colder than absolute zero!"
return ((Temperature-273)*1.8)+32
测试:
>>> print (KelvinToFahrenheit(273))
32.0
>>> print (int(KelvinToFahrenheit(505.78)))
451
>>> print (KelvinToFahrenheit(-5))
Traceback (most recent call last):
File "<pyshell#371>", line 1, in <module>
print (KelvinToFahrenheit(-5))
File "<pyshell#367>", line 2, in KelvinToFahrenheit
assert (Temperature >= 0),"Colder than absolute zero!"
AssertionError: Colder than absolute zero!
>>>
当不需要执行断言语句时,可以使用-O参数来关闭assert:
$ python3 -O err.py
logging
logging不会打印异常,可以分级别打印信息,分为DEBUG,INFO,WARNING,ERROR,CRITICAL五个级别。
import logging
logging.basicConfig(filename='example.log',level=logging.INFO)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
logging.basicConfig()
可以指定打印的级别,只有在指定打印级别之上的logging信息才可以打印出来,还可以输出到文件。
pdb
让程序单步执行或者执行到断点。为了方便,大多会用到IDE,推荐使用pycharm。
单元测试
验证一个python模块是否能正常工作,就需要进行单元测试。如编写mydict.py模块,验证其是否具有达到功能,需要编写mydict_test.py测试模块进行测试。
直接贴代码:
mydict.py文件如下:
class Dict(dict):
def __init__(self,**kw):
super().__init__(**kw)
def __getattr__(self,key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'"% key)
def __setattr__(self,key,value):
self[key] = value
mydict_test.py文件如下:
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
def test_init(self):
d = Dict(a=1, b='test')
self.assertEqual(d.a, 1)
self.assertEqual(d.b, 'test')
self.assertTrue(isinstance(d, dict))
def test_key(self):
d = Dict()
d['key'] = 'value'
self.assertEqual(d.key, 'value')
def test_attr(self):
d = Dict()
d.key = 'value'
self.assertTrue('key' in d)
self.assertEqual(d['key'], 'value')
def test_keyerror(self):
d = Dict()
with self.assertRaises(KeyError):
value = d['empty']
def test_attrerror(self):
d = Dict()
with self.assertRaises(AttributeError):
value = d.empty
def setUp(self):
print('setUp')
def tearDown(self):
print('tearDown')
if __name__ == '__main__':
unittest.main()
单元测试继承unittest.TestCase,每个测试方法需要以test
开头,setUp()
方法与tearDown()
方法是运行在测试方法之前和之后的方法,可以做特殊用途,如打开和关闭数据库。
常用的验证方法如:
self.assertEqual(d.a, 1) #是否相等
self.assertTrue('key' in d) #是否True
with self.assertRaises(KeyError): #是否为指定的异常
value = d['empty']
运行mydict_test.py:
Launching unittests with arguments python -m unittest mydict_test.TestDict in C:\renyanliu\python
setUp
tearDown
setUp
tearDown
setUp
tearDown
setUp
tearDown
setUp
tearDown
Ran 5 tests in 0.004s
OK
Process finished with exit code 0
文档测试
python中的文档注释可以让读者更容易的理解代码,如复制在python交互环境下的运行效果,能清晰的表明代码的功能,那这部分文档的注释本身就起到一个验证代码的功能。python中可以使用文档的形式来验证代码,一举两得。如下,编写mydict2模块:
# mydict2.py
class Dict(dict):
'''
Simple dict but also support access as x.y style.
>>> d1 = Dict()
>>> d1['x'] = 100
>>> d1.x
100
>>> d1.y = 200
>>> d1['y']
200
>>> d2 = Dict(a=1, b=2, c='3')
>>> d2.c
'3'
>>> d2['empty']
Traceback (most recent call last):
...
KeyError: 'empty'
>>> d2.empty
Traceback (most recent call last):
...
AttributeError: 'Dict' object has no attribute 'empty'
'''
def __init__(self, **kw):
super(Dict, self).__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
if __name__=='__main__':
import doctest
doctest.testmod()
运行代码:
Process finished with exit code 0
无报错,表明代码功能正常。如果删除setattr()这个函数,再次运行:
**********************************************************************
File "C:/renyanliu/python/mydict2.py", line 11, in __main__.Dict
Failed example:
d1['y']
Exception raised:
Traceback (most recent call last):
File "C:\Users\User\AppData\Local\Programs\Python\Python36\lib\doctest.py", line 1330, in __run
compileflags, 1), test.globs)
File "<doctest __main__.Dict[4]>", line 1, in <module>
d1['y']
KeyError: 'y'
**********************************************************************
1 items had failures:
1 of 9 in __main__.Dict
***Test Failed*** 1 failures.
Process finished with exit code 0
就会报KeyError。
文档测试严格按照python的交互环境的输入与执行,具有理解代码与检验代码的双重功能。无需担心的是在导入有文档测试的模块不会执行文档测试。
编码
- ASCII:只能表示128个字符。如:A - 65,a - 97
- GB2312:GB2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个。编码表见:GB2312编码表或者GB2312编码2
- Unicode:记录全球所有语言,一般用两个字节表示。
- UTF-8:UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。
从上表可以看出:UTF-8对比Unicode节省了内存空间,且ASCII可以看做是UTF-8编码的一部分。
- 补充:在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件。
python应用
整数与Unicode(ASCII)相互转换
- 整数->Unicode:chr(i)
给出整数i,返回对应的Unicode码,整数i的范围是0~1114111。超出范围会抛出ValueError的异常。
>>> chr(38)
'&'
>>> chr(97)
'a'
>>> chr(1114111)
'\U0010ffff'
>>> chr(1114112)
Traceback (most recent call last):
File "<pyshell#278>", line 1, in <module>
chr(1114112)
ValueError: chr() arg not in range(0x110000)
- Unicode->整数:ord(c)
与chr(i)恰好相反,给出Unicode码c,返回对应的整数。
>>> ord('a')
97
>>> ord('#')
35
- 下图为ASCII码表:
json
json数据类型使用与保存于传输数据,python中的数据可以与json数据做互相转换,标准的json编码是utf-8。
- dict转json
>>> import json
>>> D = dict(width = 20,heigh = 40,lenth = 60)>>> json.dumps(D)
>>> json.dumps(D)
'{"width": 20, "heigh": 40, "lenth": 60}'
>>>
- json转dict
>>> import json
>>> json.loads('{"width": 20, "heigh": 40, "lenth": 60}')
{'width': 20, 'heigh': 40, 'lenth': 60}
>>>
默认的python与json的转换只支持dict,list,tuple。但是如果需要把类也序列化一个json时,需要编写default参数的函数。具体参考:json进阶
正则表达式
匹配规则
-
匹配字符
\d
——匹配一个数字
\w
——匹配一个数字或字母
\s
——匹配一个空格或tab键
.
——可以匹配任意个字符(包括0个) -
匹配个数
+
——至少匹配一个字符
*
——匹配任意个字符
{n}
——匹配n个字符
?
——匹配0个或1个字符
{n-m}
——匹配n-m个字符 -
特殊匹配
|
——或匹配
^
——行开头匹配
$
——行结束匹配
[]
——精准匹配
()
——分组匹配
re模块
re模块包含正则表达式的所有功能,基本语法:re.match(pattern, string)
匹配正常,返回一个Match对象;匹配失败,返回none。
>>> import re
>>> re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> re.match(r'^\d{3}\-\d{3,8}$', '010-a12345')
>>>
编译:如果同一个表达式同多组字符串进行匹配,更方便的是利用正则表达式的编译功能。如:
>>> import re
# 编译:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')
字符串切分
>>> 'a b c'.split(' ')
['a', 'b', '', '', 'c']
>>> re.split(r'\s+', 'a b c')
['a', 'b', 'c']
>>>
分组
要提取字符串中的指定字符串,可以利用分组功能来实现,用'()'表示的就是要提取的分组(Group)。
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
group(0):表示原字符串
group(1):第1个分组提取的字符串
group(n):第n个分组提取的字符串
贪婪匹配
先看一个例子:
>>> re.match(r'^(\d+)(0*)$', '102300').groups()
('102300', '')
>>> re.match(r'^(\d+?)(0*)$', '102300').groups()
('1023', '00')
>>>
正则表达式中有两个分组匹配(\d+)(0*)
,但在第一个表达式中没有第二组((0*)
)的匹配结果,原因是都被第一组((\d+)
)给匹配了,第一组的匹配就叫做贪婪匹配,如果在表达式后加入?
如:((r'^(\d+?)
),就表示非贪婪匹配,如表达式二,第二组也有了匹配结果。
大端模式与小端模式
- 大端模式( Big-Endian):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端
- 小端模式(Little-Endian):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
- 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian。
OpenPyXl学习手册
参考OpenPyxl官方文档
在使用openpyxl之前,需要安装openpyxl,打开命令行,输入以下命令并回车(安装python时勾选pip命令):
openpyxl
pip install openpyxl
工作簿(workbook)
- 打开已有工作簿
>>> from openpyxl import Workbook,load_workbook
>>> wb = load_workbook('test.xlsx')
- 创建工作簿
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>>
- 保存到文件
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> wb.save('balances.xlsx')
- 作为模板文件
>>> from openpyxl import Workbook,load_workbook
>>> wb = load_workbook('document.xlsx')
>>> wb.template = True
>>> wb.save('document_template.xltx')
注意
1、打开文件为.xlsx格式文件时,保存文件也需要以.xlsx格式文件保存
2、要以keep_vba=True保存.xlsm格式文件
工作表(sheet)
- 获取创建的默认工作表
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> wb.active
<Worksheet "Sheet">
- 创建新的工作表
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws1 = wb.create_sheet('Mysteet') #在末尾插入
>>> ws2 = wb.create_sheet('Mysheet2',0) #插入到第一个
>>>
- 重命名工作表
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws = wb.active
>>> ws.title = 'sheet1'
>>>
- 改变标题卡颜色
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws = wb.active
>>> ws.sheet_properties.tabColor = "1072BA" #RRGGBB color code
>>>
- 根据工作表名字获取工作表
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws3 = wb['sheet1']
>>>
- 打印工作簿中的工作表名
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws1 = wb.create_sheet('Mysteet')
>>> ws2 = wb.create_sheet('Mysheet2',0)
>>> print(wb.sheetnames)
['Mysheet2', 'Sheet', 'Mysteet']
>>> for sheet in wb:
print(sheet.title)
Mysheet2
Sheet
Mysteet
>>>
- 复制一份工作表
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> source = wb.active
>>> target = wb.copy_worksheet(source)
>>>
- 获取最大的行与列
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws = wb.active
>>> for row in range(1, 40):
ws.append(range(600))
>>> ws.max_column
600
>>> ws.max_row
39
>>>
单元格(cell)
- 访问单元格
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws = wb.active
>>>
>>>#访问一个单元格
>>> c1 = ws['A2']
>>> c2 = ws.cell(row = 4,column = 2,value = 10)
>>>
>>>#访问多个单元格
>>> cell_range = ws['A1':'C2']
>>> colC = ws['C']
>>> col_range = ws['C:D']
>>> row10 = ws[10]
>>> row_range = ws[5:10]
>>>
>>>#iter_rows方式访问
>>> for row in ws.iter_rows(min_row=1, max_col=3, max_row=2):
... for cell in row:
... print(cell)
<Cell Sheet1.A1>
<Cell Sheet1.B1>
<Cell Sheet1.C1>
<Cell Sheet1.A2>
<Cell Sheet1.B2>
<Cell Sheet1.C2>
>>>
>>>#打印所有单元格
>>> tuple(ws.rows)
((<Cell 'Sheet'.A1>, <Cell 'Sheet'.B1>), (<Cell 'Sheet'.A2>, <Cell 'Sheet'.B2>), (<Cell 'Sheet'.A3>, <Cell 'Sheet'.B3>), (<Cell 'Sheet'.A4>, <Cell 'Sheet'.B4>))
>>>
>>> tuple(ws.columns)
((<Cell 'Sheet'.A1>, <Cell 'Sheet'.A2>, <Cell 'Sheet'.A3>, <Cell 'Sheet'.A4>), (<Cell 'Sheet'.B1>, <Cell 'Sheet'.B2>, <Cell 'Sheet'.B3>, <Cell 'Sheet'.B4>))
>>>
- 单元格赋值
>>> from openpyxl import Workbook
>>> wb = Workbook()
>>> ws = wb.active
>>> ws['A2'] = 2
>>> c = ws['A3']
>>> c.value = 5
>>> print(c.value)
5
>>>
xlutils学习手册
安装
pip install xlutils
各模块简介
xlutils是xlrd和xlwt两个的集合包,即可读可写Excel文件,它分为以下几个模块。
- xlutils.copy
将xlrd.Book对象转变成xlwt.Workbook对象以便于操作的工具。 - xlutils.display
用友好和安全的方式显示有关xlrd的信息的使用功能 - xlutils.filter
一个用于拆分和过滤Excel文件为新的Excel文件的框架 - xlutils.margins
一个工具,它能找出存在有用数据的Excel文件的数量 - xlutils.save
将xlrd.Book对象序列化为Excel文件的工具。 - xlutils.styles
使用格式化信息的工具表达了Excel文件中的样式。 - xlutils.view
易于表现工作表中的数据的视图
xlrd
能提取Excel的数据,Excel格式可以是xls,xlsx,可运行于任何平台上。可以用python2.7或者python3.4+开发。详细请参考xlrd文档
Excel文件
- 打开Excel文件
book = xlrd.open_workbook("myfile.xlsx")
Excel工作表
- 获取工作表数量
book.nsheets
- 获取工作表名字
book.sheet_names()
- 获取第一个工作表
sh = book.sheet_by_index(0)
table=data.sheets()[0]
- 通过表名获取工作表
table=data.sheet_by_name('mysheet')
- 获取工作表行数
sh.nrows
- 获取工作表列数
sh.ncols
Excel单元格
- 获取某行单元格
sh.row_values(m)
- 获取单元格
sh.cell_value(rowx=m, colx=n)
sh.row(m)[n].value
操作数据库
数据库可以用来存储数据,而不是存储在内存中。并且通过sql语句可以方便的操作数据库,十分方便。
一个数据库中可以存放多个表,表中就是存储数据的地方,不同的表可以通过外键进行关联。操作数据库之前,需要先连接到数据库。
付费的数据库就不说了,说一下开源免费的,有SQLite,mysql等。
SQLite
python内置的轻量级数据库,无需安装。适合桌面和移动应用。
- 打开数据库,没有则会创建:
>>> import sqlite3
>>> conn = sqlite3.connect('test.db')
- 创建cursor,操作数据库
>>> cursor = conn.cursor()
- 执行sql语句
cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
- 返回结果
>>> values = cursor.fetchall()
- 关闭cursor
>>> cursor.close()
- 提交
>>> conn.commit()
- 关闭连接
>>> conn.close()
MySQL
MySQL是使用最为广泛的数据库,在利用python使用MySQL之前,需要先安装MySQL,见:MySQL数据库community下载。安装MySQL后,还需要安装python的MySQL数据库的库,见mysql-connector python。自此,就可以使用python来操作MySQL啦。
- 导入MySQL驱动:
>>> import mysql.connector
- 建立连接, 注意把password设为你的root口令(需要先建立test数据库):
>>> conn = mysql.connector.connect(user='root', password='password', database='test')
- 创建cursor,操作数据库
>>> cursor = conn.cursor()
- 创建user表:
>>> cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
- 插入一行记录,注意MySQL的占位符是%s:
>>> cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'Michael'])
- 提交事务:
>>> conn.commit()
- 关闭cursor
>>> cursor.close()
- 运行查询:
>>> cursor = conn.cursor()
>>> cursor.execute('select * from user where id = %s', ('1',))
>>> values = cursor.fetchall()
>>> values
[('1', 'Michael')]
# 关闭Cursor和Connection:
>>> cursor.close()
True
>>> conn.close()
- 关闭连接
>>> conn.close()
IO编程
文件读写
新建一个hello.txt文件,文件内容如下:
hello.txthello2.txt
打开文件
- 以文本模式打开文件,默认
>>> f = open('C:/renyanliu/python/hello.txt','r')
>>> f.close()
- 以二进制模式打开文件
>>> f = open('C:/renyanliu/python/hello2.txt','rb')
>>> f.close()
读文件:
- f.read():返回文件的所有内容
>>> f = open('C:/renyanliu/python/hello.txt','r')
>>> f.read()
'hello-1\nhello-2\nhello-3\n\n'
>>> f.close()
- f.readlines():返回list,文件的每一行为一个元素
>>> f = open('C:/renyanliu/python/hello.txt','r')
>>> f.readlines()
['hello-1\n', 'hello-2\n', 'hello-3\n', '\n']
>>> f.close()
- f.readline():每次读取文件中的一行
>>> f = open('C:/renyanliu/python/hello.txt','r')
>>> f.readline()
'hello-1\n'
>>> f.readline()
'hello-2\n'
>>> f.close()
写文件:
- 写入文本文件
>>> f = open('C:/renyanliu/python/hello.txt','w')
>>> f.write('hello-4')
7
>>> f.close()
>>>
写文件后要记得关闭文件,确保文件的写入。 f.write('hello-4')
会清空文本的内容,重新写入。写后的hello.txt文件如下:
- 写二进制文件
>>> f = open('C:/renyanliu/python/hello2.txt','wb')
>>> f.write(b'\xc4\xe3\xba\xc3')
4
>>> f.close()
注意
文件读写很容易发生异常,如文件路径找不到等,可以使用try...except...finally...,确保文件正常关闭。
try:
f = open('/path/to/file', 'r')
f.read()
finally:
if f:
f.close()
更方便的是推荐使用with open,在操作文件后会自动关闭:
with open('/path/to/file', 'r') as f:
f.read()
StringIO和BytesIO
读写数据的对象不仅可以是文件,还能是内存,StringIO和BytesIO的使用和文件使用差不多。
- str写入StringIO:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5
>>> f.write(' ')
1
>>> f.write('world')
5
>>> print(f.getvalue())
hello world
- 读取StringIO的str:
>>> from io import StringIO
>>> f = StringIO('hello\nworld\n')
>>> while True:
line = f.readline()
if line == '':
break
print(line.strip())
hello
world
>>>
- 从内存中读取bytes:
>>> from io import BytesIO
>>> f = BytesIO()
>>> f.write('你好'.encode('utf-8'))
6
>>> print(f.getvalue())
b'\xe4\xbd\xa0\xe5\xa5\xbd'
>>>
- 写入bytes到内存中:
>>> from io import BytesIO
>>> f = BytesIO(b'\xe4\xbd\xa0\xe5\xa5\xbd')
>>> f.read()
b'\xe4\xbd\xa0\xe5\xa5\xbd'
>>>
操作文件和目录
python操作文件和目录一般用到os,os.path,shutil三个模块。
- 获取操作系统:
>>> import os
>>> os.name
'nt'
>>>
nt:windows系统
posix:Linux、Unix或Mac OS X
- 获取环境变量:
>>> import os
>>> os.environ.get('PATH')
'C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Users\\User\\AppData\\Local\\Programs\\Python\\Python36\\Scripts\\;C:\\Users\\User\\AppData\\Local\\Programs\\Python\\Python36\\'
>>>
- 获取当前绝对目录:
>>> import os
>>> os.path.abspath('.')
'C:\\Users\\User\\AppData\\Local\\Programs\\Python\\Python36'
>>> os.getcwd()
'C:\\Users\\User\\AppData\\Local\\Programs\\Python\\Python36'
>>>
- 合并目录
>>> import os
>>> os.path.join('C:\\user','test.txt')
'C:\\user\\test.txt'
- 拆分目录
>>> import os
>>> os.path.split('C:\\user\\test.txt')
('C:\\user', 'test.txt')
- 创建和删除目录
>>> import os
>>> os.mkdir('C:\\test') #创建
>>> os.rmdir('C:\\test') #删除
>>>
- 获取文件扩展名
>>> import os
>>> os.path.splitext('C:\\test\\test.txt')
('C:\\test\\test', '.txt')
>>>
- 文件重命名
>>> import os
>>> os.rename('C:\\test\\test.txt','C:\\test\\test1.txt')
>>>
- 删除文件
>>> import os
>>> os.remove('C:\\test\\test1.txt')
>>>
- 复制文件
>>> import shutil
>>> shutil.copyfile('C:\\test\\test.txt','C:\\test\\tese1.txt')
'C:\\test\\tese1.txt'
>>>
- 列出当前目录的所有目录
>>> import os
>>> [x for x in os.listdir('.') if os.path.isdir(x)]
['DLLs', 'Doc', 'include', 'Lib', 'libs', 'Scripts', 'tcl', 'Tools']
>>>
- 列出当前目录的指定扩展名文件
>>> import os
>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']
['test - 副本.py', 'test.py']
>>>
- 返回到上级目录
>>> import os
>>> import sys
>>> os.path.dirname(os.getcwd())
'C:\\Users\\User\\AppData\\Local\\Programs\\Python'
>>>
序列化
把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。
>>> import pickle
>>> D = {'length':80,'width':50,'heigh':60}
>>> pickle.dumps(D)
b'\x80\x03}q\x00(X\x06\x00\x00\x00lengthq\x01KPX\x05\x00\x00\x00widthq\x02K2X\x05\x00\x00\x00heighq\x03K<u.'
>>>
直接写入文件:
>>> import pickle
>>> D = {'length':80,'width':50,'heigh':60}
>>> f = open('C:\\renyanliu\\test.txt','wb')
>>> pickle.dump(D,f)
>>> f.close()
>>>
如图,写入的数据是一堆看不懂的东西。
test.txt
现在我们把数据度出来:
>>> import pickle
>>> f = open('C:\\renyanliu\\test.txt','rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'length': 80, 'width': 50, 'heigh': 60}
>>>
又变成熟悉的样子。
注意:Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
异步IO
多线程解决了多任务的并发执行,而异步IO可以在一个线程中执行几个任务。
先来了解几个概念:
协程
英文名Coroutine,协程在执行过程中,会执行其他的程序,有点像中断,待执行到满足条件后再返回,之后很可能再次跳到之前执行的程序中接着执行。
- 优势:相对于多线程多进程来说,协程不需要切换线程,节省了cpu切换线程的时间,大大提高了运行效率,也不需要线程的锁机制,不存在变量写冲突。
Python对协程的支持是通过generator实现的。但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。
举例:
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
运行结果:
C:\Users\renyangfar\AppData\Local\Programs\Python\Python35\python.exe D:/python/test.py
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[PRODUCER] Consumer return: 200 OK
produce(c)中通过c.send(None)启动协程,用n来控制运行的次数,r = c.send(n)可以发送带参数的信息给consumer(),在consumer中接着n = yield r这句运行,知道再次运行到这条语句,返回到produce(c)中。直到运行完毕用c.close()关闭协程。
函数式编程
函数式编程(Functional Programming)与函数编程是两个不同的概念。函数编程允许变量,是有副作用的;纯函数式编程没有变量,对应的输出是确定的,是没有副作用的。Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
函数参数用法
python中函数有四类参数-必选参数、默认参数、可变参数、关键字参数。
- 必选参数
定义函数时直接定义参数:
def sum(a,b):
return a + b
调用函数时需要给出参数:
sum(1,2)
- 默认参数
定义函数时给默认参数赋值,赋值参数要求不可变:
def sum(a,b = 2):
return a + b
调用函数时可以不给默认参数,默认参数若为多个,只要给出参数名可以不按定义顺序赋值:
sum(1)
- 可变参数
定义函数时参数前加‘*’:
def sum(*a):
sum = 0
for i in a:
sum = sum + i
return sum
调用函数时给出一个列表(list)或元组(tuple):
sum(1,2)
或者:
a = [1,2]
sum(*a)
- 关键字参数
定义函数时参数前加‘**’:
def printSum(**ps):
print ps
调用函时给出任意个关键字参数:
printSum(a = 1,b = 2,sum = 1+2)
或者给出字典(dict):
ps = {'a':1,'b':2,'sum':1+2}
printSum(**ps)
小结:
python函数的参数具有灵活的应用,以上四种参数可以进行组合,但参数的组合需要按照必选参数、默认参数、可变参数、关键字参数的顺序。通过组合参数能实现复杂灵活的函数调用。
汉诺塔递归调用
-
问题描述
有三根柱子a、b、c,在a柱子上有n个盘子,盘子由大到小叠放,要求借助b柱子把A柱子上的盘子移到c柱子上,同样由大到小叠放。请用函数输出移动过程。
汉诺塔 - 函数实现如下:
def move(n,a,b,c):
if n!=0:
move(n-1,a,c,b)
print 'move',a,'->',c
move(n-1,b,a,c)
- 分析
先不要考虑函数的实现细节,从移动盘子的思路去分析问题,把move(n,a,b,c)当做是已经实现了函数,能有神奇的力量把n个盘子顺利的从a柱子移到c柱子。要把这叠盘子从a柱子移到c柱子,需要先把最大的盘子移到c柱子,可是最大的盘子在最下面,所以需要把上面的盘子移到柱子b,即:move(n-1,a,c,b)
,然后把最大盘子移到c柱子,即输出:print 'move',a,'->',c
。最后利用递归的思想把剩下的b柱子的盘子移到c柱子,即:move(n-1,b,a,c)
。
高阶函数
高阶函数指函数的参数可以是函数,这是由于函数名也是 一个变量。如:
a = abs
print a(-10)
把a变量指向abs这个函数,a即拥有了abs函数的功能。
高阶函数实例如下:
def add(x,y,z):
return z(x) + z(y)
调用:
add(-3,-5,abs)
结果:8
返回函数
python中允许返回一个函数,返回的函数不会立即执行,而是在调用的时候执行。
def sum_lazy(*args):
def getSum():
sum = 0
for i in args:
sum = sum + i
return sum
return getSum
f = sum_lazy(1,2,3,4)
f()
#返回:10
注意:由于函数只会在调用的时候执行,如果函数内用了共同的变量,容易出现问题,如:
def getFunc():
func = []
for i in range(1,4):
def retFunc():
return i*i
func.append(retFunc)
return func
a,b,c = getFunc()
a()#返回9
b()#返回9
c()#返回9
如果需要使用循环变量,可以在函数内层再新建一个函数,使其与变量进行绑定:
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
f1, f2, f3 = count()
f1()#返回1
f2()#返回3
f3()#返回9
匿名函数
lambda [arguments]: expression
如:lambda x : x*x
匿名函数可以赋值给一个变量:y = lambda x : x*x
调用时:y(5)
常见的应用:list(map(lambda x : x * x,[1,2,3,4,5]))
装饰器
如果不想改变函数的内部结构,而需要在函数前后加某些功能,如打印日志信息,就可以用到装饰器(Decorator),如有一个函数love(who)
:
def love(who):
print('I love %s'%who)
需要在前面加入一条输出语句:I am xx
,可以这样实现:
def log(func):
def retFunc(*args,**kw):
print('I am xx')
return func(*args,**kw)
return retFunc
借助python的log语法,把log写在函数定义处:
@log
def love(who):
print('I love %s'%who)
如果Decorator本身需要传入参数,如传入text,则需要再加一层函数:
def log(text):
def myFunc(func):
def retFunc(*args,**kw):
print('I am %s'%text)
return func(*args,**kw)
return retFunc
return myFunc
@log('lucy')
def love(who):
print('I love %s'%who)
偏函数
偏函数可以使可以更改默认参数的值,使新的函数具有新的默认参数值。如int('123')
默认使用十进制得到123
,如需要把默认的十进制改为二进制,则可以利用functools中的partial函数:
import functools
int2 = functools.partial(int,base = 2)#定义
int2('1000')#调用
模块
- 优点:使用模块可以减少代码的冗余,可维护性强,在不同的模块中可以使用相同的变量名。
- 命名:如果自己创建的模块名字与系统模块名字有冲突,可以使用包名进行区分,而包名不可以冲突。
- 引用:系统如何把一个文件夹当成一个包?需要在文件夹下面新建一个init.py的文件,这个文件可以为空。具有包名的模块引用方法:包名+'.'+文件名。如:sys.path。
使用模块
标准模块编写如下:
#!/usr/bin/env python3 #表示可以直接让这个文件在Unix/Linux/Mac上运行
# -*- coding: utf-8 -*- #使用标准UTF-8编码
' a test module ' #模块文档注释,模块代码第一个字符串
__author__ = 'renyangfar' #作者名字
import sys #导入使用的模块
def test():
print(sys.version)#输出python版本
if __name__=='__main__': #测试运行时成立,当导入到其他模块时不成立
test()
作用域
分为公开域(public)和私有域(private)。
- public
普通:abc
特殊:xxx
其中特殊变量形如:name,anthor - private
_abc
__abc
私有变量或函数可以在模块内部使用,不希望其他模块调用,也不暴露出去。
安装第三方模块
通过python自带的pip命令安装第三方模块,在命令行输入pip指令,如果报错,则在安装python的时候没有安装pip指令,需要重新勾选安装pip并勾选加入环境变量。
安装python图形库:
pip install Pillow
or easy_install Pillow
常用内置模块
datetime
datetime模块是与时间相关的函数,它可以获取时间,时间格式转换,加减时间等。
- 获取当前时间
>>> from datetime import datetime
>>> now = datetime.now()
>>> print(now)
2017-09-11 11:40:43.306640
>>> print(type(now))
<class 'datetime.datetime'>
>>>
- 获取指定时间
>>> from datetime import datetime
>>> date = datetime(2017,9,11,12,00)
>>> print(date)
2017-09-11 12:00:00
>>>
- datetime转换为timestamp
在计算机中,时间是以数字表示的(timestamp)。timestamp是以1970年1月1日 00:00:00 UTC+00:00时区的时刻为0的秒数。
>>> from datetime import datetime
>>> date = datetime(2017,9,11,12,00)
>>> datetime.timestamp(date)
1505102400.0
>>> date.timestamp()
1505102400.0
>>>
- timestamp转换为datetime
>>> from datetime import datetime
>>> t = 1505102400.0
>>> print(datetime.fromtimestamp(t))
2017-09-11 12:00:00
>>>
- str转换为datetime
>>> from datetime import datetime
>>> d = '2017-09-11 12:00:00'
>>> date = datetime.strptime(d, '%Y-%m-%d %H:%M:%S')
>>> date
datetime.datetime(2017, 9, 11, 12, 0)
>>> print(date)
2017-09-11 12:00:00
>>>
- datetime转换为str
>>> from datetime import datetime
>>> now = datetime.now()
>>> print(datetime.strftime(now,'%Y-%m-%d %H:%M:%S'))
2017-09-11 14:53:18
>>>
- datetime加减
>>> from datetime import datetime,timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2017, 9, 11, 16, 25, 54, 824218)
>>> now - timedelta(hours = 10)
datetime.datetime(2017, 9, 11, 6, 25, 54, 824218)
>>> now + timedelta(days = 2)
datetime.datetime(2017, 9, 13, 16, 25, 54, 824218)
>>> now + timedelta(minutes = 2,seconds = 8)
datetime.datetime(2017, 9, 11, 16, 28, 2, 824218)
>>>
- 本地时间转换为UTC时间与时区转换略
collections
- namedtuple
namedtuple继承自tuple类,具有tuple的属性,同时可以给数据定义名称,访问数据时可以以.
直接访问。
>>> from collections import namedtuple
>>> Point = namedtuple('Point',['x','y'])
>>> P = Point(2,3)
>>> P.x
2
>>> P.y
3
>>>
- deque
把list等线性存储的队列或栈转成双向列表存储的结构,提高修改元素效率。
>>> from collections import deque
>>> d = deque([1,2,3])
>>> d.append(3)
>>> d
deque([1, 2, 3, 3])
>>> d.appendleft(0)
>>> d
deque([0, 1, 2, 3, 3])
>>> d.pop()
3
>>> d
deque([0, 1, 2, 3])
- defaultdict
当访问不存在的key时,会抛出keyerrot异常,如果希望返回默认的值,defaultddict就用上了。
>>> from collections import defaultdict
>>> myDict = defaultdict(lambda:'N/A')
>>> myDict[1] = 'first'
>>> myDict[1]
'first'
>>> myDict[2]
'N/A'
>>>
当访问不存在的key时,返回lambda:'N/A'
返回值,可以用其他函数代替。
- OrderedDict
dict里面的元素是无序的,如果需要按顺序排列dict中的元素,可以用OrderedDict模块。
>>> from collections import OrderedDict
>>> d = {'a':1,'b':2,'c':3}
>>> d
{'a': 1,'c': 3, 'b': 2,}
>>> OrderedDict({'a':1,'b':2,'c':3})
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> d1 = OrderedDict()
>>> de['x'] = 1
>>> d1['x'] = 1
>>> d1['y'] = 2
>>> d1['z'] = 3
>>> list(d1.keys())
['x', 'y', 'z']
>>>
OrderedDict是按添加的顺序排列的,它继承自dict。
>>> isinstance(d1,dict)
True
>>>
OrderedDict可以实现先进先出的dict,当数量超出限制时,会删除最先添加的元素。
from collections import OrderedDict
class LastUpdatedOrderedDict(OrderedDict):
def __init__(self, capacity):
super(LastUpdatedOrderedDict, self).__init__()
self._capacity = capacity
def __setitem__(self, key, value):
containsKey = 1 if key in self else 0
if len(self) - containsKey >= self._capacity:
last = self.popitem(last=False)
print('remove:', last)
if containsKey:
del self[key]
print('set:', (key, value))
else:
print('add:', (key, value))
OrderedDict.__setitem__(self, key, value)
运行一下代码:
import OrderedDict
od = OrderedDict.LastUpdatedOrderedDict(3)
od[1] = 'a'
od[2] = 'b'
od[3] = 'c'
print(od)
od[4] = 'd'
print(od)
结果为:
C:\Users\User\AppData\Local\Programs\Python\Python36\python.exe C:/renyanliu/python/test.py
add: (1, 'a')
add: (2, 'b')
add: (3, 'c')
LastUpdatedOrderedDict([(1, 'a'), (2, 'b'), (3, 'c')])
remove: (1, 'a')
add: (4, 'd')
LastUpdatedOrderedDict([(2, 'b'), (3, 'c'), (4, 'd')])
Process finished with exit code 0
- Counter
Counter可以统计字符出现的次数,它继承自dict。
>>> from collections import Counter
>>> c = Counter()
>>> for ch in 'I like Programmers':
c[ch] = c[ch] + 1
>>> c
Counter({'r': 3, ' ': 2, 'e': 2, 'm': 2, 'I': 1, 'l': 1, 'i': 1, 'k': 1, 'P': 1, 'o': 1, 'g': 1, 'a': 1, 's': 1})
>>>
或者:
>>> Counter('Programmers')
Counter({'r': 3, 'm': 2, 'P': 1, 'o': 1, 'g': 1, 'a': 1, 'e': 1, 's': 1})
>>>
base64
base64可以把二进制数据转为字符。base64定义了一个有64个字符的数组,默认为:[A,B...a,b...1,2,3...+,/]
。它把24位二进制(3个字符)数据分为4组,每组6位二进制数,然后把对应的数字在定义好的数组中查找,比对出的字符即为编码后的字符。
>>> import base64
>>> base64.b64encode(b'Hello Wrold')
b'SGVsbG8gV3JvbGQ='
>>> base64.b64decode(b'SGVsbG8gV3JvbGQ=')
b'Hello Wrold'
>>>
如果被编码的二进制数据对应的字符不是3的倍数,则编码出来的字符依然是4的倍数,它会自动在末尾补1个或2个‘=’,表示补的字节数,解码的时候会自动去掉。
由于url中的‘+’和‘/’会造成歧义,所有还有一种url safe的base64编码:
>>> base64.b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd++//'
>>> base64.urlsafe_b64encode(b'i\xb7\x1d\xfb\xef\xff')
b'abcd--__'
>>> base64.urlsafe_b64decode('abcd--__')
b'i\xb7\x1d\xfb\xef\xff'
它会把'+'变成'-',把'/'变成'_'。
struct
在python中,struct模块可以把二进制str与字节数组进行相互转换。
- 二进制str——>字节数组(bytes):pack
>>> import struct
>>> struct.pack('>I', 10240099)
b'\x00\x9c@c'
- 字节数组(bytes)——>二进制str:unpack
>>> struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')
(4042322160, 32896)
I:4字节无符号整数
H:2字节无符号整数。
hashlib
摘要算法:
又称哈希算法,散列算法;通过函数把任意长度的数据转换为长度固定的数据(16进制,二进制),一般不可逆,一般用在敏感信息加密上,如用户登陆密码,保存的不是原密码,而是转换的摘要。
分类:MD5,SHA1,SHA256,SHA512,CRC等。
- MD5:
产生16字节(128位)的校验值,一般用32位十六进制数表示。速度快,安全性高,被广泛应用于数据完整性校验、数据(消息)摘要、数据加密等。 - SHA:
SHA1为20字节(160位)、SHA256为32字节(256位)、 SHA384为48字节(384位)、SHA512为64字节(512位);产生的摘要长度更长,更安全,但转换速度就相对较慢。主要应用于CA和数字证书中。 - CRC:
出现时间较长,产生一个4字节(32位)的校验值,一般是以8位十六进制数。特点:简便,速度快,多用于文件校验,如简单文件校验(Simple File Verify – SFV),检测文件的完整性。
hashlib提供了几种常用的摘要算法,如MD5,SHA1等等。
- hashlib MD5
>>> import hashlib
>>> md5 = hashlib.md5()
>>> md5.update('today is goog day'.encode('utf-8'))
>>> md5.update('so lets learn python haha'.encode('utf-8'))
>>> md5.hexdigest()
'f766659f2862e8d4b3db9853c9e7dedb'
>>>
- hashlib SHA1
>>> import hashlib
>>> sha1 = hashlib.sha1()
>>> sha1.update('today is goog day'.encode('utf-8'))
>>> sha1.update('so lets learn python haha'.encode('utf-8'))
>>> sha1.hexdigest()
'2443a8c66f2098996114fb4a589e32fb82e070d1'
>>>
itertools
itertools提供了对操作可迭代对象的方法,返回一个Iterator。
- count(x):返回从x开始的步长为1的Iterator
>>> import itertools
>>> count1 = itertools.count(1)
>>> for i in count1:
print(i)
1
2
3
...
- cycle():把传入的序列无线重复下去
>>> import itertools
>>> C = itertools.cycle('AB')
>>> for n in C:
print(n)
A
B
A
B
A
B
A
- repeat():无限循环一个元素,也可以传入循环次数。
>>> import itertools
>>> R = itertools.repeat('A',3)
>>> for i in R:
print(i)
A
A
A
>>>
- takewhile(predicate, iterable) :用条件截取itertor中的一段
>>> import itertools
>>> C = itertools.count(1)
>>> takew = itertools.takewhile(lambda x:x<5,C)
>>> for n in takew:
print(n)
1
2
3
4
>>>
- chain(*iterables) :可以将几个序列当作重组的单个序列
>>> import itertools
>>> I = itertools.chain([1,2,3],'ABC')
>>> for i in I:
print (i)
1
2
3
A
B
C
>>>
- groupby(iterable, key=None) :把迭代器中相邻的重复元素挑出来放在一起
>>> for key, group in itertools.groupby('AAABBBCCAAA'):
... print(key, list(group))
...
A ['A', 'A', 'A']
B ['B', 'B', 'B']
C ['C', 'C']
A ['A', 'A', 'A']
图像处理
PIL:Python Imaging Library是python处理图像的标准库,仅支持python2.7。pyhton3用到的库是Pillow。
安装:pip install pillow
from PIL import Image
# 打开一个jpg图像文件,注意是当前路径:
im = Image.open('dog.jpg')
# 获得图像尺寸:
w, h = im.size
print('Original image size: %sx%s' % (w, h))
# 缩放到50%:
im.thumbnail((w // 2, h // 2))
print('Resize image to: %sx%s' % (w // 2, h // 2))
# 把缩放后的图像用jpeg格式保存:
im.save('thumbnail.jpg', 'jpeg')
- 模糊效果:
from PIL import Image,ImageFilter
im = Image.open('dog.jpg')
im2 = im.filter(ImageFilter.BLUR)
im2.save('dog_blur.jpg','jpeg')
原始图:
dog.jpg
dog_blur.jpg
- 绘图(生成字母验证图片)
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import random
# 随机字母:
def rndChar():
return chr(random.randint(65, 90))
# 随机颜色1:
def rndColor():
return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255))
# 随机颜色2:
def rndColor2():
return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))
# 240 x 60:
width = 60 * 4
height = 60
image = Image.new('RGB', (width, height), (255, 255, 255))
# 创建Font对象:
font = ImageFont.truetype('Arial.ttf', 36)
# 创建Draw对象:
draw = ImageDraw.Draw(image)
# 填充每个像素:
for x in range(width):
for y in range(height):
draw.point((x, y), fill=rndColor())
# 输出文字:
for t in range(4):
draw.text((60 * t + 10, 10), rndChar(), font=font, fill=rndColor2())
# 模糊:
image = image.filter(ImageFilter.BLUR)
image.save('code.jpg', 'jpeg')
code.jpg
图形界面
- Tkinter
Tkinter时python内置模块,无需安装任何包。
from tkinter import *
import tkinter.messagebox as messagebox
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
def createWidgets(self):
self.nameInput = Entry(self)
self.nameInput.pack()
self.alertButton = Button(self, text='Hello', command=self.hello)
self.alertButton.pack()
def hello(self):
name = self.nameInput.get() or 'world'
messagebox.showinfo('Message', 'Hello, %s' % name)
app = Application()
# 设置窗口标题:
app.master.title('Hello World')
# 主消息循环:
app.mainloop()
面向对象编程
自然界的一切都可以看做对象,如苹果,葡萄,香蕉等水果;长方形,正方形,圆形等图形;我们可以把这些事物进行归类,如水果,他们都有颜色,大小等属性,我们可以吃水果,存储水果等动作。在面向对象编程中,具有相同属性名称和方法我们叫做类,具体的东西称作实例。
- 类:创建实例的模板,它封装了数据和方法,具有通用性。如要管理学生的信息,学生有姓名,性别,成绩等相关属性,这些属性名称都是一样的,因此具有通用性。而输出学生的姓名,成绩这些动作也有一样的,也具有通用性。不同的是具体的属性,如每个学生都有自己唯一的名字,成绩等。
- 实例:根据类来创建,是具体的,唯一的。如创建学生“小明”这个实例,一旦创建,“小明”就拥有学生这个类的属性和方法。
- 举例:
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))
xiaoming = Student('xiaoming',90) #实例
xiaoming.print_score() #调用实例的方法
私有方法和属性
不想暴露给外部的变量和方法可以在前面加两个下划线__
,如变量__name
,方法__print_score(self)
,这样其他模块就不能访问了。这种私有变量和方法一般是只限内部调用时使用,防止外部随意修改,保证安全性。如果需要访问私有变量和方法,可以在模块内部创建set()和get()方法。
class Student(object): #类
def __init__(self, name, score):
self.__name = name #__name变成私有变量
self.score = score
def __print_score(self):
print('%s: %s' % (self.name, self.score))
def getName(self): #创建get方法,获取名字
return self.__name
def setName(self,name): #创建set方法,设置名字
self.__name = name
xiaoming = Student('xiaoming',90) #实例
xiaoming.getName() #调用get方法
xiaoming.setName('cuihua') #调用set方法
xiaoming.getName() #查看是否设置成功
xiaoming.__print_score() #调用私有方法,会报错
继承和多态
- 继承
子类继承父类后子类就拥有父类的全部属性和方法:
class Animals(): #父类
def run(self):
print('Animal is running')
class Dog(Animals): #子类继承父类
pass
dog = Dog() #实例化
dog.run() #调用继承过来的方法
子类也可以重写父类的方法:
class Animals(): #父类
def run(self):
print('Animal is running')
class Dog(Animals): #子类继承父类
def run(self): #重写父类的方法
print('Dog is running')
dog = Dog() #实例化
dog.run() #调用继承过来的方法
- 多态
子类继承了父类,子类就属于父类,在需要父类做参数的方法中,子类也可以做参数。
def runTwice(animals):
animals.run()
animals.run()
runTwice(Animals()) #调用
runTwice(Dog())
- 静态语言与动态语言
在python中,只要该类中存在run()方法,都是可以把此类作为参数传入runTwice()方法中,此类称为:鸭子类型。而在Java中,必须传入Animals或继承于Animals的类。
获取对象信息
获取对象信息的方法这里介绍有三种:
- type
- isinstance
- dir
type
type('abc') #返回:<class 'str'>
type(123) #返回:<class 'int'>
type(abs) #返回:<class 'builtin_function_or_method'>
type('abc')==str #返回:True
使用types模块中的常量判断是否为函数:
import types
def sum():
return 1 + 2
type(sum)==types.FunctionType #返回True
type(abs)==types.BuiltinFunctionType #返回True
type(lambda x: x)==types.LambdaType #返回True
type((x for x in range(10)))==types.GeneratorType #返回True
isinstance
使用isinstance判断:
isinstance('abc',str)
isinstance(123,int)
isinstance(123,(int,str))
也可以判断一个类:
class Animals(): #父类
def run(self):
print('Animal is running')
class Dog(Animals): #子类继承父类
pass
dog = Dog() #实例化
isinstance(dog,Animals) #返回True
isinstance(dog,Dog) #返回True
dir
使用dir返回一个list,这个list包含全部的属性和方法。
dir(Dog) #Dog()见isinstance
返回:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'run']
由上可见:除了run方法,还有很多以'__'开头和结尾的特殊方法。
配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:
hasattr(Dog,'run') #True
getattr(Dog,'run') #<function Animals.run at 0x0000000002F041E0>
setattr(Dog,'a',10) #增加一个属性
getattr(Dog,'a') #获取刚增加的属性
getattr(Dog,'b') #无此属性,会报错
getattr(Dog,'b',5) #增加默认 运行正常
slots
python是一种动态语言,可以在类定义之后,增加属性或方法:
- 增加属性:
class calcu(): #创建一个类
def sum(self,a,b):
return a + b
myCalcu = calcu()
myCalcu.c = 5 #增加一个属性
myCalcu.c #返回 5
- 增加方法:
通过types模块的MethodType可以增加方法:
def multi(self,a,b): #定义一个方法
return a * b
from types import MethodType
myCalcu.multi = MethodType(multi,myCalcu) #给实例增加方法
myCalcu.multi(2,3) #调用方法
这样增加的方法只能针对当前实例,对于类是无效的。如果想增加类的方法和属性,可以直接条用类:
calcu.d = 5 #增加类的属性
calcu.d #返回5
def divide(self,a,b):
return a / b
calcu.divide= divide #给实例增加方法
myCalcu2 = calcu()
myCalcu2.divide(4,2)
- 固定绑定的属性和方法,在定义类时使用slots进行限定:
class calcu():
__slots__ = ('e','f') #__slots__绑定
def sum(self,a,b):
return a + b
myCalcu3 = calcu()
myCalcu3.e = 6
myCalcu3.g = 7 #报错
注意:使用__slots__
仅对当前类起作用,对子类无效
@property
@property也是一个装饰器,它可以简化方法的调用:
class Student(object):
@property
def score(self):
return self._score
@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
调用:
>>> s = Student()
>>> s.score = 89
>>> s.score
89
>>>
通过property的就可以达到和get,set一样的效果,且调用简单方便。
多重继承
python允许一个类继承多个类,这样这个类就拥有了几个类的属性和方法,如:
class Runnable():
def run(self):
print('I can run')
class Mammal():
def mamal(self):
print('I am make baby')
class Dog(Runnable,Mammal): #多重继承
pass
定制类
类中默认有些以'__'开头和结尾的特殊方法和属性,如__str__,__repr__,__len__
,他们能完成特定的用途。
__str__
class Helloworld():
pass
看看直接打印这个类会得到什么:
>>> print(Helloworld())
<__main__.Helloworld object at 0x0000000002F065C0>
返回一串带地址的字符串。那如果想要返回一些能看懂的信息呢?就需要用到__str__
这个特殊方法。
class Helloworld():
def __str__(self):
return 'Hello World'
打印:
>>> print(Helloworld())
Hello World
如果去掉print
函数:
>>> Helloworld()
<__main__.Helloworld object at 0x0000000002F065C0>
打印的和不添加__str__
的Helloworld()
是一样样的,其实在函数内部它打印的是__repr__
这个特殊方法。修改这个方法:
class Helloworld():
def __str__(self):
return 'Hello World'
__repr__ = __str__
打印:
>>> Helloworld()
Hello World
结果打印的就是__str__
返回的字符串。
-
__iter__
通过__iter__
方法可以让一个类使用for···in···
循环。它返回一个迭代对象,然后循环调用__next__
方法,直到遇到StopIteration
错误时退出循环。
class counter():
def __init__(self):
self.a = 0
def __iter__(self):
return self
def __next__(self):
self.a = self.a + 1
if(self.a == 10):
raise StopIteration()
return self.a
使用for循环看看效果:
>>> for i in counter():
print(i)
1
2
3
4
5
6
7
8
9
-
__getitem__
如果想获取类循环中某一个特定的值,如counter()[5],__getitem__()
方法能实现这个功能:
class counter():
def __init__(self):
self.a = 0
def __getitem__(self,n):
return n
测试:
>>> counter()[5]
5
>>>
如果需要类提供切片等其他功能,借助动态类的功能依旧可以实现。
-
__getattr__
调用类中不存在的属性或方法一般会返回异常:
class Hello():
def __init__(self):
self.name = 'hello'
调用属性:
>>> Hello().name
'hello'
>>> Hello().sex #返回异常
Traceback (most recent call last):
File "<pyshell#202>", line 1, in <module>
Hello().sex
AttributeError: 'Hello' object has no attribute 'sex'
>>>
'getattr`就是可以调用为定义的属性和方法的。
class Hello():
def __init__(self):
self.name = 'hello'
def __getattr__(self,attr):
if attr == 'sex':
return 'sex'
return 'error attr'
再次调用:
>>> Hello().sex
'sex'
>>> Hello().max
'error attr'
>>>
调用sex属性是有返回,而调用max属性时,返回'error attr'。如果没有return 'error attr'
,则不报异常,返回为none
。
注意:除了调用属性,也可以调用方法;如果类中已存在属性或方法,就不会去调用__getattr__
中的属性方法。
枚举
枚举类通常用在固定的数据中,如每年的十二个月,一个星期的周几等,相对于使用变量,枚举类型可以让数据变得安全,也可以更方便的访问。
可以这样创建一个Enum:
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
通常,会创建一个class:
from enum import Enum
class Color(Enum):
Red = 'red'
Green = 'green'
Black = 'black'
访问:
>>> Color.Red
<Color.Red: 'red'>
>>> print(Color.Red)
Color.Red
>>> print(repr(Color.Red))
<Color.Red: 'red'>
>>> for color in Color:
print(color)
Color.Red
Color.Green
Color.Black
>>>
### 元类
理解:在python中一切皆为对象。
元类就是创建类的类,元类也是对象,它可以创建类,所以类也是元类的实例。通俗理解:元类——>类——>实例。
通过元类可以动态的创建一个类,而不需要先定义,再使用。
通常,type用来查看对象的属性,元类也使用type关键字,type就是创建元类的类。创建格式:
```python
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
创建一个Hello类:
def oHello(self):
print('Hello')
Hello = type('Hello',(object,),{'oHello':oHello})
使用元类创建的类与普通类一样:
>>> h = Hello()
>>> h.oHello()
Hello
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。由于metaclass很少用到,作者水平有限,无法解释。可以访问:
深刻理解Python中的元类(metaclass)
进程和线程
多进程
- 使用multiprocessing的Process类创建子进程:
from multiprocessing import Process
import os
#在子进程中执行的代码
def createProcessing(name):
print('this is a child process:%s(%d)' %(name,os.getpid()))
if __name__ == '__main__':
print('this is a parent process:%d' %os.getpid())
#创建子进程,传函数名和参数
P = Process(target= createProcessing,args=('test',))
print('start child process')
#打开子进程
P.start()
#主进程与子进程的同步
P.join()
print('Child process END!!!')
Processing能单独的创建子进程,如果需要启动很多子进程呢?
- Pool
Pool能同时启动多个进程。
from multiprocessing import Pool
import time
import os
def createProcess(name):
print('Child process start:%s(%d)' %(name,os.getpid()))
time.sleep(0.2)
print('Child process end:%s(%d)' % (name, os.getpid()))
if __name__ == '__main__':
print('Parent process :%d' %os.getpid())
P = Pool(processes=4) #同时跑4个进程,默认电脑内核数
for i in range(5):
P.apply_async(createProcess,args=(i,))
print('Waiting for all subprocess done...')
P.close()#添加进所有的子进程并启动子进程
P.join()#同步
print('END')
- 子进程
有时候需要启动带输入输出的外部子进程,并获得返回码。可以使用subprocess模块。
建议使用run()方法,run()方法是从python3.5开始设立的。更高级的可以使用Popen接口。
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, encoding=None, errors=None)
举例:
import subprocess
print('$ nslookup')
p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate(b'set q=mx\npython.org\nexit\n')
print(output.decode('utf-8'))
print('Exit code:', p.returncode)
上面的代码相当于在命令行执行命令nslookup,然后手动输入:
set q=mx
python.org
exit
- 进程间通信
python提供Queue、Pipes等多种方式来交换数据。我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:
from multiprocessing import Process, Queue
import os, time, random
# 写数据进程执行的代码:
def write(q):
print('Process to write: %s' % os.getpid())
for value in ['A', 'B', 'C']:
print('Put %s to queue...' % value)
q.put(value)
time.sleep(random.random())
# 读数据进程执行的代码:
def read(q):
print('Process to read: %s' % os.getpid())
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()
运行结果如下:
Process to write: 50563
Put A to queue...
Process to read: 50564
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.
子线程
同时执行多条任务不仅可以开启多进程,也可以在进程中开启子线程。
threading模块可以创建子线程。
import threading
import time
def loop():
print('%s is running...'% threading.current_thread().name)
time.sleep(2)
print('%s is END...' % threading.current_thread().name)
print('%s is running...'% threading.current_thread().name) #主线程开始
t = threading.Thread(target= loop,name = 'loopthread') #创建子线程
t.start() #开始子线程
t.join() #同步
print('%s is END...'% threading.current_thread().name)
运行如下:
C:\Users\renyangfar\AppData\Local\Programs\Python\Python36\python.exe F:/Python/PythonStudy/Project/Thread.py
MainThread is running...
loopthread is running...
loopthread is END...
MainThread is END...
开启的主线程名字为:MainThread,在开启子线程时可以提供子线程名称,如:loopthread,如果不提供,默认为:Thread-1,Thread-2,Thread-2...
- lock
多线程可以同时访问同一个变量,这样会造成变量的不可预测性。如:
import time, threading
# 假定这是你的银行存款:
balance = 0
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
change_it(n)
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)
结果不一定是0。
而要想在同一个时刻只有一个子线程可以访问balance 变量,需要加一把锁lock。
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
这样,当几个现场执行到lock.acquire()
时,只有一个线程能获取到。其他的线程要继续等待,知道获得锁的线程释放锁lock.release()
。
另外:Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。
ThreadLocal
多线程中,如果一个属性需要绑定一个线程,线程在调用自己的属性。python中的Thread Local很好的解决了这样问题。假设每个线程管理一种花,需要设置花的颜色并打印出来。
import threading
flower = threading.local() #folwer绑定了线程
def print_flower():
clor = flower.clor #直接调用
print('%s\'s clor is %s'% (threading.current_thread().name,clor))
def manager_flower(clor):
flower.clor = clor
print_flower()
t1 = threading.Thread(target= manager_flower,args=('RED',),name = 'rose')
t2 = threading.Thread(target= manager_flower,args=('BLUE',),name = 'lavender')
t1.start()
t2.start()
t1.join()
t2.join()
运行结果:
C:\Users\renyangfar\AppData\Local\Programs\Python\Python36\python.exe F:/Python/PythonStudy/Project/ThreadLocal.py
rose's clor is RED
lavender's clor is BLUE
程序中flower
是在全局变量定义的,但是它会根据不同的子线程而有不同的属性。
web学习
基于python开发web的框架有很多,简要介绍几种常用的框架:
- Flask
轻量级框架,适合初学者学习,只提供常用的核心组件,可以自定义模块。开发的网站:果壳网 - Django
全能型框架,能开发大型网站,但涉及知识广,自带Admin管理后台,可重用模块众多。 - Tornado
高性能框架,支持异步 处理的功能,可以做长连接。开发的网站:知乎 - 还有web.py web2.py bottle Pyramid等。
涉及知识:
- WSGI:Web服务网关接口(Web Server Gateway Interface,简称“WSGI”),是一种在Web服务器 和Python Web应用程序或框架之间的标准接口。
- WSBI服务器:
1:Gunicorn:是一个纯Python WSGI服务器,Gunicorn是如今新Python web应用程序的推荐选择。
2:Waitress:Waitress 是一个纯Python WSGI服务器,声称具备“非常可接受的性能”。
网络编程
网络通信:两个进程间的通信
进程确定:IP地址+端口号
TCP客户端
TCP是可靠传输协议,需要先建立连接,然后传输数据,最后释放连接。主动发起连接的是客户端,等待连接的是服务器。
建立连接
要建立连接,需要创建一个socket,而常见socket,需要知道目标计算机的ip地址和端口号,还需要指定协议类型。
import socket
# 创建一个socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接
s.connect(('www.baidu.com', 80))
socket.AF_INET表示IPv4协议,如果使用IPv6,则为socket.AF_INET6,socket.SOCK_STREAM表示使用面向流的TCP协议。
建立连接时制定ip地址和端口号,域名服务器会把'www.baidu.com'自动转为ip地址,80是web服务器默认的端口。SMTP服务是25端口,FTP服务是21端口。端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。
发送请求
连接建立后,就可以请求数据了。
# 发送数据:
s.send(b'GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n')
接受数据
# 接收数据:
buffer = []
while True:
# 每次最多接收1k字节:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
关闭连接
# 关闭连接:
s.close()
获取到数据后,根据http协议即可获取服务器返回的数据,包括网页本身和http头。
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
# 把接收的数据写入文件:
with open('baidu.html', 'wb') as f:
f.write(html)
TCP服务器
创建socket
服务器接收客户端的请求,所以需要先建立一个socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
绑定ip地址和端口号
s.bind(('127.0.0.1',9999))
其中127.0.0.1为本机地址,只有运行在本机的客户端才可以访问。另外每台电脑可以有几块网卡,多个ip地址,可以用0.0.0.0绑定到所有的网络地址。
运行监听客户端访问
s.listen(5)
5表示最多接受5个客户端连接。
服务器程序
while True:
sock,addr = s.accept()
t = threading.Thread(target= clent_connec,args=(sock,addr))
t.start()
用while True:永久运行服务器,知道用ctrl+c关闭服务。s.accept()会等待连接客户端,返回客户端的socket和address。因为每个服务可以同时接受好几个客户端请求,所以用多线程或多进程接受请求。
def clent_connec(sock, addr):
print('start new client named %s' % sock)
sock.send(b'hello')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
sock.close()
在服务器响应程序中,先返回“hello”给客户端,接下来接受客户端数据并在数据前加”hello“返回给客户端,最后关闭socket。
UDP
UDP不同于TCP,不需要建立连接,客户端直接可以向服务器传输数据,数据传输快,但不保证传输的有效性。
服务器端程序:
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.bind(('127.0.0.1',9999))
while True:
data,addr = s.recvfrom(1024)
print('receive data form client based udp:%s' %data,addr)
s.sendto(b'hello,%s'%data)
客户端程序:
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
l = [b'good',b'ok',b'thanks']
for i in l:
s.sendto(i,('127.0.0.1',9999))
print(s.recv(1024).decode('utf-8'))
s.close()
Flask学习笔记
前言
- 特点:保持核心,简单而易于扩展。数据库集成、表单验证、上传处理、各种各样的开放认证技术等需要扩展实现。对pyton2的支持优于python3
- Flask依赖两个库- Jinja2 模板引擎和 Werkzeug WSGI 工具集。
WSGI:在 Web 应用和多种服务器之间的标准 Python 接口
Jinja2:负责渲染模板
安装Flask
打开cmd,运行:
pip install flask
上代码:
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return '<h1>Home</h1>'
@app.route('/signin', methods=['GET'])
def signin_form():
return '''<form action="/signin" method="post">
<p><input name="username"></p>
<p><input name="password" type="password"></p>
<p><button type="submit">Sign In</button></p>
</form>'''
@app.route('/signin', methods=['POST'])
def signin():
# 需要从request对象读取表单内容:
if request.form['username']=='admin' and request.form['password']=='password':
return '<h3>Hello, admin!</h3>'
return '<h3>Bad username or password.</h3>'
if __name__ == '__main__':
app.run()
运行结果:
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
说明web服务已经启动成功,等待请求:
于是打开浏览器输入网址:
http://localhost:5000/
显示:
image.png输入网址:
http://localhost:5000/signin
显示:
输入用户名:admin,密码:password,点击sigin in
显示:
使用模板
开发web,服务器需要给browser返回页面,如return '<h1>Home</h1>'
,如果有大量的页面内容需要返回,这样写就很容易出错,普遍的方法是使用模板,当需要返回页面时,返回一套页面模板,这样就把业务逻辑和显示区分开来,加上模型,就是常用的mvc编程模型。
这里以jinja2模板为例实现上一节的登陆功能:
先用pip安装jinja2:
pip install jinja2
在app.py中代码:
from 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()
同级目录下新建templates文件夹,并在此文件夹下新建home.html,form.html,signin-ok.html,如图:
image.png- home.html
用来显示首页的模板:
<html>
<head>
<title>Home</title>
</head>
<body>
<h1 style="font-style:italic">Home</h1>
</body>
</html>
- form.html
用来显示登录表单的模板:
<html>
<head>
<title>Please Sign In</title>
</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 In</button></p>
</form>
</body>
</html>
- signin-ok.html
登录成功的模板:
<html>
<head>
<title>Welcome, {{ username }}</title>
</head>
<body>
<p>Welcome, {{ username }}!</p>
</body>
</html>
重新运行程序,可以达到很好的效果。
网友评论