Author:@南非波波
声明:本电子书为为本人从图灵社区所购,涉及版权,请读者不要私自传播。本平台仅提供在线阅读功能。如果您觉得此书帮助了您,同时也想帮作者分摊一部分电子书版权费。欢迎通过微信二维码进行资助,非常感谢。
Author:@南非波波
声明:本电子书为为本人从图灵社区所购,涉及版权,请读者不要私自传播。本平台仅提供在线阅读功能。如果您觉得此书帮助了您,同时也想帮作者分摊一部分电子书版权费。欢迎通过微信二维码进行资助,非常感谢。
Author:@南非波波
课程大纲:
http://www.cnblogs.com/alex3714/articles/5474411.html
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import random,time
#m冒泡排序
def bubble_up1(array):
'''
m冒泡排序算法
:param array:
:return: count: 6190862 time: 6.706383466720581
'''
count = 0
for i in range(len(array)):
for j in range(len(array) - 1 - i):
if array[j] > array[j + 1]:
temp = array[j + 1]
array[j + 1] = array[j]
array[j] = temp
count += 1
print("count:", count)
print("array:", array)
def bubble_up2(array):
'''
m冒泡排序算法
:param array:
:return:count: 5000 time: 3.825218915939331
'''
count = 0
for i in range(len(array)):
for j in range(len(array) - 1 - i):
big_temp = j
if array[big_temp] > array[j + 1]:
big_temp = j + 1
temp = array[big_temp]
array[i] = array[big_temp]
array[big_temp] = temp
count += 1
print("count:", count)
print("array:", array)
if __name__ == '__main__':
array = []
for i in range(5000):
array.append(random.randrange(1000))
time_start = time.time()
bubble_up2(array)
time_end = time.time()
#
print(array[0:100])
print("time:",time_end - time_start)
def select1(array):
'''
选择排序算法v1
:param array:
:return:count: 1996750 time: 3.2961885929107666
'''
count = 0
for i in range(len(array)):
for j in range(i,len(array)):
if array[i] > array[j]:
temp = array[j]
array[j] = array[i]
array[i] = temp
count += 1
print("count:",count)
print("array:",array)
def select2(array):
'''
选择排序算法v2
:param array:
:return:count: 5000 time: 2.4801418781280518
'''
count = 0
for i in range(len(array)):
smallest_index = i
for j in range(i,len(array)):
if array[smallest_index] > array[j]:
smallest_index = j
temp = array[smallest_index]
array[smallest_index] = array[i]
array[i] = temp
count += 1
print("count:",count)
print("array:",array)
def insert1(array):
'''
插入排序算法
:param array:
:return: count: 4999 time: 3.685210704803467
'''
count = 0
for index in range(1, len(array)):
current_val = array[index] # 先记下来每次大循环走到的第几个元素的值
position = index
while position > 0 and array[
position - 1] > current_val: # 当前元素的左边的紧靠的元素比它大,要把左边的元素一个一个的往右移一位,给当前这个值插入到左边挪一个位置出来
array[position] = array[position - 1] # 把左边的一个元素往右移一位
position -= 1 # 只一次左移只能把当前元素一个位置 ,还得继续左移只到此元素放到排序好的列表的适当位置 为止
array[position] = current_val # 已经找到了左边排序好的列表里不小于current_val的元素的位置,把current_val放在这里
count += 1
print("count:", count)
print(array)
def quick_sort(array,start,end):
'''
快速排序算法
:param array:
:param start:
:param end:
:return:time: 0.03600192070007324
'''
if start >= end:
return
k = array[start]
left_flag = start
right_flag = end
while left_flag < right_flag:
while array[right_flag] > k:
right_flag -= 1
temp = array[right_flag]
array[left_flag] = array[right_flag]
array[right_flag] = temp
while array[left_flag] <= k:
left_flag += 1
temp = array[left_flag]
array[left_flag] = array[right_flag]
array[right_flag] = temp
quick_sort(array,start,left_flag - 1)
quick_sort(array,left_flag + 1,end)
Author:@南非波波
课程大纲:
http://www.cnblogs.com/alex3714/articles/5457672.html
两种方式:使用pycharm工具进行创建
使用manage命令进行创建
|
|
|
|
|
|
使用主页的头部进行继承,然后将主体进行重写
index.html
|
|
year.html
|
|
说明:
|
|
模板的继承和重写支持:子继承父、孙继承子
|
|
|
|
models
|
|
由于django需要MySQLdb进行连接mysql数据库,需要安装MySQLdb模块和vc++ for python2.7环境包。
|
|
|
|
shell>>python2 manage.py shell
\>>>from article.models import Publisher
\>>>publisher_list = Publisher.objects.all() #查询所有的出版社列表
\>>>publisher_list[1].name #查询第2个出版社的名称
\>>>publisher_list[1].id #查询第2个出版社的id
\>>> p1 = Publisher.objects.create(name='SWht',address='haidian',city='beijing',state_province='CA',country='CN',website='http://www.songqingbo.cn')
\>>>p2 = Publisher(name='SWht2',address='haidian1',city='beijing',state_province='CA',country='CN',website='http://www.songqingbo.cn')
\>>>p2.save() #增加数据两种方式,1.使用对象的objects的create方法进行数据的创建,2.使用对象的save方法进行数据的保存。
\>>> from article.models import Author
\>>> author_list = Author.objects.all()
\>>> author_list
[<Author: <shen test>>, <Author: <alix sds>>]
\>>> Author.objects.filter(first_name='shen')
[<Author: <shen test>>]
\>>> Author.objects.get(first_name='shen')
<Author: <shen test>>
\>>> Author.objects.get(id='1')
<Author: <shen test>>
\>>> Author.objects.get(id='2')
<Author: <alix sds>>
注意:如果查询没有返回结果也会抛出异常
\>>> Author.objects.order_by("first_name")
[<Author: <alix sds>>, <Author: <shen test>>]
\>>> Author.objects.order_by("id")
[<Author: <shen test>>, <Author: <alix sds>>]
\>>> author = Author.objects.get(last_name='test')
\>>> author.last_name = 'diaoxia'
\>>> author.save()
\>>> Author.objects.all()
[<Author: <shen diaoxia>>, <Author: <alix sds>>]
\>>> author = Author.objects.get(first_name='alix')
\>>> author
<Author: <alix sds>>
\>>> author.delete()
(2L, {u'article.Book_authors': 1L, u'article.Author': 1L})
\>>> Author.objects.all()
[<Author: <shen diaoxia>>]
Author:@南非波波
课程大纲:
http://www.cnblogs.com/wupeiqi/articles/4491246.html
http://www.cnblogs.com/wupeiqi/articles/4508271.html
|
|
Font Awesome
http://fontawesome.io/
a、图片,自己找图片,挖洞
b、现成的图标
css
使用样式
--以前版本
css
图片库
使用样式
-- 现在
css
字体文件
使用样式
c、css
字体文件
样式
=====》 大图片
|
|
#!/usr/bin/env python
#coding:utf-8
import socket
def handle_request(client):
buf = client.recv(1024)
client.send("HTTP/1.1 200 OK\r\n\r\n")
client.send("Hello, Seven")
def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost',8000))
sock.listen(5)
while True:
connection, address = sock.accept()
handle_request(connection)
connection.close()
if __name__ == '__main__':
main()
MVC:Models Views Controllers
数据库操作 模板 处理请求的函数
代码块的归类结构
MTV:Models Templates Views
数据库操作 模板 处理请求的函数
Django框架属于MTV框架。程序需要的操作的:
1.models
2.Templates
3.Views
4.urls
pip install django 1.9.5
使用命令创建
django-admin startproject demo 创建projects
cd demo
python manage.py startapp app0 创建应用
使用pycharm进行创建项目和应用
进入项目, python manage.py runserver 127.0.0.1:8000
python manage.py makemigrations #生成配置文件
python manage.py migrate #根据配置文件创建数据库相关 表
创建超级用户名 python manage.py createsuperuser
静态路由
动态路由
安照顺序,第n个匹配的数据交给函数的第n个参数,严格按照顺序
url(r'^page/(\d+)/(\d+)',views.page)
模板的方法,将匹配的参数,传给指定的形式参数
url(r'^page/(?P<n1>\d+)/(?P<n2>\d+)',views.page)
二级路由
app01
urls.py
project name
URL:app01 ->include("app01.urls")
ORM框架
code first
自己写类 -->数据库表
db first
自己写命令操作数据库-->更新类
使用类进行数据操作
创建类
from django.db import models
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
age = models.IntegerField()
配置
setting
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
]
使用命令根据类创建表
python manage.py makemigrations #生成配置文件
python manage.py migrate #根据配置文件创建数据库相关 表
默认表名:
appname_classname
d.
views中导入models
e.
POST提交数据
settings里操作:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Author:@南非波波
课程大纲:
http://www.cnblogs.com/wupeiqi/articles/5369773.html
http://www.php100.com/manual/jquery/
$('#nid') 根据id找到某个标签
$('.nid') 根据class查找
$('.nid div #nid') 根据class查找下面的div标签下面的id
$('.nid,div,#nid') 查找到class或div或id条件的查找到
$('li:eq(0)') 查到到第一个li标签行
$('li').eq(0) 查到到第一个li标签行
属性
添加指定属性,并删除兄弟的属性
$(ths).addClass('current').siblings().removeClass('current');
attr:
其他所有标签都适用,除checkbox、redio之外
prop:
checkbox、redio
jQuery循环:
var userList = ['swht','shen','test'];
$.each(userList,fun(i,item){
console.log(i,item);
})
文档处理
增加
append(content|fn)
在指定的标签内部后面追加
appendTo(content)
把指定的某个标签追加某个标签内部后面
prepend(content|fn)
在指定的标签内部前面追加
prependTo(content)
把指定的某个标签追加某个标签内部前面
包裹
wrap(html|ele|fn)
unwrap()
wrapAll(html|ele)
wrapInner(html|ele|fn)
删除
empty()
remove([expr])
detach([expr])
复制
clone([Even[,deepEven]])
事件
绑定事件:
1. 直接绑定ready(fn) 文本加载完之后执行事件绑定
1. $(document).ready(function(){
})
2. $(function(){
})
2. 委派delegate(s,[t],[d],fn)
1. $('ul').delegate('li','click',function(){
})
3. bind(type,[data],fn)
1. $('li').click(function(){
})
2. $('li').bind('click')
3. unbind
AJAX
异步的javascript和xml
ajax是对javascript和Dom的封装。
ajax容易出现跨域的问题。
$.ajax({
url:"",
data:{},
type:"",
dateType:"",
jsonp:"",
jsonpCallback:"",
sucess:function(){},
error:function(){}
})
JQuery扩展
(function(arg){
arg.extend({
qinghua: function() {
return "SB";
},
qinghua1: function() {
return this.each(function() { this.checked = false; });
}
});
arg.fn.extend({
sanjiang:function(){
return "DSB"
}
});
})(jQuery);
其他
30款最好的 Bootstrap 3.0 免费主题和模板
http://www.cnblogs.com/lhb25/p/30-free-bootstrap-templates.html
例子:
tab菜单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tab</title>
<style>
.tab-box .box-menu{
background-color: #DDDDDD;
border: 1px solid #DDDDDD;
height: 33px;
line-height: 33px;
}
.box-menu a{
border-right: 1px solid #664747;
padding: 10px;
background-color: #425a66;;
}
.tab-box .box-body{
border: 1px solid #dddddd;
}
.hide{
display: none;
}
.current{
background-color: white;
color: black;
border-top: 2px solid red;
}
</style>
</head>
<body>
<div class="tab-box">
<div class="box-menu">
<!--所有菜单-->
<a menu1="c1" onclick="ChangeTab(this);" class="current">菜单一</a>
<a menu1="c2" onclick="ChangeTab(this);">菜单二</a>
<a menu1="c3" onclick="ChangeTab(this);">菜单三</a>
</div>
<div class="box-body">
<!--所有内容-->
<div id="c1">内容一</div>
<div id="c2" class="hide">内容二</div>
<div id="c3" class="hide">内容三</div>
</div>
</div>
<script src="jquery-2.2.3.js"></script>
<script>
function ChangeTab(ths){
$(ths).addClass('current').siblings().removeClass('current');
var contentId = $(ths).attr('menu1');
var temp = "#" + contentId;
$(temp).removeClass('hide').siblings().addClass('hide');
}
</script>
</body>
</html>
全选、反选、取消
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全选、反选、取消</title>
</head>
<body>
<div>
<input type="button" value="全选" onclick="selectAll();" />
<input type="button" value="反选" onclick="selectInvert();" />
<input type="button" value="取消" onclick="clearAll();" />
</div>
<div>
<table border="1">
<tr>
<td>
<input type="checkbox" />
</td>
<td>
第一章
</td>
<td>
第一篇
</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>
第二章
</td>
<td>
第一篇
</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>
第三章
</td>
<td>
第一篇
</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>
第四章
</td>
<td>
第一篇
</td>
</tr>
</table>
</div>
<script src="jquery-2.2.3.js"></script>
<script>
function selectAll(){
$("table input[type='checkbox']").prop('checked',true);
}
function selectInvert(){
$("table input[type='checkbox']").each(function(){
var isChecked = $(this).prop('checked');
if(isChecked){
$(this).prop('checked',false);
}else{
$(this).prop('checked',true);
}
});
}
function clearAll(){
$("table input[type='checkbox']").prop('checked',false);
}
</script>
</body>
</html>
ajax_jsonp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ajax_jsonp</title>
</head>
<body>
<input type="button" value="获取电视节目" onclick="Ajax_jsonp();"/>
<dev id="contaner">
</dev>
<script src="jquery-2.2.3.js"></script>
<script>
function Ajax_jsonp(){
$.ajax({
url:"http://www.jxntv.cn/data/jmd-jxtv2.html",
data:{},
type:"GET",
dataType:"jsonp",
jsonp:"callback",
jsonpCallback:"list",
success:function(arg){
console.log(arg);
var jsonArray = arg.data;
$.each(jsonArray,function(k,v){
var week = v.week;
var label = "<h1>" + week + "</h1>";
$("#contaner").append(label);
var listArray = v.list;
$.each(listArray,function(kk,vv){
var link = vv.link;
var name = vv.name;
var time = vv.time;
var labelNew = "<a href='" + link + "'>" + time+name + "</a><br/>";
$("#contaner").append(labelNew);
})
})
},
error:function(arg){
console.log(arg);
}
})
}
</script>
</body>
</html>
Author:@南非波波
课程大纲:
day13
http://www.cnblogs.com/wupeiqi/articles/5369773.html
js
使页面动起来的一门语言,解释器就是浏览器的引擎
dom
提供一套api
jQuery
封装的JS和dom的类库
1. 存在形式:
文件
标签
2. 放置位置:
原则上可以存在head 和body,但是当页面请求不到js的时候就会一直在等待。建议将js代码放在body底部
3. 声明变量:
name = "swht"; //全局变量
age = 18; //局部变量
4. 注释:
当行注释: //
多行注释: /* .. */
每行代码结束需要加分号(;)
5. 类型:
数字
字符串
数组(字典)
6. 类型转换:
var age = 18;
var age = Number(18);
Number("123");
parseInt('123'); //将字符串转换成数字类型
var num = 18.9;
num1 = parseInt(num); //将数字类型转换成整型数字输出
num2 = parseFloat(num); //将数字类型转换成浮点型数字输出
console.log("num1:",num1,typeof num1,"num2:",num2,typeof num2); //控制台打印转换后的值和类型
//输出结果:num1: 18 number num2: 18.9 number
7. 控制台打印:
var age = "18";
var n1 = 1,n2 = 3,n3 = 4; //单行可以声明多个变量并赋值
console.log(age,typeof age); //控制台输出变量的值和类型
1. 去除字符串左右空格:
var name = "swht ";
name.trim();
2. 按索引取值:
var name = "swht";
name.charAt(1);
3. search:
name.search("w"); //返回字符所在的索引值
4. split:
name.split(""); //将字符串转换成数组
["s", "w", "h", "t", " ", " ", " "]
5. xx
|
|
while(true){
countine;
break;
}
var name = '1';
switch (name){
case "1":
console.log(1);
break;
case "2":
console.log(2);
break;
case "3":
console.log(3);
break;
default:
console.log('default');
break;
}
var name = "swht";
if (name == "alex"){
console.log(err);
}else if (name == "hh"){
console.log(true);
}else {
console.log("你逗呢!");
}
var name = "swht";
try {
if (name == "shen"){
console.log("err");
}else {
console.log("false");
}
}catch (e){
console.log(e);
}finally {
console.log("finally");
}
//函数的声明
function func1(arg){
return true;
}
//匿名函数
var func2 = function(arg){
return true;
}
//自执行函数,一般用在jq封装类库时使用
(function(arg){
console.log(arg);
})('123')
function Foo(name,age){
this.Name = name;
this.Age = age;
this.Func = function(arg){
return this.Name +arg;
}
}
var obj = new Foo('swht',22)
console.log(obj.Name);
console.log(obj.Age);
var ret = obj.Func('haha');
console.log(ret);
文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口。它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式。我们最为关心的是,DOM把网页和脚本以及其他的编程语言联系了起来。DOM属于浏览器,而不是JavaScript语言规范里的规定的核心内容。
注:一般说的JS让页面动起来泛指JavaScript和Dom
document.getElementById('id');
document.getElementsByName('name');
document.getElementsByTagName('tagname');
innerText
innerHTML
var obj = document.getElementById('nid')
obj.innerText # 获取文本内容
obj.innerText = "hello" # 设置文本内容
obj.innerHTML # 获取HTML内容
obj.innerHTML = "<h1>asd</h1>" # 设置HTML内容
特殊的:
input系列
textarea标签
select标签
value属性操作用户输入和选择的值
方式一:
var obj = document.createElement('a');
obj.href = "http://www.apicloud.com";
obj.innerText = "APICloud";
var container = document.getElementById('container');
//container.appendChild(obj);
//container.insertBefore(obj, container.firstChild);
//container.insertBefore(obj, document.getElementById('hhh'));
方式二:
var container = document.getElementById('container');
var obj = "<input type='text' />";
container.innerHTML = obj;
// 'beforeBegin', 'afterBegin', 'beforeEnd', 'afterEnd'
//container.insertAdjacentHTML("beforeEnd",obj);
var obj = document.getElementById('container');
固定属性
obj.id
obj.id = "nid"
obj.className
obj.style.fontSize = "88px";
自定义属性
obj.setAttribute(name,value)
obj.getAttribute(name)
obj.removeAttribute(name)
document.geElementById('form').submit()
console.log()
alert()
confirm()
// URL和刷新
location.href
location.href = "url" window.location.reload()
// 定时器
setInterval("alert()",2000);
clearInterval(obj)
setTimeout();
clearTimeout(obj)
示例:
跑马灯
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎南非波波同志成为本届董事长</title>
</head>
<body>
<input type="button" onclick="DropInterval();" value="停止滚动" />
<script>
obj1 = setInterval("GunDong()",1000);
console.log(obj1);
function DropInterval(){
clearInterval(obj1);
}
function GunDong(){
var text = document.title;
var firstWord = text.charAt(0);
var subWord = text.substring(1,text.length);
var newWord = subWord + firstWord;
document.title = newWord;
}
</script>
</body>
</html>
搜索框
<input type="text" placeholder="请输入关键字" id="search" onfocus="Focus();" onblur="Blur();"/>
<script type="text/javascript">
function Focus(){
var nid = document.getElementById("search");
var value = nid.placeholder;
if (value == "请输入关键字"){
nid.placeholder = "";
}
}
function Blur(){
var nid = document.getElementById("search");
var value = nid.placeholder;
if (!value.trim()){
nid.placeholder = "请输入关键字";
}
}
</script>
#id
element
.class
*
selector1,selector2,selectorN
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<div id="n1">11</div>
<div class="c1">22</div>
<div class="c1">33</div>
<a></a>
<span id="n2"></span>
<div id="n3">
<div>
<div class="c3">
<span>
<a class="c4">asdf</a>
</span>
</div>
</div>
<span>asdf</span>
</div>
</div>
<script src="jquery-2.2.3.js"></script>
<script>
/*
选择器基础使用
*/
$("#n1").text("中国好声音");
$(".c1").text("欢迎三江同学");
$(".c4").text("一不小心挂掉了");
$("#n3 span").text("游泳冠军");
</script>
</body>
</html>
作业
Author:@南非波波
课程大纲:
day09
http://www.cnblogs.com/alex3714/articles/5248247.html
day10
http://www.cnblogs.com/alex3714/articles/5286889.html
1. 队列的作业就是实现多个线程之间数据安全的交互
2. 队列类型:先进先出、后进先出、优先级
3. queue的数据必须按照顺序进行取出-->处理-->放回。主要作用就是不同进程之间数据的交换,manager可以进行多个进程之间的数据的共享,而且是数据安全的。
4. 生产者-消费者模型:实现程序的松耦合
1. gevent里面的socket本身可以实现IO阻塞变成非阻塞
2. monkey.path_all()可以帮助我们实现阻塞变成非阻塞
1. 实现单个线程里面的并发
2. 无需线程上下文切换的开销,无需原子操作锁定及同步的开销,方便切换控制流,高并发+高扩展性+低成本
3. 无法利用多核资源,但是可以实现单个进程下面起一个线程,然后一个线程下面实现多个协程并发
1. select 与poll的区别
select有一个最大文件数的限制1024,文件扫描一个列表是非常低效的;poll没有这个限制
内核态到用户态的数据copy;Epoll直接调用C语言进行内核态的数据nat到用户态
2. select代码注释
__auther__ = 'Victor'
import select
import socket
import sys
import queue
# 创建一个TCP/IP socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)
# 绑定socket到指定端口
server_address = ('localhost', 10000)
print(sys.stderr, 'starting up on %s port %s' % server_address)
server.bind(server_address)
# 监听连接的地址
server.listen(5)
inputs = [server]
# Socket的读操作
outputs = []
# socket的写操作
message_queues = {}
while inputs:
# Wait for at least one of the sockets to be ready for processing
print( '\nwaiting for the next event')
readable, writable, exceptional = select.select(inputs, outputs, inputs)
# 监听句柄序列,如果某个发生变化,select的第一个rLest会拿到数据,output只要有数据wLest就能获取到,select的第三个参数inputs用来监测异常,并赋值给exceptional。
# 监听inputs,outputs,inputs 如果他们的值有变化,就将分别赋值给readable,writable,exceptional。
for s in readable:
# 遍历readable的值。
if s is server:
connection, client_address = s.accept()
# 如果s 是server,那么server socket将接收连接。
print('new connection from', client_address)
# 打印出连接客户端的地址。
connection.setblocking(False)
# 设置socket 为非阻塞模式。
inputs.append(connection)
# 因为有读操作发生,所以将此连接加入inputs
message_queues[connection] = queue.Queue()
# 为每个连接创建一个queue队列。使得每个连接接收到正确的数据。
else:
data = s.recv(1024)
# 如果s不是server,说明客户端连接来了,那么就接受客户端的数据。
if data:
# 如果接收到客户端的数据
print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()) )
message_queues[s].put(data)
# 将收到的数据放入队列中
if s not in outputs:
outputs.append(s)
# 将socket客户端的连接加入select的output中,并且用来返回给客户端数据。
else:
print('closing', client_address, 'after reading no data')
# 如果没有收到客户端发来的空消息,则说明客户端已经断开连接。
if s in outputs:
outputs.remove(s)
# 既然客户端都断开了,我就不用再给它返回数据了,所以这时候如果这个客户端的连接对象还在outputs列表中,就把它删掉
inputs.remove(s)
# inputs中也删除掉
s.close()
# 把这个连接关闭掉
del message_queues[s]
# 删除此客户端的消息队列
for s in writable:
# 遍历output的数据
try:
next_msg = message_queues[s].get_nowait()
except queue.Empty:
# 获取对应客户端消息队列中的数据,如果队列中的数据为空,从消息队列中移除此客户端连接。
print('output queue for', s.getpeername(), 'is empty')
outputs.remove(s)
else:
print( 'sending "%s" to %s' % (next_msg, s.getpeername()))
s.send(next_msg)
# 如果消息队列有数据,则发送给客户端。
for s in exceptional:
# 处理 "exceptional conditions"
print('handling exceptional condition for', s.getpeername() )
inputs.remove(s)
# 取消对出现异常的客户端的监听
if s in outputs:
outputs.remove(s)
# 移除客户端的连接对象。
s.close()
# 关闭此socket连接
del message_queues[s]
# 删除此消息队列。
'''
在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,
轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般只能处理几千的并发连接。
epoll的设计和实现与select完全不同。epoll通过在Linux内核中申请一个简易的文件系统(文件系统一般用什么数据结构实现?B+树)。把原先的select/poll调用分成了3个部分:
1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3)调用epoll_wait收集发生的事件的连接
'''
3. epoll代码注释
__auther__ = 'Victor'
#--------------这是一个epoll的例子--------------
import socket, select
# 'windows'下不支持'epoll'
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!'
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('0.0.0.0', 8080))
serversocket.listen(1)
# 建立socket连接。
serversocket.setblocking(0)
# 因为socket本身是阻塞的,setblocking(0)使得socket不阻塞
epoll = select.epoll()
# 创建一个eopll对象
epoll.register(serversocket.fileno(), select.EPOLLIN)
# 在服务器端socket上面注册对读event的关注,一个读event随时会触发服务器端socket去接收一个socket连接。
try:
connections = {}; requests = {}; responses = {}
# 生成3个字典,connection字典是存储文件描述符映射到他们相应的网络连接对象
while True:
events = epoll.poll(1)
# 查询epoll对象,看是否有任何关注的event被触发,参数‘1’表示,会等待一秒来看是否有event发生,如果有任何感兴趣的event发生在这次查询之前,这个查询就会带着这些event的列表立即返回
for fileno, event in events:
# event作为一个序列(fileno,event code)的元组返回,fileno是文件描述符的代名词,始终是一个整数。
if fileno == serversocket.fileno():
# 如果一个读event在服务器端socket发生,就会有一个新的socket连接可能被创建。
connection, address = serversocket.accept()
# 服务器端开始接收连接和客户端地址
connection.setblocking(0)
# 设置新的socket为非阻塞模式
epoll.register(connection.fileno(), select.EPOLLIN)
# 为新的socket注册对读(EPOLLIN)event的关注
connections[connection.fileno()] = connection
requests[connection.fileno()] = b''
responses[connection.fileno()] = response
elif event & select.EPOLLIN:
requests[fileno] += connections[fileno].recv(1024)
# 如果发生一个读event,就读取从客户端发过来的数据。
if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
epoll.modify(fileno, select.EPOLLOUT)
# 一旦完成请求已经收到,就注销对读event的关注,注册对写(EPOLLOUT)event的关注,写event发生的时候,会回复数据给客户端。
print('-'*40 + '\n' + requests[fileno].decode()[:-2])
# 打印完整的请求,证明虽然与客户端的通信是交错进行的,但是数据可以作为一个整体来组装和处理。
elif event & select.EPOLLOUT:
# 如果一个写event在一个客户端socket上面发生,他会接受新的数据以便发送到客户端。
byteswritten = connections[fileno].send(responses[fileno])
responses[fileno] = responses[fileno][byteswritten:]
if len(responses[fileno]) == 0:
# 每次发送一部分响应数据,直到完整的响应数据都已经发送给操作系统等待传输给客户端。
epoll.modify(fileno, 0)
# 一旦完整的响应数据发送完成,就不再关注读或者写event。
connections[fileno].shutdown(socket.SHUT_RDWR)
# 如果一个连接显式关闭,那么socket shutdown是可选的,在这里这样使用,是为了让客户端首先关闭。
# shutdown调用会通知客户端socket没有更多的数据应该被发送或者接收,并会让功能正常的客户端关闭自己的socket连接。
elif event & select.EPOLLHUP:
# HUP挂起event表明客户端socket已经断开(即关闭),所以服务器端也需要关闭,没有必要注册对HUP event的关注,在socket上面,他们总是会被epoll对象注册。
epoll.unregister(fileno)
# 注销对此socket连接的关注。
connections[fileno].close()
# 关闭socket连接。
del connections[fileno]
finally:
epoll.unregister(serversocket.fileno())
# 去掉已经注册的文件句柄
epoll.close()
# 关闭epoll对象
serversocket.close()
# 关闭服务器连接
# 打开的socket连接不需要关闭,因为Python会在程序结束时关闭, 这里的显示关闭是个好的习惯。
'''
首先我们来定义流的概念,一个流可以是文件,socket,pipe等等可以进行I/O操作的内核对象。
不管是文件,还是套接字,还是管道,我们都可以把他们看作流。
之后我们来讨论I/O的操作,通过read,我们可以从流中读入数据;通过write,我们可以往流写入数据。现在假定一个情形,
我们需要从流中读数据,但是流中还没有数据,(典型的例子为,客户端要从socket读如数据,但是服务器还没有把数据传回来),
这时候该怎么办?
阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做);
那么你可以去睡觉了,因为你知道快递把货送来时一定会给你打个电话(假定一定能叫醒你)。
非阻塞忙轮询:接着上面等快递的例子,如果用忙轮询的方法,那么你需要知道快递员的手机号,然后每分钟给他挂个电话:“你到了没?”
很明显一般人不会用第二种做法,不仅显很无脑,浪费话费不说,还占用了快递员大量的时间。
大部分程序也不会用第二种做法,因为第一种方法经济而简单,经济是指消耗很少的CPU时间,如果线程睡眠了,就掉出了系统的调度队列,暂时不会去瓜分CPU宝贵的时间片了。
为了了解阻塞是如何进行的,我们来讨论缓冲区,以及内核缓冲区,最终把I/O事件解释清楚。缓冲区的引入是为了减少频繁I/O操作而引起频繁的系统调用(你知道它很慢的),
当你操作一个流时,更多的是以缓冲区为单位进行操作,这是相对于用户空间而言。对于内核来说,也需要缓冲区。
假设有一个管道,进程A为管道的写入方,B为管道的读出方。
假设一开始内核缓冲区是空的,B作为读出方,被阻塞着。然后首先A往管道写入,这时候内核缓冲区由空的状态变到非空状态,内核就会产生一个事件告诉B该醒来了,
这个事件姑且称之为“缓冲区非空”。
但是“缓冲区非空”事件通知B后,B却还没有读出数据;且内核许诺了不能把写入管道中的数据丢掉这个时候,A写入的数据会滞留在内核缓冲区中,如果内核也缓冲区满了,
B仍未开始读数据,最终内核缓冲区会被填满,这个时候会产生一个I/O事件,告诉进程A,你该等等(阻塞)了,我们把这个事件定义为“缓冲区满”。
假设后来B终于开始读数据了,于是内核的缓冲区空了出来,这时候内核会告诉A,内核缓冲区有空位了,你可以从长眠中醒来了,继续写数据了,我们把这个事件叫做“缓冲区非满”
也许事件Y1已经通知了A,但是A也没有数据写入了,而B继续读出数据,知道内核缓冲区空了。这个时候内核就告诉B,你需要阻塞了!,我们把这个时间定为“缓冲区空”。
这四个情形涵盖了四个I/O事件,缓冲区满,缓冲区空,缓冲区非空,缓冲区非满(注都是说的内核缓冲区,且这四个术语都是我生造的,仅为解释其原理而造)。
这四个I/O事件是进行阻塞同步的根本。(如果不能理解“同步”是什么概念,请学习操作系统的锁,信号量,条件变量等任务同步方面的相关知识)。
然后我们来说说阻塞I/O的缺点。但是阻塞I/O模式下,一个线程只能处理一个流的I/O事件。如果想要同时处理多个流,要么多进程(fork),要么多线程(pthread_create),
很不幸这两种方法效率都不高。
于是再来考虑非阻塞忙轮询的I/O方式,我们发现我们可以同时处理多个流了(把一个流从阻塞模式切换到非阻塞模式再此不予讨论):
while true {
for i in stream[]; {
if i has data
read until unavailable
}
}
我们只要不停的把所有流从头到尾问一遍,又从头开始。这样就可以处理多个流了,但这样的做法显然不好,因为如果所有的流都没有数据,那么只会白白浪费CPU。
这里要补充一点,阻塞模式下,内核对于I/O事件的处理是阻塞或者唤醒,而非阻塞模式下则把I/O事件交给其他对象(后文介绍的select以及epoll)处理甚至直接忽略。
为了避免CPU空转,可以引进了一个代理(一开始有一位叫做select的代理,后来又有一位叫做poll的代理,不过两者的本质是一样的)。这个代理比较厉害,
可以同时观察许多流的I/O事件,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中醒来,于是我们的程序就会轮询一遍所有的流
(于是我们可以把“忙”字去掉了)。代码长这样:
while true {
select(streams[])
for i in streams[] {
if i has data
read until unavailable
}
}
于是,如果没有I/O事件产生,我们的程序就会阻塞在select处。但是依然有个问题,我们从select那里仅仅知道了,有I/O事件发生了,但却并不知道是那几个流
(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。
但是使用select,我们有O(n)的无差别轮询复杂度,同时处理的流越多,没一次无差别轮询时间就越长。再次
说了这么多,终于能好好解释epoll了
epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll之会把哪个流发生了怎样的I/O事件通知我们。此时我们对这些流的操作都是有意义的。
(复杂度降低到了O(1))
在讨论epoll的实现细节之前,先把epoll的相关操作列出:
epoll_create 创建一个epoll对象,一般epollfd = epoll_create()
epoll_ctl (epoll_add/epoll_del的合体),往epoll对象中增加/删除某一个流的某一个事件
比如
epoll_ctl(epollfd, EPOLL_CTL_ADD, socket, EPOLLIN);//注册缓冲区非空事件,即有数据流入
epoll_ctl(epollfd, EPOLL_CTL_DEL, socket, EPOLLOUT);//注册缓冲区非满事件,即流可以被写入
epoll_wait(epollfd,...)等待直到注册的事件发生
(注:当对一个非阻塞流的读写发生缓冲区满或缓冲区空,write/read会返回-1,并设置errno=EAGAIN。而epoll只关心缓冲区非满和缓冲区非空事件)。
一个epoll模式的代码大概的样子是:
while true {
active_stream[] = epoll_wait(epollfd)
for i in active_stream[] {
read or write till
}
}
'''
将自定义的类和函数注册到事件列表中,事件驱动框架就会自行去列表中获取事件并执行。
第一,注册事件;第二,触发事件
![](http://i.imgur.com/6brDszu.png)
示例代码:
#event_drive.py
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
'''
模拟twsited异步网络框架的流程
'''
#创建一个事件列表
event_list = []
#创建一个事件驱动动作
def run():
for event in event_list:
obj = event()
obj.execute()
#创建事件定义规则,用户将自定义事件注册到事件列表中需要继承此类
class BaseHandler(object):
"""
用户必须继承该类,从而规范所有类的方法(类似于接口的功能)
"""
def execute(self):
raise Exception('you must overwrite execute')
#event_run.py
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import event_drive
#自定义事件,继承事件驱动自定义类
class MyHandler(event_drive.BaseHandler):
#重写执行函数
def execute(self):
print('event-drive execute MyHandler')
class YourHandler(event_drive.BaseHandler):
def execute(self):
print('event-drive ezecute YourHandler')
event_drive.event_list.append(MyHandler) #将事件注册到事件列表中
event_drive.event_list.append(YourHandler)
event_drive.run()
Echo_server
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
from twisted.internet import protocol
from twisted.internet import reactor
class Echo(protocol.Protocol):
'''
定义一个类,处理客户端传递的数据
'''
def dataReceived(self, data):
'''
一旦接收到客户端传递的数据就要调用该方法
:param data: 客户端传递过来的数据,python3版本传递的数据需要转换成bytes
:return: 返回的数据是将客户端传递过来的数据返回给客户端
'''
print("Client said:",data)
self.transport.write(data)
def main():
'''
主函数,程序执行时直接从该函数调用事件类
:return:
'''
factory = protocol.ServerFactory() #定义基础工厂类
factory.protocol = Echo #相当于socketserver中的Handler方法,工厂协议直接引用自定义的Echo类
reactor.listenTCP(5000,factory) #reactor自动重复去做一件事情。使用listenTCP监听端口
reactor.run() #运行
if __name__ == '__main__':
main()
Echo_client:
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
from twisted.internet import reactor, protocol
# a client protocol
class EchoClient(protocol.Protocol):
'''
客户端Echo事件
'''
def connectionMade(self):
'''
连接建立执行该方法,客户端发送数据
:return:
'''
self.transport.write(b"hello alex!")
def dataReceived(self, data):
'''
客户端接收服务端的数据
:param data:
:return:
'''
print("Server said:", data)
self.transport.loseConnection()
def connectionLost(self, reason):
'''
客户端接收完数据断开连接,主动执行该方法断开连接
:param reason:
:return:
'''
print("connection lost")
class EchoFactory(protocol.ClientFactory):
'''
自定义工厂类,继承prorocol.ClientFactory类
'''
protocol = EchoClient #hanld。自己重写了protocol类
def clientConnectionFailed(self, connector, reason):
print("Connection failed - goodbye!")
reactor.stop()
def clientConnectionLost(self, connector, reason):
print("Connection lost - goodbye!")
reactor.stop()
# this connects the protocol to a server running on port 8000
def main():
f = EchoFactory()
reactor.connectTCP("localhost", 5000, f)
reactor.run()
# this only runs if the module was *not* imported
if __name__ == '__main__':
main()
http://blog.csdn.net/hanhuili/article/details/9389433
http://krondo.com/an-introduction-to-asynchronous-programming-and-twisted/
参考:http://www.cnblogs.com/wupeiqi/articles/5132791.html
数据(键值对)存储在内存中,一个独立的内存管理器,可以使多个程序共享数据
默认是非持久化的,但是可以在配置文件中进行设置
1. redis基础使用
cli>keys * #查看所有的键
cli>set name swht ex 5 #设置一个键值对,其有效时间为5秒
cli>get name #获取键值
2. redis连接
import redis
redis_cli = redis.Redis("localhost")
print(redis_cli.get('name')) #b'swht' get方法只能获取字符
3. redis连接池
import redis
pool = redis.ConnectionPool(host = 'localhost',port = 6379)
redis_cli = redis.Redis(connection_pool=pool)
redis_cli.set('age',56)
print(redis_cli.get('age')) #b'56'
4. 操作
set(name, value, ex=None, px=None, nx=False, xx=False)
在Redis中设置值,默认,不存在则创建,存在则修改
参数:
ex,过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行
xx,如果设置为True,则只有name存在时,岗前set操作才执行
setnx(name, value)
设置值,只有name不存在时,执行设置操作(添加)
setex(name, value, time)
# 设置值
# 参数:
# time,过期时间(数字秒 或 timedelta对象)
psetex(name, time_ms, value)
# 设置值
# 参数:
# time_ms,过期时间(数字毫秒 或 timedelta对象)
mset(*args, **kwargs)
批量设置值
如:
mset(k1='v1', k2='v2')
或
mget({'k1': 'v1', 'k2': 'v2'})
get(name)
获取值
mget(keys, *args)
批量获取
如:
mget('ylr', 'wupeiqi')
或
r.mget(['ylr', 'wupeiqi'])
getset(name, value)
设置新值并获取原来的值
getrange(key, start, end)
# 获取子序列(根据字节获取,非字符)
# 参数:
# name,Redis 的 name
# start,起始位置(字节)
# end,结束位置(字节)
# 如: "武沛齐" ,0-3表示 "武"
setrange(name, offset, value)
# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
# 参数:
# offset,字符串的索引,字节(一个汉字三个字节)
# value,要设置的值
setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作
# 参数:
# name,redis的name
# offset,位的索引(将值变换成二进制后再进行索引)
# value,值只能是 1 或 0
# 注:如果在Redis中有一个对应: n1 = "foo",
那么字符串foo的二进制表示为:01100110 01101111 01101111
所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
# 扩展,转换二进制表示:
# source = "武沛齐"
source = "foo"
for i in source:
num = ord(i)
print bin(num).replace('b','')
特别的,如果source是汉字 "武沛齐"怎么办?
答:对于utf-8,每一个汉字占 3 个字节,那么 "武沛齐" 则有 9个字节
对于汉字,for循环时候会按照 字节 迭代,那么在迭代时,将每一个字节转换 十进制数,然后再将十进制数转换成二进制
假定统计UV,使用setbit可以进行相应UV数统计。
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import redis
pool = redis.ConnectionPool(host = 'localhost',port = 6379)
redis_cli = redis.Redis(connection_pool=pool)
redis_cli.setbit('ip',5,1)
redis_cli.setbit('ip',45,1)
redis_cli.setbit('ip',15,1)
redis_cli.setbit('ip',45,1)
print("uv_count:",redis_cli.bitcount('ip'))
getbit(name, offset)
# 获取name对应的值的二进制表示中的某位的值 (0或1)
bitcount(key, start=None, end=None)
# 获取name对应的值的二进制表示中 1 的个数
# 参数:
# key,Redis的name
# start,位起始位置
# end,位结束位置
bitop(operation, dest, *keys)
# 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
# 参数:
# operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
# dest, 新的Redis的name
# *keys,要查找的Redis的name
# 如:
bitop("AND", 'new_name', 'n1', 'n2', 'n3')
# 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中
strlen(name)
# 返回name对应值的字节长度(一个汉字3个字节)
incr(self, name, amount=1)
做pv统计比较有用
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
# name,Redis的name
# amount,自增数(必须是整数)
# 注:同incrby
incrbyfloat(self, name, amount=1.0)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
# name,Redis的name
# amount,自增数(浮点型)
decr(self, name, amount=1)
# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
# 参数:
# name,Redis的name
# amount,自减数(整数)
append(key, value)
# 在redis name对应的值后面追加内容
# 参数:
key, redis的name
value, 要追加的字符串
5. Hash操作
hset(name, key, value)
# name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
# 参数:
# name,redis的name
# key,name对应的hash中的key
# value,name对应的hash中的value
# 注:
# hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
hmset(name, mapping)
# 在name对应的hash中批量设置键值对
# 参数:
# name,redis的name
# mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
# 如:
# r.hmset('xx', {'k1':'v1', 'k2': 'v2'})
hget(name,key)
# 在name对应的hash中获取根据key获取value
hmget(name, keys, *args)
# 在name对应的hash中获取多个key的值
# 参数:
# name,reids对应的name
# keys,要获取key集合,如:['k1', 'k2', 'k3']
# *args,要获取的key,如:k1,k2,k3
# 如:
# r.mget('xx', ['k1', 'k2'])
# 或
# print r.hmget('xx', 'k1', 'k2')
hgetall(name)
获取name对应hash的所有键值
hlen(name)
# 获取name对应的hash中键值对的个数
hkeys(name)
# 获取name对应的hash中所有的key的值
hvals(name)
# 获取name对应的hash中所有的value的值
hexists(name, key)
# 检查name对应的hash是否存在当前传入的key
hdel(name,*keys)
# 将name对应的hash中指定key的键值对删除
hincrby(name, key, amount=1)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
# 参数:
# name,redis中的name
# key, hash对应的key
# amount,自增数(整数)
hincrbyfloat(name, key, amount=1.0)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
# 参数:
# name,redis中的name
# key, hash对应的key
# amount,自增数(浮点数)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
hscan(name, cursor=0, match=None, count=None)
# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
# 参数:
# name,redis的name
# cursor,游标(基于游标分批取获取数据)
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
# ...
# 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
hscan_iter(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
# 参数:
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# for item in r.hscan_iter('xx'):
# print item
6. List操作
lpush(name,values)
# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
# 如:
# r.lpush('oo', 11,22,33)
# 保存顺序为: 33,22,11
# 扩展:
# rpush(name, values) 表示从右向左操作
lpushx(name,value)
# 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
# 更多:
# rpushx(name, value) 表示从右向左操作
llen(name)
# name对应的list元素的个数
linsert(name, where, refvalue, value))
# 在name对应的列表的某一个值前或后插入一个新值
# 参数:
# name,redis的name
# where,BEFORE或AFTER
# refvalue,标杆值,即:在它前后插入数据
# value,要插入的数据
r.lset(name, index, value)
# 对name对应的list中的某一个索引位置重新赋值
# 参数:
# name,redis的name
# index,list的索引位置
# value,要设置的值
r.lrem(name, value, num)
# 在name对应的list中删除指定的值
# 参数:
# name,redis的name
# value,要删除的值
# num, num=0,删除列表中所有的指定值;
# num=2,从前到后,删除2个;
# num=-2,从后向前,删除2个
lpop(name)
# 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
# 更多:
# rpop(name) 表示从右向左操作
lindex(name, index)
在name对应的列表中根据索引获取列表元素
lrange(name, start, end)
# 在name对应的列表分片获取数据
# 参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置
ltrim(name, start, end)
# 在name对应的列表中移除没有在start-end索引之间的值
# 参数:
# name,redis的name
# start,索引的起始位置
# end,索引结束位置
rpoplpush(src, dst)
# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
# 参数:
# src,要取数据的列表的name
# dst,要添加数据的列表的name
blpop(keys, timeout)
# 将多个列表排列,按照从左到右去pop对应列表的元素
# 参数:
# keys,redis的name的集合
# timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞
# 更多:
# r.brpop(keys, timeout),从右向左获取数据
brpoplpush(src, dst, timeout=0)
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
# 参数:
# src,取出并要移除元素的列表对应的name
# dst,要插入元素的列表对应的name
# timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
自定义增量迭代
# 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:
# 1、获取name对应的所有列表
# 2、循环列表
# 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:
def list_iter(name):
"""
自定义redis列表增量迭代
:param name: redis中的name,即:迭代name对应的列表
:return: yield 返回 列表元素
"""
list_count = r.llen(name)
for index in xrange(list_count):
yield r.lindex(name, index)
# 使用
for item in list_iter('pp'):
print item
7. Set操作,Set集合就是不允许重复的列表
sadd(name,values)
# name对应的集合中添加元素
scard(name)
获取name对应的集合中元素个数
sdiff(keys, *args)
在第一个name对应的集合中且不在其他name对应的集合的元素集合
sdiffstore(dest, keys, *args)
# 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中
sinter(keys, *args)
# 获取多一个name对应集合的并集
sinterstore(dest, keys, *args)
# 获取多一个name对应集合的并集,再讲其加入到dest对应的集合中
sismember(name, value)
# 检查value是否是name对应的集合的成员
smembers(name)
# 获取name对应的集合的所有成员
smove(src, dst, value)
# 将某个成员从一个集合中移动到另外一个集合
spop(name)
# 从集合的右侧(尾部)移除一个成员,并将其返回
srandmember(name, numbers)
# 从name对应的集合中随机获取 numbers 个元素
srem(name, values)
# 在name对应的集合中删除某些值
sunion(keys, *args)
# 获取多一个name对应的集合的并集
sunionstore(dest,keys, *args)
# 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中
sscan(name, cursor=0, match=None, count=None)
sscan_iter(name, match=None, count=None)
# 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
8. 有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
zadd(name, *args, **kwargs)
# 在name对应的有序集合中添加元素
# 如:
# zadd('zz', 'n1', 1, 'n2', 2)
# 或
# zadd('zz', n1=11, n2=22)
zcard(name)
# 获取name对应的有序集合元素的数量
zcount(name, min, max)
# 获取name对应的有序集合中分数 在 [min,max] 之间的个数
zincrby(name, value, amount)
# 自增name对应的有序集合的 name 对应的分数
r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
# 按照索引范围获取name对应的有序集合的元素
# 参数:
# name,redis的name
# start,有序集合索引起始位置(非分数)
# end,有序集合索引结束位置(非分数)
# desc,排序规则,默认按照分数从小到大排序
# withscores,是否获取元素的分数,默认只获取元素的值
# score_cast_func,对分数进行数据转换的函数
# 更多:
# 从大到小排序
# zrevrange(name, start, end, withscores=False, score_cast_func=float)
# 按照分数范围获取name对应的有序集合的元素
# zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
# 从大到小排序
# zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)
zrank(name, value)
# 获取某个值在 name对应的有序集合中的排行(从 0 开始)
# 更多:
# zrevrank(name, value),从大到小排序
zrangebylex(name, min, max, start=None, num=None)
# 当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的 值 (lexicographical ordering)来进行排序,而这个命令则可以返回给定的有序集合键 key 中, 元素的值介于 min 和 max 之间的成员
# 对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 返回排序后的集合成员。 如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大
# 参数:
# name,redis的name
# min,左区间(值)。 + 表示正无限; - 表示负无限; ( 表示开区间; [ 则表示闭区间
# min,右区间(值)
# start,对结果进行分片处理,索引位置
# num,对结果进行分片处理,索引后面的num个元素
# 如:
# ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga
# r.zrangebylex('myzset', "-", "[ca") 结果为:['aa', 'ba', 'ca']
# 更多:
# 从大到小排序
# zrevrangebylex(name, max, min, start=None, num=None)
zrem(name, values)
# 删除name对应的有序集合中值是values的成员
# 如:zrem('zz', ['s1', 's2'])
zremrangebyrank(name, min, max)
# 根据排行范围删除
zremrangebyscore(name, min, max)
# 根据分数范围删除
zremrangebylex(name, min, max)
# 根据值返回删除
zscore(name, value)
# 获取name对应有序集合中 value 对应的分数
zinterstore(dest, keys, aggregate=None)
# 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作
# aggregate的值为: SUM MIN MAX
zunionstore(dest, keys, aggregate=None)
# 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作
# aggregate的值为: SUM MIN MAX
zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)
# 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作
9. redis的发布与订阅
基础类:
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import redis
class RedisHelper:
def __init__(self):
self.__conn = redis.Redis(host='localhost',port=6379)
self.chan_sub = 'fm104.5'
self.chan_pub = 'fm104.5'
def public(self, msg):
self.__conn.publish(self.chan_pub, msg)
return True
def subscribe(self):
pub = self.__conn.pubsub()
pub.subscribe(self.chan_sub)
pub.parse_response()
return pub
redis_sub.py
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
from RedisHelper import RedisHelper
obj = RedisHelper()
redis_sub = obj.subscribe()
while True:
msg= redis_sub.parse_response()
print(msg)
redis_pub.py
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
from RedisHelper import RedisHelper
obj = RedisHelper()
obj.public('hello')
非持久化轻量级缓存,使用第三方工具可以实现数据的持久化存储
天生的数据持久化,默认将数据持久化存储在本地磁盘。
通信模式:
rabbit_send
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import pika
#与消息队列建立一个连接
connection = pika.BlockingConnection(pika.ConnectionParameters(
'localhost'))
#创建一个管道
channel = connection.channel()
#在管道中声明一个名称为'name'的队列
channel.queue_declare(queue='name')
#一个消息不能直接发送给消息队列,需要通过一个路由器进行转发,这个路由器就是由exchange进行设置
channel.basic_publish(exchange='', #路由器
routing_key='name', #队列名称
body='swht') #消息
print(" [swht] Sent a message")
connection.close()
rabbit_recive
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import pika
#与消息队列服务器建立连接
connection = pika.BlockingConnection(pika.ConnectionParameters(
'localhost'))
#创建一个管道
channel = connection.channel()
#消费者声明一个队列,为了防止生产者还没有启动没有完成创建队列时代码出错的问题。如果队列已存在,则忽略该操作,否则则创建队列
channel.queue_declare(queue='name')
def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
channel.basic_consume(callback,
queue='name',
no_ack=True) #接收消息不进行确认
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
channel.queue_declare(queue='name',durable=True)
已经存在的队列是不能再进行持久化设置的,所以在只有创建队列的时候设置持久化选项
basc_ack = (delivery_tag= method.delivry_tag)
查看当前所有的queue XX
只在消费者添加
channel.basic_qos(prefetch_count=1)
示例代码:
rabbit_slb_send.py
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
'192.168.137.6'))
channel = connection.channel()
#声明queue
channel.queue_declare(queue='task_queue')
#n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
import sys
message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='',
routing_key='task_queue',
body=message,
properties=pika.BasicProperties(
delivery_mode = 2, # make message persistent
))
print(" [x] Sent %r" % message)
connection.close()
代码:
publisher.py
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs',
type='fanout')
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
channel.basic_publish(exchange='logs',
routing_key='',
body=message)
print(" [x] Sent %r" % message)
connection.close()
subscriber.py
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs',
type='fanout')
result = channel.queue_declare(exclusive=True) #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = result.method.queue
channel.queue_bind(exchange='logs',
queue=queue_name)
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(" [x] %r" % body)
channel.basic_consume(callback,
queue=queue_name,
no_ack=True)
channel.start_consuming()
Author:@南非波波
课程大纲:
day08
http://www.cnblogs.com/alex3714/articles/5227251.html
day09
http://www.cnblogs.com/alex3714/articles/5248247.html
###推荐电影###
<权利的游戏:冰与火之歌> <纸牌屋> <绝命毒师>
《林大看美国》
python调用C的原生线程
GIL(全局解释器)防止数据被修改异常。使用线程锁控制同时仅有一个线程对数据有操作权限
全局解释器限制的是原生C线程,
python同一时刻只有一个线程
python会处理不同cpu核之间的进程切换
父进程与子进程之间默认不能共享数据
获取线程的数据
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import time,threading
data = [] #列表是一个安全的内存空间,可以不用加线程锁
def run(n):
'''
定义一个run方法,返回的n的n次方
:param n:
:return:
'''
data.append(n**n)
return n**n
#创建一个多线程
t = threading.Thread(target=run,args=[8,])
t.start()
t.join() #起阻塞作用,让主进程等待线程执行完再执行主进程 默认无限期的等待,添加一个超时时间timeout=3.在守护线程下面是不好使的
print(data)
python队列queue默认的是先进先出。
import queue
Queue = queue.Queue(maxsize=5)
Queue.put((2,["swht","shen"]))
Queue.put((5,{"name":"swht"}))
Queue.put((1,"sdsd"),timeout=2)
for i in range(Queue._qsize()):
print("data%s:%s" % (i,Queue.get()))
返回结果:
data0:(2, ['swht', 'shen'])
data1:(5, {'name': 'swht'})
data2:(1, 'sdsd')
python队列支持后面进去的消息最先被取出。
python队列queue默认的是先进先出。
import queue
Queue = queue.LifoQueue(maxsize=5)
Queue.put((2,["swht","shen"]))
Queue.put((5,{"name":"swht"}))
Queue.put((1,"sdsd"),timeout=2)
for i in range(Queue._qsize()):
print("data%s:%s" % (i,Queue.get()))
返回结果:
data0:(1, 'sdsd')
data1:(5, {'name': 'swht'})
data2:(2, ['swht', 'shen'])
python queue支持设置优先级,取消息可以安装优先级顺序取出消息
import queue
Queue = queue.PriorityQueue(maxsize=5)
Queue.put((2,["swht","shen"]))
Queue.put((5,{"name":"swht"}))
Queue.put((1,"sdsd"),timeout=2)
for i in range(Queue._qsize()):
print("data%s:%s" % (i,Queue.get()))
返回结果:
data0:(1, 'sdsd')
data1:(2, ['swht', 'shen'])
data2:(5, {'name': 'swht'})
import threading,queue,time
def consumer(n):
'''
消费者,消费生产者产生的消息、数据
:param n: 消费者标志
:return:
'''
while True:
print("\033[32;1m消费者[%s]\033[0m 获取消息 %s" % (n,q.get()))
time.sleep(1)
q.task_done() #消费者等待生产者生产消息
def prodeucer(n):
'''
生产者,生产消息、数据
:param n: 生产者标志
:return:
'''
count = 1
while True:
print("\033[31;1m生产者[%s]\033[0m 生产消息 %s" % (n,count))
q.put(count)
count += 1
q.join() #生产者阻塞
print("==================") #发出信号:消息已经生产
q = queue.Queue()
c1 = threading.Thread(target=consumer,args=[1,])
p1 = threading.Thread(target=prodeucer,args=["swht",])
c1.start()
p1.start()
结果:
生产者[swht] 生产消息 1
消费者[1] 获取消息 1
==================
生产者[swht] 生产消息 2
消费者[1] 获取消息 2
==================
生产者[swht] 生产消息 3
消费者[1] 获取消息 3
==================
生产者[swht] 生产消息 4
消费者[1] 获取消息 4
==================
生产者[swht] 生产消息 5
消费者[1] 获取消息 5
==================
生产者[swht] 生产消息 6
消费者[1] 获取消息 6
==================
生产者[swht] 生产消息 7
消费者[1] 获取消息 7
==================
import threading,queue,time
def consumer(n):
'''
消费者,消费生产者产生的消息、数据
:param n: 消费者标志
:return:
'''
while True:
print("\033[32;1m消费者[%s]\033[0m 获取消息 %s" % (n,q.get()))
time.sleep(1)
q.task_done() #消费者等待生产者生产消息
def prodeucer(n):
'''
生产者,生产消息、数据
:param n: 生产者标志
:return:
'''
count = 1
while True:
print("\033[31;1m生产者[%s]\033[0m 生产消息 %s" % (n,count))
q.put(count)
count += 1
q.join() #生产者阻塞
print("==================") #发出信号:消息已经生产
q = queue.Queue()
c1 = threading.Thread(target=consumer,args=[1,])
c2 = threading.Thread(target=consumer,args=[2,])
c3 = threading.Thread(target=consumer,args=[3,])
p1 = threading.Thread(target=prodeucer,args=["swht",])
c1.start()
c2.start()
c3.start()
p1.start()
结果:
生产者[swht] 生产消息 1
消费者[1] 获取消息 1
==================
生产者[swht] 生产消息 2
消费者[2] 获取消息 2
==================
生产者[swht] 生产消息 3
消费者[3] 获取消息 3
==================
生产者[swht] 生产消息 4
消费者[1] 获取消息 4
==================
生产者[swht] 生产消息 5
消费者[2] 获取消息 5
==================
生产者[swht] 生产消息 6
消费者[3] 获取消息 6
==================
生产者[swht] 生产消息 7
消费者[1] 获取消息 7
==================
import threading,queue,time
def consumer(n):
'''
消费者,消费生产者产生的消息、数据
:param n: 消费者标志
:return:
'''
while True:
print("\033[32;1m消费者[%s]\033[0m 获取消息 %s" % (n,q.get()))
time.sleep(1)
q.task_done() #消费者等待生产者生产消息
def prodeucer(n):
'''
生产者,生产消息、数据
:param n: 生产者标志
:return:
'''
count = 1
while True:
print("\033[31;1m生产者[%s]\033[0m 生产消息 %s" % (n,count))
q.put(count)
count += 1
q.join() #生产者阻塞
print("==================") #发出信号:消息已经生产
q = queue.Queue()
c1 = threading.Thread(target=consumer,args=[1,])
c2 = threading.Thread(target=consumer,args=[2,])
c3 = threading.Thread(target=consumer,args=[3,])
p1 = threading.Thread(target=prodeucer,args=["swht",])
p2 = threading.Thread(target=prodeucer,args=["shen",])
p3 = threading.Thread(target=prodeucer,args=["alex",])
c1.start()
c2.start()
c3.start()
p1.start()
p2.start()
p3.start()
结果:
生产者[swht] 生产消息 1
生产者[shen] 生产消息 1
消费者[1] 获取消息 1
生产者[alex] 生产消息 1
消费者[2] 获取消息 1
消费者[3] 获取消息 1
==================
生产者[swht] 生产消息 2
消费者[1] 获取消息 2
==================
生产者[swht] 生产消息 3
消费者[2] 获取消息 3
==================
生产者[swht] 生产消息 4
消费者[3] 获取消息 4
==================
生产者[alex] 生产消息 2
消费者[1] 获取消息 2
==================
生产者[swht] 生产消息 5
==================
生产者[shen] 生产消息 2
消费者[2] 获取消息 2
消费者[3] 获取消息 5
==================
生产者[swht] 生产消息 6
==================
生产者[shen] 生产消息 3
消费者[1] 获取消息 3
消费者[3] 获取消息 6
==================
生产者[shen] 生产消息 4
消费者[2] 获取消息 4
==================
生产者[shen] 生产消息 5
消费者[1] 获取消息 5
==================
生产者[shen] 生产消息 6
消费者[3] 获取消息 6
==================
生产者[shen] 生产消息 7
消费者[2] 获取消息 7
==================
协程是一种用户态的轻量级线程。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。
通过协程进行线程内切换,虽然还是串行的执行,但是因为切换速度快,可以达到一种并发的效果。用户自行控制
协程的好处:
无需线程上下文切换的开销
无需原子操作锁定及同步的开销
方便切换控制流,简化编程模型
高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序
通过yeild简单模拟协程
import time
import queue
def consumer(name):
print("--->starting eating baozi...")
while True:
new_baozi = yield
print("[%s] is eating baozi %s" % (name,new_baozi))
#time.sleep(1)
def producer():
r = con.__next__()
r = con2.__next__()
n = 0
while n < 5:
n +=1
con.send(n)
con2.send(n)
print("\033[32;1m[producer]\033[0m is making baozi %s" %n )
if __name__ == '__main__':
con = consumer("c1")
con2 = consumer("c2")
p = producer()
'''
结果:
--->starting eating baozi...
--->starting eating baozi...
[c1] is eating baozi 1
[c2] is eating baozi 1
[producer] is making baozi 1
[c1] is eating baozi 2
[c2] is eating baozi 2
[producer] is making baozi 2
[c1] is eating baozi 3
[c2] is eating baozi 3
[producer] is making baozi 3
[c1] is eating baozi 4
[c2] is eating baozi 4
[producer] is making baozi 4
[c1] is eating baozi 5
[c2] is eating baozi 5
[producer] is making baozi 5
'''
greenlet模块
greenlet模块可以简单的实现协程之间的切换
示例代码:
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(34)
gr2.switch()
def test2():
print(56)
gr1.switch()
print(78)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
gevent模块
Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。
gevent模块实现协程在遇到IO阻塞时进行切换的功能。避免了协程的致命缺点:一个协程阻塞,整个线程宕掉
示例代码:
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import gevent
def foo():
print('\033[32;1mRunning in foo\033[0m')
gevent.sleep(1)
print('\033[32;1mExplicit context switch to foo again\033[0m')
def bar():
print('Explicit context to bar')
gevent.sleep(1)
print('Implicit context switch back to bar')
def exe():
print('\033[31;1mExplicit context to bar\033[0m')
gevent.sleep(1)
print('\033[31;1mImplicit context switch back to bar\033[0m')
gevent.joinall([
gevent.spawn(foo),
gevent.spawn(bar),
gevent.spawn(exe),
])
附注:
gevent模块安装:
1. windows环境:
使用pip进行安装:pip2 install gevent
pip3 install gevent
2. Linux环境:
使用pip进行安装:pip2 install gevent
pip3 install gevent
使用源码包编译安装:
select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。
select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。
epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
server端
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
'''
通过echo server例子了解select 是如何通过单进程实现同时处理多个非阻塞的socket连接
'''
import select
import socket
import sys
import queue
# 创建一个TCP/IP socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False) #设置非阻塞 ==>server.setblocking(0)
# 绑定端口和ip地址
server_address = ('localhost', 10000)
print(sys.stderr, 'starting up on %s port %s' % server_address) #
# print >> sys.stderr,'starting up on %s port %s' % server_address
server.bind(server_address)
# 监听连接,允许同时有5个链接
server.listen(5)
#select()方法接收并监控3个通信列表
#1.所有的输入数据,即外部发过来的数据;
#2.监控和监听所要发出去的数据;
#3.监控错误信息
# 创建输入列表将输入的信息传递给select()
inputs = [ server ]
# 创建输出列表将输出的信息传递给select()
outputs = [ ]
#创建一个缓存队列,用来存储每个连接的输入或输出的数据,然后由select去出来再发出去。
message_queues = {}
#程序的主循环,当调用select()时会阻塞和等待直到新的连接和数据进来
while inputs:
# Wait for at least one of the sockets to be ready for processing
print( '\nwaiting for the next event')
#readable 代表socket连接有数据可以接收(resv)
#writable 代表socket连接有可以进行发送(send)的数据
#exceptional 存放连接通信出现的error错误信息。这里使用inputs信息代替
readable, writable, exceptional = select.select(inputs, outputs, inputs)
# Handle inputs
#readsble list中可以有3种可能状态,第一种是如果这个socket是main"server" socket,它负责监听客户端的连接,
for s in readable:
# 如果这个main server socket出现在readable里,那代表这是server端已经ready来接收一个新的连接进来了
if s is server:
# A "readable" server socket is ready to accept a connection
connection, client_address = s.accept() #接收一个新的连接
print('new connection from', client_address) #打印客户端连接的地址
connection.setblocking(False) #设置成非阻塞状态
inputs.append(connection) #将链接添加到inputs链表中
# Give the connection a queue for data we want to send
message_queues[connection] = queue.Queue()
#这种情况是这个socket是已经建立了的连接,它把数据发了过来,这个时候就可以通过recv()来接收它发过来的数据,
# 然后把接收到的数据放到queue里,这样就可以把接收到的数据再传回给客户端了。
else:
data = s.recv(1024) #接收客户端传递的数据
if data: #如果接收的数据不为空
# A readable client socket has data
print(sys.stderr, 'received "%s" from %s' % (data, s.getpeername()) )
message_queues[s].put(data) #从缓存队列里获取数据然后将其传递给客户端
# 如果连接不在发送列表中,则将其添加到发送列表中
if s not in outputs:
outputs.append(s)
else: #如果接收的数据为空
# Interpret empty result as closed connection
print('closing', client_address, 'after reading no data')
# Stop listening for input on the connection
#停止监听这个连接
# 这种情况就是这个客户端已经断开了,所以你再通过recv()接收到的数据就为空了,
# 所以这个时候你就可以把这个跟客户端的连接关闭了。
if s in outputs:
outputs.remove(s) #既然客户端都断开了,我就不用再给它返回数据了,所以这时候如果这个客户端的连接对象还在outputs列表中,就把它删掉
inputs.remove(s) #inputs中也删除掉
s.close() #把这个连接关闭掉
# Remove message queue
del message_queues[s]
# 对于writable list中的socket,也有几种状态,如果这个客户端连接在跟它对应的queue里有数据,就把这个数据取出来再发回给这个客户端,否则就把这个连接从output list中移除,
# 这样下一次循环select()调用时检测到outputs list中没有这个连接,那就会认为这个连接还处于非活动状态
for s in writable:
try:
next_msg = message_queues[s].get_nowait() #获取信息不阻塞等待
except queue.Empty:
# No messages waiting so stop checking for writability.
print('output queue for', s.getpeername(), 'is empty')
outputs.remove(s) #队列为空,则从发送列表中删除连接
else:
print( 'sending "%s" to %s' % (next_msg, s.getpeername()))
s.send(next_msg) #当从队列中获取到信息时将其发送给客户端
#果在跟某个socket连接通信过程中出了错误,就把这个连接对象在inputs\outputs\message_queue中都删除,
# 再把连接关闭掉
for s in exceptional:
print('handling exceptional condition for', s.getpeername() )
# Stop listening for input on the connection
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
# Remove message queue
del message_queues[s]
client端
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import socket
import sys
messages = [ 'This is the message. ',
'It will be sent ',
'in parts.',
]
server_address = ('localhost', 10000)
# Create a TCP/IP socket
socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM),
socket.socket(socket.AF_INET, socket.SOCK_STREAM),
]
# Connect the socket to the port where the server is listening
print(sys.stderr, 'connecting to %s port %s' % server_address)
for s in socks:
s.connect(server_address)
for message in messages:
# Send messages on both sockets
for s in socks:
print(sys.stderr, '%s: sending "%s"' % (s.getsockname(), message))
s.send(bytes(message,'utf8'))
# Read responses on both sockets
for s in socks:
data = s.recv(1024)
print(sys.stderr, '%s: received "%s"' % (s.getsockname(), data))
if not data:
print(sys.stderr, 'closing socket', s.getsockname())
s.close()
参考:http://www.cnblogs.com/wupeiqi/articles/5095821.html
pip2 install paramiko
pip3 install paramiko
#win下python3安装paramiko模块涉及到编译工具的问题没有安装成功,在Linux环境下面测试下面的代码正常。
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import paramiko
'''
基于用户名密码连接
'''
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='192.168.137.5', port=22, username='root', password='shen1234')
# 执行命令
stdin, stdout, stderr = ssh.exec_command('df -h')
# 获取命令结果
result = stdout.read()
#打印结果
print(result.decode())
# 关闭连接
ssh.close()
import paramiko
transport = paramiko.Transport(('192.168.137.5', 22))
transport.connect(username='root', password='shen1234')
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('ifconfig')
print(stdout.read().decode())
transport.close()
import paramiko
def ssh_connet(host,user,shell):
'''
实现ssh远程登录服务器进行相关操作
:param host: 远程服务器地址和端口号 host_ip:port
:param user: 远程服务器用户名密码 user:passwd
:parm shell: 需要执行的shell命令
:return: 返回用户执行shell命令的
'''
host = host.split(":")
user = user.split(":")
transport = paramiko.Transport((host[0], int(host[1])))
transport.connect(username=user[0], password=user[1])
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command(shell)
reaults = stdout.read().decode()
transport.close()
return reaults
print(ssh_connet("192.168.137.5:22","root:shen1234","df -h"))
import paramiko
def ssh_connet(host,user,private_key,shell):
'''
实现ssh远程登录服务器进行相关操作
:param host: 远程服务器地址和端口号 host_ip:port
:param user: 远程服务器用户名密码 user:passwd
:param private_key: 基于公钥的形式登录
:parm shell: 需要执行的shell命令
:return: 返回用户执行shell命令的
'''
host = host.split(":")
transport = paramiko.Transport((host[0], int(host[1])))
transport.connect(username=user[0], key=private_key)
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command(shell)
reaults = stdout.read().decode()
transport.close()
return reaults
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
print(ssh_connet("192.168.137.5:22","root",private_key,"df -h"))
rollback()事务回滚
excutemany()
作业:
1. select()代码,注释
2. 主机批量管理工具
1. saltstack文档阅读(常用架构弄清楚)
2. 修改主机批量管理工具的架构
Author:@南非波波
课程大纲:
day07
http://www.cnblogs.com/alex3714/articles/5213184.html
day08
http://www.cnblogs.com/alex3714/articles/5227251.html
###推荐电影###
绝美之城 上帝之城 | 千与千寻 龙猫 卡尔的移动城堡
通过实例私有变量,需要将在类中封装一个方法,该方法返回私有变量的值
Unix的进程通信机制。一个完整的socket有一个本地唯一的socket号,由操作系统分配。socket是面向客户/服务器模型而设计的,针对客户和服务器程序提供不同的socket系统调用。socket利用客户/服务器模式巧妙的解决了进程之间建立通信连接的问题。
套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
socket.AF_UNIX unix本机进程间通信
socket.AF_INET 使用IPV4地址协议进行进程间通信
socket.AF_INET6 使用IPV6地址协议进行进程间通信
socket.SOCK_STREAM #使用tcp协议
socket.SOCK_DGRAM #使用udp协议
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IFMP等网络报文,而SOCK_RAM可以。其次SOCK_RAM也可以处理特殊的IPV4报文,此外,利用原始套接字可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAW用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAW通常仅限于高级用户或管理员运行的程序使用。
1. socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
2. socket.socketpair([family[, type[, proto]]])
3. socket.create_connection(address[, timeout[, source_address]])
4. socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
获取要连接的对端主机地址
5. sk.bind(address)
将套接字绑定到地址,地址的格式取决于地址簇。在AF_INET下,以元组(host.port)的形式表示地址。
6. sk.listen(backlog)
开始监听传入的连接,backlog指定在拒绝连接之前,可以挂起的最大连接数量。backlog等于5,表示内核已经连接到连接请求,但服务器还没有调用accept进行处理的连接个数最大为5.这个值根据内核和服务器物理配置进行设置。
7. sk.setblocking(bool)
是否阻塞(默认True),如果设置为False,那么accept和recv时一旦无数据则报错。
8. sk.accept()
接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据,address是连接客户端的地址。接收TCP客户的连接(阻塞式)等待连接的到来。
9. sk.connect(address)
连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.err错误。
10. sk.connect_ex(address)
同上,只是会有返回值,连接成功时返回0,连接失败时会返回编码,例如:10061
11. sk.close()
关闭套接字
12. sk.recv(bufsize[,flag])
接收套接字的数据,数据以字符串形式返回。bufsize指定最多可以接收的数量,建议不要超过1024*8。flag提供有关消息的其他信息。通常可以忽略。
13. sk.recvfrom(bufsize[.flag])
与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址
14. sk.send(string[,flag])
将string中的数据发送到连接的套接字,返回值是要发送的字节数量,该数量可能小于string的字节大小,即:可能未壮指定内容全部发送
15. sk.sendall(string[,flag])
将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则跑出异常。内部通过递归调用send将所有内容发送出去。
16. sk.sendto(string[,flag],address)
将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
17. sk.settimeout(timeout)
设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
18. sk.getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
19. sk.getsockname()
返回套接字自己的地址,通常在是一个元组(ipaddr,port)
20. socket.gethostname()
获取程序运行所在计算机的主机名
21. gethostbyname(name)
尝试将给定的主机名解释为一个IP地址。首先将检查当前计算机是否能够解释。如果不能,一个解释请求将发送给一个远程的DNS服务器(远程的DNS服务器 还可能将解释请求转发给另一个DNS服务器,直到该请求可以被处理)。gethostbyname函数返回这个IP地址或在查找失败后引发一个异常。例如: socket.gethostbyname('www.apicloud.com')
扩展形式:socket.gethostbyname_ex('www.apicloud.com')
('98e86f98d416f10c.7cname.com', ['www.apicloud.com'], ['117.25.140.17'])
它返回一个包含三个元素的元组,分别是给定地址的主要的主机名、同一IP地址的可选的主机名的一个列表、关于同一主机的同一接口的其它IP地址的一个列表(列表可能都是空的)。
22. gethostbyaddr(address)
函数的作用与gethostbyname_ex相同,只是你提供给它的参数是一个IP地址字符串。
socket.gethostbyaddr('202.165.102.205')
('homepage.vip.cnb.yahoo.com', ['www.yahoo.com.cn'], ['202.165.102.205'])
23. getservbyname(service,protocol)
函数要求一个服务名(如'telnet'或'ftp')和一个协议(如'tcp'或'udp'),返回服务所使用的端口号:
>>> socket.getservbyname('http','tcp')
80
>>> socket.getservbyname('https','tcp')
443
>>> socket.getservbyname('telnet','tcp')
24. sk.fileno()
套接字的文件描述符
socket.sendfile(file, offset=0, count=None)
发送文件 ,但目前多数情况下并无什么卵用
参考链接:http://my.oschina.net/u/1433482/blog/190612
SocketServer简化了网络服务器的编写。它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。
首先,你必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。其次,你必须实例化一个服务器类,传入服务器的地址和请求处理程序类。最后,调用handle_request()(一般是调用其他事件循环或者使用select())或serve_forever()。
5种类型:BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。 注意:BaseServer不直接对外服务。
class SocketServer.BaseServer:这是模块中的所有服务器对象的超类。它定义了接口,如下所述,但是大多数的方法不实现,在子类中进行细化。
BaseServer.fileno():返回服务器监听套接字的整数文件描述符。通常用来传递给select.select(), 以允许一个进程监视多个服务器。
BaseServer.handle_request():处理单个请求。处理顺序:get_request(), verify_request(), process_request()。如果用户提供handle()方法抛出异常,将调用服务器的handle_error()方法。如果self.timeout内没有请求收到, 将调用handle_timeout()并返回handle_request()。
BaseServer.serve_forever(poll_interval=0.5): 处理请求,直到一个明确的shutdown()请求。每poll_interval秒轮询一次shutdown。忽略self.timeout。如果你需要做周期性的任务,建议放置在其他线程。
BaseServer.shutdown():告诉serve_forever()循环停止并等待其停止。python2.6版本。
BaseServer.address_family: 地址家族,比如socket.AF_INET和socket.AF_UNIX。
BaseServer.RequestHandlerClass:用户提供的请求处理类,这个类为每个请求创建实例。
BaseServer.server_address:服务器侦听的地址。格式根据协议家族地址的各不相同,请参阅socket模块的文档。
BaseServer.socketSocket:服务器上侦听传入的请求socket对象的服务器。
服务器类支持下面的类变量:
BaseServer.allow_reuse_address:服务器是否允许地址的重用。默认为false ,并且可在子类中更改。
BaseServer.request_queue_size
请求队列的大小。如果单个请求需要很长的时间来处理,服务器忙时请求被放置到队列中,最多可以放request_queue_size个。一旦队列已满,来自客户端的请求将得到 “Connection denied”错误。默认值通常为5 ,但可以被子类覆盖。
BaseServer.socket_type:服务器使用的套接字类型; socket.SOCK_STREAM和socket.SOCK_DGRAM等。
BaseServer.timeout:超时时间,以秒为单位,或 None表示没有超时。如果handle_request()在timeout内没有收到请求,将调用handle_timeout()。
下面方法可以被子类重载,它们对服务器对象的外部用户没有影响。
BaseServer.finish_request():实际处理RequestHandlerClass发起的请求并调用其handle()方法。 常用。
BaseServer.get_request():接受socket请求,并返回二元组包含要用于与客户端通信的新socket对象,以及客户端的地址。
BaseServer.handle_error(request, client_address):如果RequestHandlerClass的handle()方法抛出异常时调用。默认操作是打印traceback到标准输出,并继续处理其他请求。
BaseServer.handle_timeout():超时处理。默认对于forking服务器是收集退出的子进程状态,threading服务器则什么都不做。
BaseServer.process_request(request, client_address) :调用finish_request()创建RequestHandlerClass的实例。如果需要,此功能可以创建新的进程或线程来处理请求,ForkingMixIn和ThreadingMixIn类做到这点。常用。
BaseServer.server_activate():通过服务器的构造函数来激活服务器。默认的行为只是监听服务器套接字。可重载。
BaseServer.server_bind():通过服务器的构造函数中调用绑定socket到所需的地址。可重载。
BaseServer.verify_request(request, client_address):返回一个布尔值,如果该值为True ,则该请求将被处理,反之请求将被拒绝。此功能可以重写来实现对服务器的访问控制。默认的实现始终返回True。client_address可以限定客户端,比如只处理指定ip区间的请求。 常用。
处理器接收数据并决定如何操作。它负责在socket层之上实现协议(i.e., HTTP, XML-RPC, or AMQP),读取数据,处理并写反应。可以重载的方法如下:
setup(): 准备请求处理. 默认什么都不做,StreamRequestHandler中会创建文件类似的对象以读写socket.
handle(): 处理请求。解析传入的请求,处理数据,并发送响应。默认什么都不做。常用变量:self.request,self.client_address,self.server。
finish(): 环境清理。默认什么都不做,如果setup产生异常,不会执行finish。
通常只需要重载handle。self.request的类型和数据报或流的服务不同。对于流服务,self.request是socket 对象;对于数据报服务,self.request是字符串和socket 。可以在子类StreamRequestHandler或DatagramRequestHandler中重载,重写setup()和finish() ,并提供self.rfile和self.wfile属性。 self.rfile和self.wfile可以读取或写入,以获得请求数据或将数据返回到客户端。
1.server
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import socketserver
class MyHandleServer(socketserver.BaseRequestHandler):
'''
定义一个测试socketserver类
'''
def handle(self):
'''
定义一个函数,用来处理每个客户端发来的请求
:return:
'''
print("新建立一个连接:",self.client_address)
while True:
try:
client_data = self.request.recv(1024)
if not client_data:
print("客户端发送的数据为空,主动断开!",self.client_address)
break
print("客户端发来的请求:",client_data.decode())
self.request.send(client_data)
except ConnectionResetError:
print("客户端主动断开!",self.client_address)
break
if __name__ == "__main__":
HOST,PORT = "127.0.0.1",5000
server = socketserver.ThreadingTCPServer((HOST,PORT),MyHandleServer)
server.serve_forever()
2.client
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
import socket
ip_port = ("127.0.0.1",5000)
sk = socket.socket()
sk.connect(ip_port)
while True:
msg = input(">>:").strip()
if not msg:
break
sk.sendall(bytes(msg,"utf8"))
server_reply = sk.recv(1024)
print("服务端返回:",str(server_reply,"utf8"))
sk.close()
http://www.cnblogs.com/wupeiqi/articles/5017742.html
在编程过程中为了增加友好性,在程序出现bug时一般不会将错误信息显示给用户,而是显示一个提示的页面,通俗来说就是不让用户看见代码出错的页面
try:
pass
except Exception as ex:
pass
常用异常:
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
更多种类:
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception #捕获一般的所有异常
FloatingPointError
FutureWarning
GeneratorExit
ImportError #捕获导入模块异常
ImportWarning
IndentationError
IndexError #捕获索引异常
IOError #捕获IO异常
KeyboardInterrupt #捕获键盘组合键异常
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
#!/usr/local/env python3
'''
Author:@南非波波
Blog:http://www.cnblogs.com/songqingbo/
E-mail:qingbo.song@gmail.com
'''
class SwhtError(Exception):
'''
自定义异常
'''
def __init__(self,msg):
'''
初始化函数
:param msg:用户输入message
:return:
'''
self.message = msg
def __str__(self): #名称可以自行定义,只是通过该方法返回message的值
'''
返回用户输入的信息
:return:
'''
return self.message
try:
raise SwhtError("这是一个致命的错误!")
except Exception as e:
print("dsdsd",e)
dic = ["swht", 'shen']
try:
dic[10]
except IndexError as e:
print("IndexError:",e)
dic = {'k1':'v1'}
try:
dic['k20']
except KeyError as e:
print("keyError:",e)
s1 = 'hello'
try:
int(s1)
except ValueError as e:
print("ValueError:",e)
虽然python自带的一个处理万能异常类Exception,但是并不是有一些异常都能被捕获的。如果要想捕获这些特殊的异常,就需要进行自定义异常类
try:
# 主代码块
pass
except KeyError as e:
# 异常时,执行该块
pass
else:
# 主代码块执行完,执行该块
pass
finally:
# 无论异常与否,最终执行该块
pass
try:
raise Exception('错误了。。。')
except Exception as e:
print("Error",e)
至关重要的判断,强制判断前面的业务结果是否符合要求,否则就抛出异常
a = 1
try:
assert a == 2
print("True")
except Exception as e:
print("False",e)
1.进程
一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是系统进行资源分配和调度运行的基本单位
2.线程
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
3.进程与线程的区别
1. 进程是一个动态的概念
进程是程序的一次执行过程,是动态概念
程序是一组有序的指令集和,是静态概念
2. 不同的进程可以执行同一个程序
区分进程的条件:所执行的程序和数据集合。
两个进程即使执行在相同的程序上,只要他们运行在不同的数据集合上,他们也是两个进程。例如:多个用户同时调用同一个编译程序编译他们编写的C语言源程序,由于编译程序运行在不同的数据集合(不同的C语言源程序)上,于是产生了一个个不同的进程
3. 每个进程都有自己的生命周期
当操作系统要完成某个任务时,它会创建一个进程。当进程完成任务之后,系统就会撤销这个进程,收回它所占用的资源。从创建到撤销的时间段就是进程的生命期
4. 进程之间存在并发性
在一个系统中,同时会存在多个进程。他们轮流占用CPU和各种资源
5. 进程间会相互制约
进程是系统中资源分配和运行调度的单位,在对资源的共享和竞争中,必然相互制约,影响各自向前推进的速度
6. 进程可以创建子进程,程序不能创建子程序
7. 从结构上讲,每个进程都由程序、数据和一个进程控制块(Process Control Block, PCB)组成
Python threading模块
1.线程的两种调用方式
1. 直接调用
2. 继承式调用
2.join&&Demo
3.线程锁
1. 互斥锁:同时仅且只有一个线程在运行
2. 共享锁:同时可以有多个线程共同运行,可以实现一个线程池的效果
2. 递归锁
3. Semaphore(信号量)
#!/usr/local/env python3
import threading,time
def addNum():
global num #声明修改全局变量
print("--get num:",num)
time.sleep(1)
lock.acquire()
num -=1
lock.release()
lock = threading.Lock()
num = 100
threading_list = []
for i in range(2040):
t = threading.Thread(target=addNum)
t.start()
threading_list.append(t)
for t in threading_list: #等待所有线程执行完毕
t.join()
print("num:",num)
event
多进程
进程间通讯
队列
管道
manager
Manager 所有的子进程和父进程之间都能进行数据的共享
Manager 是一个安全的进程管理池,不需要加进程锁
进程同步
进程池
开启一个进程的系统开销太大,设置进程池限制启动的进程数,以达到保护系统的目的。另外在线程池内的进程可以实现同步或异步调用。