上海防疫何以至此

上海防疫何以至此
上海“分区封控”检测核酸 
Aly Song/Reuters
原创:维舟
来源:维舟
上海本轮疫情已持续一个多月,至今仍处于高位,这两天新增确诊+无症状感染者每天都超过2万人。有朋友对此深感困惑:“都封得这么严了,为什么还这样?到底哪里没做到位?”
他这么问,隐含着一个前提:只要封得够严,“本应”迅速好转。照这个思路,确实无法理解,因为他没有考虑另一种可能:封控手段对这种新病毒已经无效、或效力有限了——就像你筑堤坝可以堵住水流,但却挡不住水汽。
上海这次防疫何以陷入今天这样的境地?现在回头来看,或许从一开始,这就是不可避免的。
上海防疫何以至此
没错,不是没有人预料到疫情会爆发,现在很多人提到张文宏早在1月就提醒,要针对“疫情规模超出现在5倍或者10倍”做好预案。
然而,冷静下来想想,恐怕张文宏也没料到是现在这番情形,因为显而易见的,现在相比起上海此前两年里零星的感染病例,何止是5-10倍的规模?那是上千倍的海啸!
我当然并不比他更高明,出于对开放后病例暴增的隐忧,1月20日我写过一篇上海能扛住吗?》,然而我得承认,当时我想的仍然是上海原有那套“精准防控”模式下如何更好、更快速地处理更多病例——换言之,是旧有模式的优化,而没有觉察到这套模式隐藏的问题。
“精准防控”是一种走钢丝般的微妙平衡,看上去简直完美地兼顾了多个目标:
  • 快速、专业地控制本地疫情,几乎没有外溢;

  • 医疗资源未被挤兑,没有次生灾害;

  • 对社会生活、经济活动的干扰最小化;

  • 保障个体的权利。

上海防疫何以至此
更妙的是,“精准防控”虽然常被视为上海独有的模式,但其实也完全吻合“动态清零”的要求。直到4月5日,在外界看来上海的“精准防控”早成往事时,张伯礼院士在接受采访时仍表示,“当前上海全市上下,全国上下都要统一思想、坚定信念,就是坚持精准防控、动态清零”。
正因为当时做得太成功,上海的表现曾引来无数好评,以至于许多人都好奇一点:“上海究竟是怎么做到的?”但少有人注意到的是:这种平衡需要高度的技巧和小心谨慎,其实很容易颠覆。
上海防疫何以至此
上海流调人员 
Hector Retamal
Agence France-Presse
Getty Images
专栏作家刘远举日前在一篇《疫情中上海能做得更好的判断,并未过时》的文章里说:
政府能力也不是按照疫情来设计的,一旦压力越过一个阈值,政府能力跟不上,就会出现混乱。上海、吉林,西安都是如此。上海相关资源、能力、专业度、素质的冗余度当然会高一些,正是这些因素让上海应对德尔塔变种可以做到游刃有余,但当压力越过一个更高的门槛时,也会被击溃,而击溃之后的混乱类似的。奥密克戎就是一个更高的压力。
在此可以看出,他认为本轮疫情的混乱,是因量变引发的压力暴增。这一点应该没有异议,“城市数据团”在《多强的封控政策,才能防住奥密克戎?》中估算,在同等的初始爆发条件和防控力度下,奥密克戎的感染力比此前的病毒高出5.82倍,因而目前看到唯一能确保清零的办法,“就是在第一例病例出现后,立刻采取全城封控”。
现在问题来了:这样一种做法,在国内其它地方或许有先例,却正是上海最为排斥的,并坚持到了最后一刻。3月27日晚宣布划江封控,但就在前一天,上海防疫专家吴凡仍对外宣称上海不能封城,而此时仅八天的无症状感染者就已超过了1万例。
也就是说,上海此前的模式,在决策优先级上,最看重的是保障这座超大城市的流动性和正常运转,基于此,在充满不确定性风险的局面下,以有限的资源来管理疫情不至失控。“精准防控”并不是因为上海多有钱,恰恰是因为没钱,深知自身的有限性,竭力避免总动员。
上海防疫何以至此
在3月1日的疫情防控发布会上,吴凡还说过,上海无症状感染者多,这“对个体是好事,对面上防控是巨大挑战”。这相当于委婉地承认:无症状是可自愈的轻微感染,对个体伤害不大,但尽管如此,当下却仍需要防控——甚至正因它毒性低而感染力强,“面上防控”变得更难了。
从后视镜来看,精准防控前期太成功也造成了后面的大溃败,因为你太成功就很难去放弃这个策略。
精准防控对付不了Omicron,即便没有华亭宾馆,上海也迟早会被攻陷。因为精准防控的前提是发现及时、快速甄别隔离、病毒感染力相对较低,流调跑在病毒传播前面,但Omicron的隐蔽性、感染力都极强,使得这些都落空了——如果发现时已经快速传播开了,那筛查密接是难以想象的工作量,即便可能,也已经失去意义了。
此时,防疫人员就陷入了两难:他们无法再像以前那样完美地兼顾多个目标,而只能在相互冲突的要求之间做出痛苦的抉择。
现在我们都已经知道结果了:他们起初还是最优先确保上海的开放流动,但随着感染人数指数级暴增,人力和医疗资源遭到挤兑,此时唯一的办法就是推行抗原自测、居家隔离,将有限的资源用于救治确诊病例和危重症。
然而,这就势必顾此失彼,无法实现所有阳性清零,也难以阻止外溢。此时就面临一个决断:要么全国各地陆续跟进上海的做法,要么是上海跟其它地方一样一刀切。结果是没有意外的。
上海防疫何以至此
事后有人说:“我们上海人本来都已经躺平了,又被全国人民硬生生扶起来。”语虽戏谑,但却一语道破真相。是不是“优等生”,每个人判断不同,但有一点可以肯定:当疫情外溢,上海就无法单独探索一条新的道路了,只能和全国步调一致。
如果决策过程是这样,那也表明,上海的防疫原本是着眼于本地视角、医学视角,但最终却正因此而导致翻船。因为他们在作出决策时,只考虑如何从医学上控制住本地疫情,并深信这是科学合理的,但却没有充分意识到一点:这种决策的自由度,只有当它不影响到全局的情况下,才能得到容忍。
医学专家凭借其专业,在决策时可以相对灵活,一线医护人员面对病人也可以有一定的自主权;这不像有些基层社区,只管执行,不管死活。然而,如果对专家的考核也是“一例阳性,全院停诊”,那么结果和社区恐怕也不会有什么差别。
也就是说,专家的决策自由度只能在一个有限的范围内存在,一旦“溢出”,就不是专家能说了算的了。当然,此前上海坚持不封城的最重要理由(“上海是国际大都市,不能封”)其实也是个政治判断,但这不如说是为了抬出来保全这种有限决策自由的。
前些天在一个群里,有朋友提到经济学家Thomas Sowell的名言:“当代价不由决策者承担时,犯错是容易的,坚持错误也是容易的。”他原本是想说上海人吃的苦不值得,但讽刺的是,有人却将之理解为当初上海决策失误,导致外溢,而代价由外地来承受。实际上,更契合当下情形的,或许是这位经济学家的另一句名言:“最重要的决策是谁来做决策。”
上海防疫何以至此
上海模式失败了吗?眼下不得而知。很多人都没有意识到的一点是:当下强硬的“动态清零”模式,其实和上海的“精准防控”一样,都基于对资源有限性的认识。
在3月28日之后,“全国驰援上海”的景象,说白了,就是上海资源不足,从全国范围内调动资源来加码。然而,全国的资源也同样有个限度。昨天还有个外地朋友跟我说,现在各地还能援助上海,要是哪天各地都爆发了,自顾不暇,谁来救?
从这一意义上说,现在的做法相当于是上海走到一半,突然掉过头去重新再走一遍,“上海撑不住,全国来撑你”。然而,就算这次能撑住,仍然没有走出新路子。一旦全国都陷入上海当初的境地,那么可能还是不得不走上海走到一半的那条路。
假如我们在付出巨大代价后,能争取到一点缓冲时间,可千万别再像之前,白白浪费了一年多窗口期了。
留给我们的时间可能不多了。

转自:https://mp.weixin.qq.com/s/pZCf8cwsXzAbSEkKQYhyvA

STL总结与常见面试题

1 STL概述

为了建立数据结构和算法的一套标准,并且降低他们之间的耦合关系,以提升各自的独立性、弹性、交互操作性(相互合作性,interoperability),诞生了STL。

STL提供了六大组件,彼此之间可以组合套用,这六大组件分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  • 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据,从实现角度来看,STL容器是一种class template。

  • 算法:各种常用的算法,如sort、find、copy、for_each。从实现的角度来看,STL算法是一种function tempalte.

  • 迭代器:扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将operator* , operator-> , operator++,operator–等指针相关操作予以重载的class template. 所有STL容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。原生指针(native pointer)也是一种迭代器。

  • 仿函数:行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operator()的class 或者class template

  • 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。

  • 空间配置器:负责空间的配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class tempalte.

STL六大组件的交互关系,容器通过空间配置器取得数据存储空间,算法通过迭代器存储容器中的内容,仿函数可以协助算法完成不同的策略的变化,适配器可以修饰仿函数。

2 STL的优点:

  1. STL 是 C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
  2. STL 的一个重要特性是将数据和操作分离。数据由容器类别加以管理,操作则由可定制的算法定义。迭代器在两者之间充当“粘合剂”,以使算法可以和容器交互运作
  3. 程序员可以不用思考 STL 具体的实现过程,只要能够熟练使用 STL 就 OK 了。这样他们就可以把精力放在程序开发的别的方面。
  4. STL 具有高可重用性,高性能,高移植性,跨平台的优点。
  5. 高可重用性:STL 中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
  6. 高性能:如 map 可以高效地从十万条记录里面查找出指定的记录,因为 map 是采用红黑树的变体实现的。
  7. 高移植性:如在项目 A 上用 STL 编写的模块,可以直接移植到项目 B 上。容器和算法之间通过迭代器进行无缝连接。STL 几乎所有的代码都采用了模板类或者模板函数,这相比传统的由函数和类组成的库来说提供了更好的代码重用机会。

3 容器

STL容器就是将运用最广泛的一些数据结构实现出来。

常用的数据结构:数组(array) , 链表(list), tree(树),栈(stack), 队列(queue), 集合(set),映射表(map), 根据数据在容器中的排列特性,这些数据分为序列式容器和关联式容器两种。

序列式容器强调值的排序,序列式容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。Vector容器、Deque容器、List容器等。

关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。关联式容器另一个显著特点是:在值中选择一个值作为关键字key,这个关键字对值起到索引的作用,方便查找。Set/multiset容器 Map/multimap容器

容器 底层数据结构 时间复杂度 有无序 可不可重复
array 数组 随机读改 O(1) 无序 可重复
vector 数组 随机读改、尾部插入、尾部删除 O(1)头部插入、头部删除 O(n) 无序 可重复
deque 双端队列 头尾插入、头尾删除 O(1) 无序 可重复
forward_list 单向链表 插入、删除 O(1) 无序 可重复
list 双向链表 插入、删除 O(1) 无序 可重复
stack deque / list 顶部插入、顶部删除 O(1) 无序 可重复
queue deque / list 尾部插入、头部删除 O(1) 无序 可重复
priority_queue vector /max-heap 插入、删除 O(log2n) 有序 可重复
set 红黑树 插入、删除、查找 O(log2n) 有序 不可重复
multiset 红黑树 插入、删除、查找 O(log2n) 有序 可重复
map 红黑树 插入、删除、查找 O(log2n) 有序 不可重复
multimap 红黑树 插入、删除、查找 O(log2n) 有序 可重复
unordered_set 哈希表 插入、删除、查找 O(1) 最差 O(n) 无序 不可重复
unordered_multiset 哈希表 插入、删除、查找 O(1) 最差 O(n) 无序 可重复
unordered_map 哈希表 插入、删除、查找 O(1) 最差 O(n) 无序 不可重复
unordered_multimap 哈希表 插入、删除、查找 O(1) 最差 O(n) 无序 可重复

1 array

array 是固定大小的顺序容器,它们保存了一个以严格的线性顺序排列的特定数量的元素。

方法 说明
begin 返回指向数组容器中第一个元素的迭代器
end 返回指向数组容器中最后一个元素之后的理论元素的迭代器
rbegin 返回指向数组容器中最后一个元素的反向迭代器
rend 返回一个反向迭代器,指向数组中第一个元素之前的理论元素
cbegin 返回指向数组容器中第一个元素的常量迭代器(const_iterator)
cend 返回指向数组容器中最后一个元素之后的理论元素的常量迭代器(const_iterator)
crbegin 返回指向数组容器中最后一个元素的常量反向迭代器(const_reverse_iterator)
crend 返回指向数组中第一个元素之前的理论元素的常量反向迭代器(const_reverse_iterator)
size 返回数组容器中元素的数量
max_size 返回数组容器可容纳的最大元素数
empty 返回一个布尔值,指示数组容器是否为空
operator[] 返回容器中第 n(参数)个位置的元素的引用
at 返回容器中第 n(参数)个位置的元素的引用
front 返回对容器中第一个元素的引用
back 返回对容器中最后一个元素的引用
data 返回指向容器中第一个元素的指针
fill 用 val(参数)填充数组所有元素
swap 通过 x(参数)的内容交换数组的内容
get(array) 形如 std::get<0>(myarray);传入一个数组容器,返回指定位置元素的引用
relational operators (array) 形如 arrayA > arrayB;依此比较数组每个元素的大小关系

测试代码

#include<iostream>
#include<array>
using namespace std;

int main() 
{
	array<int, 8> myArr = {1,3,4,6,9};//固定大小为8
	cout << "myArr元素序列:";
	for (auto i = 0; i < 8; ++i) 
	{
		cout << myArr[i] << " ";
	}
	cout << endl;

	array<int, 8> myArr1 = {2,3,4,7,8,9};//固定大小为8
	cout << "myArr1元素序列:";
	for (auto i = 0; i < 8; ++i) 
	{
		cout << myArr1[i] << " ";
	}
	cout << endl;

	myArr.swap(myArr1);   //交换两个容器的内容
	cout << "交换myArr与myArr1"<< endl;
	cout << endl;

	cout << "myArr.at(3) = " << myArr.at(3) << endl;//任意访问
	cout << "myArr[3] = " << myArr[3] << endl;//任意访问
	cout << "myArr.front() = " << myArr.front() << endl;//获取第一个元素
	cout << "myArr.back() =  " << myArr.back() << endl;//获取最后一个元素
	cout << "myArr.data() = " << myArr.data() << endl;//获取第一个元素的指针
	cout << "*myArr.data() = " << *myArr.data() << endl;//获取第一个元素的指针指向的元素

	cout << "正向迭代器遍历容器:";
	for (auto it = myArr.begin(); it != myArr.end(); ++it) 
	{
		cout << *it << " ";
	}
	cout << endl;
	//逆向迭代器测试
	cout << "逆向迭代器遍历容器:";
	for (auto rit = myArr.rbegin(); rit != myArr.rend(); ++rit) 
	{
		cout << *rit << " ";
	}
	cout << endl;
	//正向常迭代器测试
	cout << "正向常迭代器遍历容器:";
	for (auto it = myArr.cbegin(); it != myArr.cend(); ++it) 
	{
		cout << *it << " ";
	}
	cout << endl;
	//逆向常迭代器测试
	cout << "逆向常迭代器遍历容器:";
	for (auto rit = myArr.crbegin(); rit != myArr.crend(); ++rit) 
	{
		cout << *rit << " ";
	}
	cout << endl;
	if(myArr.empty())
		cout << "myArr为空 " << endl;
	else
		cout << "myArr不为空 " << endl;
	cout << "myArr.size() = " << myArr.size() << endl;
	cout << "myArr.max_size() = " << myArr.max_size() << endl;

	return 0;
}

运行结果

STL总结与常见面试题
运行结果

vector

vector 是表示可以改变大小的数组的序列容器。

方法 说明
vector 构造函数
~vector 析构函数,销毁容器对象
operator= 将新内容分配给容器,替换其当前内容,并相应地修改其大小
begin 返回指向容器中第一个元素的迭代器
end 返回指向容器中最后一个元素之后的理论元素的迭代器
rbegin 返回指向容器中最后一个元素的反向迭代器
rend 返回一个反向迭代器,指向中第一个元素之前的理论元素
cbegin 返回指向容器中第一个元素的常量迭代器(const_iterator)
cend 返回指向容器中最后一个元素之后的理论元素的常量迭代器(const_iterator)
crbegin 返回指向容器中最后一个元素的常量反向迭代器(const_reverse_iterator)
crend 返回指向容器中第一个元素之前的理论元素的常量反向迭代器(const_reverse_iterator)
size 返回容器中元素的数量
max_size 返回容器可容纳的最大元素数
resize 调整容器的大小,使其包含 n(参数)个元素
capacity 返回当前为 vector 分配的存储空间(容量)的大小
empty 返回 vector 是否为空
reserve 请求 vector 容量至少足以包含 n(参数)个元素
shrink_to_fit 要求容器减小其 capacity(容量)以适应其 size(元素数量)
operator[] 返回容器中第 n(参数)个位置的元素的引用
at 返回容器中第 n(参数)个位置的元素的引用
front 返回对容器中第一个元素的引用
back 返回对容器中最后一个元素的引用
data 返回指向容器中第一个元素的指针
assign 将新内容分配给 vector,替换其当前内容,并相应地修改其 size
push_back 在容器的最后一个元素之后添加一个新元素
pop_back 删除容器中的最后一个元素,有效地将容器 size 减少一个
insert 通过在指定位置的元素之前插入新元素来扩展该容器,通过插入元素的数量有效地增加容器大小
erase 从 vector 中删除单个元素(position)或一系列元素([first,last)),这有效地减少了被去除的元素的数量,从而破坏了容器的大小
swap 通过 x(参数)的内容交换容器的内容,x 是另一个类型相同、size 可能不同的 vector 对象
clear 从 vector 中删除所有的元素(被销毁),留下 size 为 0 的容器
emplace 通过在 position(参数)位置处插入新元素 args(参数)来扩展容器
emplace_back 在 vector 的末尾插入一个新的元素,紧跟在当前的最后一个元素之后
get_allocator 返回与vector关联的构造器对象的副本
swap(vector) 容器 x(参数)的内容与容器 y(参数)的内容交换。两个容器对象都必须是相同的类型(相同的模板参数),尽管大小可能不同
relational operators (vector) 形如 vectorA > vectorB;依此比较每个元素的大小关系

测试代码

#include <vector>
#include <iostream>
using namespace std;

int main()
{

	//构造函数,复制构造函数(元素类型要一致),
	vector<int> vecA;  //创建一个空的的容器
	vector<int> vecB(10,20); //创建一个10个元素,每个元素值为20
	vector<int> vecC(vecB.begin(),vecB.end()); //使用迭代器,可以取部分元素创建一个新的容器
	vector<int> vecD(vecC); //复制构造函数,创建一个完全一样的容器

	//重载=
	vector<int> vecE;
	vecE = vecB;

	//vector::begin(),返回的是迭代器

	vector<int> vecF(10); //创建一个有10个元素的容器
	cout << "vecF:";
	for (int i = 0; i < 10; i++)
	{
		vecF[i] = i;
		cout << vecF[i]<< " ";
	}
	cout << endl;

	//vector::begin() 返回迭代器
	vector<int>::iterator Beginit = vecF.begin();
	cout<< "vecF.begin():" << *Beginit << endl; 

	//vector::end() 返回迭代器
	vector<int>::iterator EndIter = vecF.end();
	EndIter--; //向后移一个位置
	cout <<"vecF.end():"<< *EndIter << endl; 

	//vector::rbegin() 返回倒序的第一个元素,相当于最后一个元素
	vector<int>::reverse_iterator ReverBeIter = vecF.rbegin();
	cout << "vecF.rbegin(): "<< *ReverBeIter << endl; 

	//vector::rend() 反序的最后一个元素下一个位置,也相当于正序的第一个元素前一个位置
	vector<int>::reverse_iterator ReverEnIter = vecF.rend();
	ReverEnIter--;
	cout << "vecF.rend():"<< *ReverEnIter << endl; 

	//vector::size() 返回元素的个数
	cout << "vecF.size():"<< vecF.size() << endl; 

	//vector::max_size()
	cout << "vecF.max_size():"<< vecF.max_size() << endl; 

	//vector::resize()
	cout<< "vecF.size():" << vecF.size() << endl; 
	vecF.resize(5);

	cout<< "调整vecF大小后重新赋值:"; 
	for(int k = 0; k < vecF.size(); k++)
		cout << vecF[k] << "  "; 
	cout << endl;

	//vector::capacity()
	cout<< "调整后vecF.size():"<< vecF.size() << endl; 
	cout<< "调整后vecF.capacity():" << vecF.capacity() << endl; 

	//vector::empty()
	vecB.resize(0);
	cout<< "vecB.resize(0)后"<< endl; 

	cout  << "vecB.size():" << vecB.size() << endl; 
	cout  << "vecB.capacity():" << vecB.capacity() << endl; 
	if(vecB.empty())
	    cout << "vecB为空"<< endl; 
	else
		cout << "vecB不为空"<< endl; 

	//vector::reserve() //重新分配存储空间大小
	cout<< "vecC.capacity():" << vecC.capacity() << endl; //

	vecC.reserve(4);
	cout << "vecC.reserve(4)后vecC.capacity(): "<< vecC.capacity() << endl; //10
	vecC.reserve(14);
	cout << "vecC.reserve(14)后vecC.capacity(): "<< vecC.capacity() << endl; //14

	//vector::operator []
	cout << "vecF[0]:"<< vecF[0] << endl; //第一个元素是0

	//vector::at()
	try
	{
		cout << "vecF.size = " << vecF.size() << endl; //5
		cout << vecF.at(6) << endl; //抛出异常
	}
	catch(out_of_range)
	{	
		cout << "at()访问越界" << endl;
	}

	//vector::front() 返回第一个元素的值
	cout << "vecF.front():"<< vecF.front() << endl; //0

	//vector::back()
	cout << "vecF.back():"<< vecF.back() << endl; //4

	//vector::assign()
	cout <<"vecA.size():"<< vecA.size() << endl; //0
	vector<int>::iterator First = vecC.begin();
	vector<int>::iterator End = vecC.end()-2;
	vecA.assign(First,End);
	cout << vecA.size() << endl; //8
	cout << vecA.capacity() << endl; //8

	vecA.assign(5,3); //将丢弃原来的所有元素然后重新赋值
	cout << vecA.size() << endl; //5
	cout << vecA.capacity() << endl; //8

	//vector::push_back()
	cout << *(vecF.end()-1) << endl; //4
	vecF.push_back(20);
	cout << *(vecF.end()-1) << endl; //20

	//vector::pop_back()
	cout << *(vecF.end()-1) << endl; //20
	vecF.pop_back();
	cout << *(vecF.end()-1) << endl; //4

	//vector::swap()
	cout << "vecF:";
	for (int i = 0; i < vecF.size(); i++)
	{
		vecF[i] = i;
		cout << vecF[i]<< " ";
	}
	cout << endl;
	cout << "vecD:";
	for (int d = 0; d < vecD.size(); d++)
	{
		vecD[d] = d;
		cout << vecD[d]<< " ";
	}
	cout << endl;

	vecF.swap(vecD); //交换这两个容器的内容
	cout <<"vecD与vecF交换后:" <<endl;
	cout << "vecF:";
	for(int f = 0; f < vecF.size(); f++)
		cout << vecF[f] << " ";
	cout << endl;

	cout << "vecD:";
	for (int d = 0; d <vecD.size(); d++)
		cout << vecD[d] << " ";
	cout << endl;
	//vector::clear()
	vecF.clear();
	cout << "vecF.clear()后vecF.size():"<< vecF.size() << endl;     //0
	cout << "vecF.clear()后vecF.capacity():"<< vecF.capacity() << endl; //10

	return 0;
}

运行结果

STL总结与常见面试题
运行结果

deque

deque容器为一个给定类型的元素进行线性处理,像向量一样,它能够快速地随机访问任一个元素,并且能够高效地插入和删除容器的尾部元素。但它又与vector不同,deque支持高效插入和删除容器的头部元素,因此也叫做双端队列。

STL总结与常见面试题

deque的中控器: deque是由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。避开了“重新配置、复制、释放”的轮回,代价则是复杂的迭代器结构。

STL总结与常见面试题

deque采用一块所谓的map(不是STL的map容器)作为主控。

map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。

缓冲区才是deque的储存空间主体。

template<class T, class Alloc = alloc, size_t BufSiz = 0>  
class deque{  
public :  
    typedef T value_type ;  
    typedef value_type* pointer ;  
    ...  
protected :  
    //元素的指针的指针(pointer of pointer of T)  
    // 其实就是T**,一个二级指针,维护一个二维数组
    typedef pointer* map_pointer ; 
  
protected :  
    map_pointer map ; //指向map,map是块连续空间,其内的每个元素  
                      //都是一个指针(称为节点),指向一块缓冲区  
    size_type map_size ;//map内可容纳多少指针  
    ...  
};  

map其实是一个T**,也就是说它是一个指针,所指之物也是一个指针,指向型别为T的一块空间。

方法 说明
deque 构造函数
push_back 在当前的最后一个元素之后 ,在 deque 容器的末尾添加一个新元素
push_front 在 deque 容器的开始位置插入一个新的元素,位于当前的第一个元素之前
pop_back 删除 deque 容器中的最后一个元素,有效地将容器大小减少一个
pop_front 删除 deque 容器中的第一个元素,有效地减小其大小
emplace_front 在 deque 的开头插入一个新的元素,就在其当前的第一个元素之前
emplace_back 在 deque 的末尾插入一个新的元素,紧跟在当前的最后一个元素之后

测试代码

#include "stdafx.h"
#include<iostream>
#include<deque>
 
using namespace std;
int main()
{
	deque<int> d;
	d.push_back( 11 );//在 deque 容器的末尾添加一个新元素
	d.push_back(20);
	d.push_back(35);
	cout<<"初始化双端队列d:"<<endl;
	for(int i = 0; i < d.size(); i++)
	{
		cout<<d.at(i)<<"t";
	}
	cout<<endl;

	d.push_front(10);//容器的开始位置插入一个新的元素,位于当前的第一个元素之前
	d.push_front(7);
	d.push_front(1);
 
	cout<<"队列d向前陆续插入10、7、1:"<<endl;
	for(int i = 0;i < d.size();i++)
	{
		cout<<d.at(i)<<"t";
	}
	cout<<endl;

	d.pop_back(); //删除 deque 容器中的最后一个元素,有效地将容器大小减少一个
	d.pop_front(); //删除 deque 容器中的第一个元素,有效地减小其大小
	cout<<"删除deque最后一个和第一个元素后:"<<endl;
	for(int i = 0;i < d.size();i++)
	{
		cout<<d.at(i)<<"t";
	}
	cout<<endl;
	return 0;
}

forward_list

在头文件<forward_list>中,与list类似,区别就是list时双链表,forward_list是单链表,forward_list(单向链表)是序列容器,允许在序列中的任何地方进行恒定的时间插入和擦除操作。在链表的任何位置进行插入/删除操作都非常快。

STL总结与常见面试题

forward_list的特点

  • forward_list只提供钱箱迭代器,因此不支持反向迭代器,比如rbegin()等成员函数。
  • forward_list不提供size()成员函数。
  • forward_list没有指向最末元素的锚点,因此不提供back()、push_back()和pop_back()。
  • forward_list不提供随机访问,这一点跟list相同。
  • 插入和删除元素不会造成“指向至其他元素”的指针,引用和迭代器失效。

容器成员函数总结就不写了,太多影响阅读,感兴趣小伙伴戳http://www.cplusplus.com/reference/stl/

list

STL总结与常见面试题

list双向链表,是序列容器,允许在序列中的任何地方进行常数时间插入和擦除操作,并在两个方向上进行迭代,可以高效地进行插入删除元素。

使用list容器之前必须加上头文件:#include;

list容器的底层实现:

和 array、vector 这些容器迭代器的实现方式不同,由于 list 容器的元素并不是连续存储的,所以该容器迭代器中,必须包含一个可以指向 list 容器的指针,并且该指针还可以借助重载的 *、++、–、==、!= 等运算符,实现迭代器正确的递增、递减、取值等操作。

template<tyepname T,...>
struct __list_iterator{
    __list_node<T>* node;
    //...
    //重载 == 运算符
    bool operator==(const __list_iterator& x){return node == x.node;}
    //重载 != 运算符
    bool operator!=(const __list_iterator& x){return node != x.node;}
    //重载 * 运算符,返回引用类型
    T* operator *() const {return *(node).myval;}
    //重载前置 ++ 运算符
    __list_iterator<T>& operator ++(){
        node = (*node).next;
        return *this;
    }
    //重载后置 ++ 运算符
    __list_iterator<T>& operator ++(int){
        __list_iterator<T> tmp = *this;
        ++(*this);
        return tmp;
    }
    //重载前置 -- 运算符
    __list_iterator<T>& operator--(){
        node = (*node).prev;
        return *this;
    }
    //重载后置 -- 运算符
    __list_iterator<T> operator--(int){
        __list_iterator<T> tmp = *this;
        --(*this);
        return tmp;
    }
    //...
}

stack

stack没有迭代器,是一种容器适配器,用于在LIFO(后进先出)的操作,其中元素仅从容器的一端插入和提取。

STL总结与常见面试题

stack底层一般用list或deque实现,封闭头部即可,不用vector的原因应该是容量大小有限制,扩容耗时

底层用deque实现

//deque<T> >中间有个空格是为了兼容较老的版本
template <class T, class Sequence = deque<T> >
class stack {
    // 以㆘的 __STL_NULL_TMPL_ARGS 会开展为 <>
    friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&);
    friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const stack&);
public:
    typedef typename Sequence::value_type value_type;
    typedef typename Sequence::size_type size_type;
    typedef typename Sequence::reference reference;
    typedef typename Sequence::const_reference const_reference;
protected:
    Sequence c; // 底层容器
public:
    // 以㆘完全利用 Sequence c 的操作,完成 stack 的操作。
    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }
    reference top() { return c.back(); }
    const_reference top() const { return c.back(); }
    // deque 是两头可进出,stack 是末端进,末端出(所以后进者先出)。
    void push(const value_type& x) { c.push_back(x); }
    void pop() { c.pop_back(); }
};

template <class T, class Sequence>
bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y) {
    return x.c == y.c;
}

template <class T, class Sequence>
bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y) {
    return x.c < y.c;
}

底层用list实现

  #include<stack>  
  #include<list>  
  #include<algorithm>  
  #include <iostream>  
  using namespace std;  
    
  int main(){  
      stack<int, list<int>> istack;  
      istack.push(1);  
      istack.push(3);  
      istack.push(5);  
        
      cout << istack.size() << endl; //3  
      cout << istack.top() << endl;//5  
      istack.pop();  
      cout << istack.top() << endl;//3  
      cout << istack.size() << endl;//2  
    
      system("pause");  
      return 0;  
  }  

queue

queue 是一种容器适配器,用于在FIFO(先入先出)的操作,其中元素插入到容器的一端并从另一端提取。

STL总结与常见面试题

队列不提供迭代器,不实现遍历操作。

template <class T, class Sequence = deque<T> >
class queue {
  friend bool operator== __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
  friend bool operator< __STL_NULL_TMPL_ARGS (const queue& x, const queue& y);
public:
  typedef typename Sequence::value_type value_type;
  typedef typename Sequence::size_type size_type;
  typedef typename Sequence::reference reference;
  typedef typename Sequence::const_reference const_reference;
protected:
  Sequence c;
public:
  bool empty() const { return c.empty(); }
  size_type size() const { return c.size(); }
  reference front() { return c.front(); }
  const_reference front() const { return c.front(); }
  reference back() { return c.back(); }
  const_reference back() const { return c.back(); }
  void push(const value_type& x) { c.push_back(x); }
  void pop() { c.pop_front(); }
};

template <class T, class Sequence>
bool operator==(const queue<T, Sequence>& x, const queue<T, Sequence>& y) {
  return x.c == y.c;
}

template <class T, class Sequence>
bool operator<(const queue<T, Sequence>& x, const queue<T, Sequence>& y) {
  return x.c < y.c;
}

priority_queue

优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。

set

set 是按照特定顺序存储唯一元素的容器。

template<class _Kty,
    class _Pr = less<_Kty>,
    class _Alloc = allocator<_Kty> >
class set
  1. set 的 底层数据结构是 红黑树,一种高效的平衡检索二叉树。
  2. set 容器中 每一个元素就是二叉树的每一个节点,对于set容器的插入删除操作,效率都比较高,原因是因为二叉树的删除插入元素并不需要进行内存拷贝和内存移动,只是改变了指针的指向。
  3. 对 set 进行插入删除操作 都不会引起iterator的失效,因为迭代器相当于一个指针指向每一个二叉树的节点,对set的插入删除并不会改变原有内存中节点的改变, 但是vector的插入删除操作一般会发生内存移动和内存拷贝,所以会发生迭代器的失效。
  4. set容器的检索速度很快,因为采用二分查找的方法 。

multiset

multiset允许元素重复而set不允许

template<class _Kty,
	class _Pr = less<_Kty>,
	class _Alloc = allocator<_Kty> >
class multiset

map

map 是关联容器,按照特定顺序存储由 key value (键值) 和 mapped value (映射值) 组合形成的元素。

由于 RB-tree 是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL map 即以 RB-tree 为底层机制。又由于 map 所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 操作行为,都只是转调 RB-tree 的操作行为。

方法 说明
map 构造函数
begin 返回引用容器中第一个元素的迭代器
key_comp 返回容器用于比较键的比较对象的副本
value_comp 返回可用于比较两个元素的比较对象,以获取第一个元素的键是否在第二个元素之前
find 在容器中搜索具有等于 k的键的元素,如果找到返回一个迭代器,否则返回 map::end
count 在容器中搜索具有等于 k(参数)的键的元素,并返回匹配的数量
lower_bound 返回一个非递减序列 [first, last)中的第一个大于等于值 val的位置的迭代器
upper_bound 返回一个非递减序列 [first, last)中第一个大于 val的位置的迭代器
equal_range 获取相同元素的范围,返回包含容器中所有具有与 k等价的键的元素的范围边界

multimap

multimap 的特性以及用法与 map 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制 RB-tree 的 insert_equal() 而非 insert_unique。

unordered_set

unordered_set是基于哈希表,因此要了解unordered_set,就必须了解哈希表的机制。哈希表是根据关键码值而进行直接访问的数据结构,通过相应的哈希函数(也称散列函数)处理关键字得到相应的关键码值,关键码值对应着一个特定位置,用该位置来存取相应的信息,这样就能以较快的速度获取关键字的信息。

template < class Key,  
    class Hash = hash<Key>,  
    class Pred = equal_to<Key>,  
    class Alloc = allocator<Key>  
> class unordered_set;  

4 算法

  1. 简单查找算法,要求输入迭代器(input iterator)
find(beg, end, val); // 返回一个迭代器,指向输入序列中第一个等于 val 的元素,未找到返回 end
find_if(beg, end, unaryPred); // 返回一个迭代器,指向第一个满足 unaryPred 的元素,未找到返回 end
find_if_not(beg, end, unaryPred); // 返回一个迭代器,指向第一个令 unaryPred 为 false 的元素,未找到返回 end
count(beg, end, val); // 返回一个计数器,指出 val 出现了多少次
count_if(beg, end, unaryPred); // 统计有多少个元素满足 unaryPred
all_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都满足 unaryPred
any_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否任意(存在)一个元素满足 unaryPred
none_of(beg, end, unaryPred); // 返回一个 bool 值,判断是否所有元素都不满足 unaryPred
  1. 查找重复值的算法,传入向前迭代器(forward iterator)
adjacent_find(beg, end); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end
adjacent_find(beg, end, binaryPred); // 返回指向第一对相邻重复元素的迭代器,无相邻元素则返回 end
search_n(beg, end, count, val); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end
search_n(beg, end, count, val, binaryPred); // 返回一个迭代器,从此位置开始有 count 个相等元素,不存在则返回 end
  1. 查找子序列算法,除 find_first_of(前两个输入迭代器,后两个前向迭代器) 外,都要求两个前向迭代器
search(beg1, end1, beg2, end2); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1
search(beg1, end1, beg2, end2, binaryPred); // 返回第二个输入范围(子序列)在爹一个输入范围中第一次出现的位置,未找到则返回 end1
find_first_of(beg1, end1, beg2, end2); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1
find_first_of(beg1, end1, beg2, end2, binaryPred); // 返回一个迭代器,指向第二个输入范围中任意元素在第一个范围中首次出现的位置,未找到则返回end1
find_end(beg1, end1, beg2, end2); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1
find_end(beg1, end1, beg2, end2, binaryPred); // 类似 search,但返回的最后一次出现的位置。如果第二个输入范围为空,或者在第一个输入范围为空,或者在第一个输入范围中未找到它,则返回 end1
  1. 其他只读算法,传入输入迭代器
for_each(beg, end, unaryOp); // 对输入序列中的每个元素应用可调用对象 unaryOp,unaryOp 的返回值被忽略
mismatch(beg1, end1, beg2); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素
mismatch(beg1, end1, beg2, binaryPred); // 比较两个序列中的元素。返回一个迭代器的 pair,表示两个序列中第一个不匹配的元素
equal(beg1, end1, beg2); // 比较每个元素,确定两个序列是否相等。
equal(beg1, end1, beg2, binaryPred); // 比较每个元素,确定两个序列是否相等。
  1. 二分搜索算法,传入前向迭代器或随机访问迭代器(random-access iterator),要求序列中的元素已经是有序的
lower_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end
lower_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中的第一个大于等于值 val 的位置的迭代器,不存在则返回 end
upper_bound(beg, end, val); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end
upper_bound(beg, end, val, comp); // 返回一个非递减序列 [beg, end) 中第一个大于 val 的位置的迭代器,不存在则返回 end
equal_range(beg, end, val); // 返回一个 pair,其 first 成员是 lower_bound 返回的迭代器,其 second 成员是 upper_bound 返回的迭代器
binary_search(beg, end, val); // 返回一个 bool 值,指出序列中是否包含等于 val 的元素。对于两个值 x 和 y,当 x 不小于 y 且 y 也不小于 x 时,认为它们相等。
  1. 只写不读算法,要求输出迭代器(output iterator)
fill(beg, end, val); // 将 val 赋予每个元素,返回 void
fill_n(beg, cnt, val); // 将 val 赋予 cnt 个元素,返回指向写入到输出序列最有一个元素之后位置的迭代器
genetate(beg, end, Gen); // 每次调用 Gen() 生成不同的值赋予每个序列,返回 void
genetate_n(beg, cnt, Gen); // 每次调用 Gen() 生成不同的值赋予 cnt 个序列,返回指向写入到输出序列最有一个元素之后位置的迭代器

7.使用输入迭代器的写算法,读取一个输入序列,将值写入到一个输出序列(dest)中

copy(beg, end, dest); // 从输入范围将元素拷贝所有元素到 dest 指定定的目的序列
copy_if(beg, end, dest, unaryPred); // 从输入范围将元素拷贝满足 unaryPred 的元素到 dest 指定定的目的序列
copy_n(beg, n, dest); // 从输入范围将元素拷贝前 n 个元素到 dest 指定定的目的序列
move(beg, end, dest); // 对输入序列中的每个元素调用 std::move,将其移动到迭代器 dest 开始始的序列中
transform(beg, end, dest, unaryOp); // 调用给定操作(一元操作),并将结果写到dest中
transform(beg, end, beg2, dest, binaryOp); // 调用给定操作(二元操作),并将结果写到dest中
replace_copy(beg, end, dest, old_val, new_val); // 将每个元素拷贝到 dest,将等于 old_val 的的元素替换为 new_val
replace_copy_if(beg, end, dest, unaryPred, new_val); // 将每个元素拷贝到 dest,将满足 unaryPred 的的元素替换为 new_val
merge(beg1, end1, beg2, end2, dest); // 两个输入序列必须都是有序的,用小于号运算符将合并后的序列写入到 dest 中
merge(beg1, end1, beg2, end2, dest, comp); // 两个输入序列必须都是有序的,使用给定的比较操作(comp)将合并后的序列写入到 dest 中

8.划分算法,要求双向选代器(bidirectional iterator)

is_partitioned(beg, end, unaryPred); // 如果所有满足谓词 unaryPred 的元素都在不满足 unarypred 的元素之前,则返回 true。若序列为空,也返回 true
partition_copy(beg, end, dest1, dest2, unaryPred); // 将满足 unaryPred 的元素拷贝到到 dest1,并将不满足 unaryPred 的元素拷贝到到 dest2。返回一个迭代器 pair,其 first 成员表示拷贝到 dest1 的的元素的末尾,second 表示拷贝到 dest2 的元素的末尾。
partitioned_point(beg, end, unaryPred); // 输入序列必须是已经用 unaryPred 划分过的。返回满足  unaryPred 的范围的尾后迭代器。如果返回的迭代器不是 end,则它指向的元素及其后的元素必须都不满足 unaryPred
stable_partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg
partition(beg, end, unaryPred); // 使用 unaryPred 划分输入序列。满足 unaryPred 的元素放置在序列开始,不满足的元素放在序列尾部。返回一个迭代器,指向最后一个满足 unaryPred 的元素之后的位置如果所有元素都不满足 unaryPred,则返回 beg
  1. 排序算法,要求随机访问迭代器(random-access iterator)
sort(beg, end); // 排序整个范围
stable_sort(beg, end); // 排序整个范围(稳定排序)
sort(beg, end, comp); // 排序整个范围
stable_sort(beg, end, comp); // 排序整个范围(稳定排序)
is_sorted(beg, end); // 返回一个 bool 值,指出整个输入序列是否有序
is_sorted(beg, end, comp); // 返回一个 bool 值,指出整个输入序列是否有序
is_sorted_until(beg, end); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器
is_sorted_until(beg, end, comp); // 在输入序列中査找最长初始有序子序列,并返回子序列的尾后迭代器
partial_sort(beg, mid, end); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置
partial_sort(beg, mid, end, comp); // 排序 mid-beg 个元素。即,如果 mid-beg 等于 42,则此函数将值最小的 42 个元素有序放在序列前 42 个位置
partial_sort_copy(beg, end, destBeg, destEnd); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中
partial_sort_copy(beg, end, destBeg, destEnd, comp); // 排序输入范围中的元素,并将足够多的已排序元素放到 destBeg 和 destEnd 所指示的序列中
nth_element(beg, nth, end); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它
nth_element(beg, nth, end, comp); // nth 是一个迭代器,指向输入序列中第 n 大的元素。nth 之前的元素都小于等于它,而之后的元素都大于等于它
  1. 使用前向迭代器的重排算法。普通版本在输入序列自身内部重拍元素,_copy 版本完成重拍后写入到指定目的序列中,而不改变输入序列
remove(beg, end, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_if(beg, end, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_copy(beg, end, dest, val); // 通过用保留的元素覆盖要删除的元素实现删除 ==val 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
remove_copy_if(beg, end, dest, unaryPred); // 通过用保留的元素覆盖要删除的元素实现删除满足 unaryPred 的元素,返回一个指向最后一个删除元素的尾后位置的迭代器
unique(beg, end); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique (beg, end, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique_copy(beg, end, dest); // 通过对覆盖相邻的重复元素(用 == 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
unique_copy_if(beg, end, dest, binaryPred); // 通过对覆盖相邻的重复元素(用 binaryPred 确定是否相同)实现重排序列。返回一个迭代器,指向不重复元素的尾后位置
rotate(beg, mid, end); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素
rotate_copy(beg, mid, end, dest); // 围绕 mid 指向的元素进行元素转动。元素 mid 成为为首元素,随后是 mid+1 到到 end 之前的元素,再接着是 beg 到 mid 之前的元素。返回一个迭代器,指向原来在 beg 位置的元素
  1. 使用双向迭代器的重排算法
reverse(beg, end); // 翻转序列中的元素,返回 void
reverse_copy(beg, end, dest);; // 翻转序列中的元素,返回一个迭代器,指向拷贝到目的序列的元素的尾后位置
  1. 使用随机访问迭代器的重排算法
random_shuffle(beg, end); // 混洗输入序列中的元素,返回 void
random_shuffle(beg, end, rand); // 混洗输入序列中的元素,rand 接受一个正整数的随机对象,返回 void
shuffle(beg, end, Uniform_rand); // 混洗输入序列中的元素,Uniform_rand 必须满足均匀分布随机数生成器的要求,返回 void
  1. 最小值和最大值
min(val1, va12); // 返回 val1 和 val2 中的最小值,两个实参的类型必须完全一致。参数和返回类型都是 const的引引用,意味着对象不会被拷贝。下略
min(val1, val2, comp);
min(init_list);
min(init_list, comp);
max(val1, val2);
max(val1, val2, comp);
max(init_list);
max(init_list, comp);
minmax(val1, val2); // 返回一个 pair,其 first 成员为提供的值中的较小者,second 成员为较大者。下略
minmax(vall, val2, comp);
minmax(init_list);
minmax(init_list, comp);
min_element(beg, end); // 返回指向输入序列中最小元素的迭代器
min_element(beg, end, comp); // 返回指向输入序列中最小元素的迭代器
max_element(beg, end); // 返回指向输入序列中最大元素的迭代器
max_element(beg, end, comp); // 返回指向输入序列中最大元素的迭代器
minmax_element(beg, end); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素
minmax_element(beg, end, comp); // 返回一个 pair,其中 first 成员为最小元素,second 成员为最大元素
  1. 字典序比较,根据第一对不相等的元素的相对大小来返回结果。如果第一个序列在字典序中小于第二个序列,则返回 true。否则,返回 fa1se。如果个序列比另一个短,且所有元素都与较长序列的对应元素相等,则较短序列在字典序中更小。如果序列长度相等,且对应元素都相等,则在字典序中任何一个都不大于另外一个。
lexicographical_compare(beg1, end1, beg2, end2);
lexicographical_compare(beg1, end1, beg2, end2, comp);

5 如何选择合适的容器

需要根据容器的特点和使用场景而定,可能满足需求的不止一种容器。

按是否有序关联性分为:

  1. 序列式容器:array、vector、deque、list 和 forward_list;
  2. 关联式容器:map、multimap、set 和 multiset;
  3. 无序关联式容器:unordered_map、unordered_multimap、unordered_set 和 unordered_multiset;
  4. 容器适配器:stack、queue 和 priority_queue。 根据容器底层采用是否是连续的存储空间分为:
  5. 采用连续的存储空间:array、vector、deque;
  6. 采用分散的存储空间:list、forward_list 以及所有的关联式容器和哈希容器。

注意:deque 容器归为使用连续存储空间的这一类,是存在争议的。因为 deque 容器底层采用一段一段的连续空间存储元素,但是各段存储空间之间并不一定是紧挨着的。

STL总结与常见面试题
选择容器流程图(来源于网络)

选择容器的几点建议:

  • 如果只是存储确定或不确定的对象,而不去删除它们,可以选用vector。就是因为vector是数组的替代品,是连续内存的,不适合频繁的删除。
  • 如果在容器的指定位置插入新元素,则只能选择序列式容器,不选择关联式容器和哈希容器。
  • 如果频繁的插入和删除,可以选用list(链表),内存不是连续的,可以方便的插入和删除,但是不支持索引访问。
  • 数据量很大,不在乎他们的排序,要求效率,对容器中各元素的存储位置没有要求,可以考虑使用哈希容器,反之就要避免使用哈希容器。
  • 如果是随机访问迭代器,选择 array、vector、deque。
  • 如果是双向迭代器,考虑 list 序列式容器以及所有的关联式容器。
  • 如果必须是前向迭代器,考虑 forward_list序列式容器以及所有的哈希容器。
  • 如果插入或删除操作时,容器中的其它元素不移动?选择不是array、vector、deque的其它容器。

6 面试中常出现的STL问题

  1. vector的底层原理

vector底层是一个动态数组,包含三个迭代器,start和finish之间是已经被使用的空间范围,end_of_storage是整块连续空间包括备用空间的尾部。

当空间不够装下数据(vec.push_back(val))时,会自动申请另一片更大的空间(1.5倍或者2倍),然后把原来的数据拷贝到新的内存空间,接着释放原来的那片空间【vector内存增长机制】。

当释放或者删除(vec.clear())里面的数据时,其存储空间不释放,仅仅是清空了里面的数据。

因此,对vector的任何操作一旦引起了空间的重新配置,指向原vector的所有迭代器会都失效了

STL总结与常见面试题
  1. vector中的reserve和resize的区别
  • reserve是直接扩充到已经确定的大小,可以减少多次开辟、释放空间的问题(优化push_back),就可以提高效率,其次还可以减少多次要拷贝数据的问题。reserve只是保证vector中的空间大小(capacity)最少达到参数所指定的大小n。reserve()只有一个参数。
  • resize()可以改变有效空间的大小,也有改变默认值的功能。capacity的大小也会随着改变。resize()可以有多个参数。
  1. vector中的size和capacity的区别
  • size表示当前vector中有多少个元素(finish – start);
  • capacity函数则表示它已经分配的内存中可以容纳多少元素(end_of_storage – start);
  1. vector中erase方法与algorithn中的remove方法区别
  • vector中erase方法真正删除了元素,迭代器不能访问了
  • remove只是简单地将元素移到了容器的最后面,迭代器还是可以访问到。因为algorithm通过迭代器进行操作,不知道容器的内部结构,所以无法进行真正的删除。
  1. vector迭代器失效的情况
  • 当插入一个元素到vector中,由于引起了内存重新分配,所以指向原内存的迭代器全部失效。
  • 当删除容器中一个元素后,该迭代器所指向的元素已经被删除,那么也造成迭代器失效。erase方法会返回下一个有效的迭代器,所以当我们要删除某个元素时,需要it=vec.erase(it);。
  1. 正确释放vector的内存(clear(), swap(), shrink_to_fit())
  • vec.clear():清空内容,但是不释放内存。
  • vector<int>().swap(vec):清空内容,且释放内存,想得到一个全新的vector。
  • vec.shrink_to_fit():请求容器降低其capacity和size匹配。
  • vec.clear();vec.shrink_to_fit();:清空内容,且释放内存。
  1. list的底层原理
STL总结与常见面试题
  • ist的底层是一个双向链表,使用链表存储数据,并不会将它们存储到一整块连续的内存空间中。恰恰相反,各元素占用的存储空间(又称为节点)是独立的、分散的,它们之间的线性关系通过指针来维持,每次插入或删除一个元素,就配置或释放一个元素空间。
  • list不支持随机存取,如果需要大量的插入和删除,而不关心随即存取
  1. 什么情况下用vector,什么情况下用list,什么情况下用deque
  • vector可以随机存储元素(即可以通过公式直接计算出元素地址,而不需要挨个查找),但在非尾部插入删除数据时,效率很低,适合对象简单,对象数量变化不大,随机访问频繁。除非必要,我们尽可能选择使用vector而非deque,因为deque的迭代器比vector迭代器复杂很多。
  • list不支持随机存储,适用于对象大,对象数量变化频繁,插入和删除频繁,比如写多读少的场景。
  • 需要从首尾两端进行插入或删除操作的时候需要选择deque。
  1. priority_queue的底层原理
  • priority_queue:优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。
  1. map 、set、multiset、multimap的底层原理

map 、set、multiset、multimap的底层实现都是红黑树,epoll模型的底层数据结构也是红黑树,linux系统中CFS进程调度算法,也用到红黑树。

STL总结与常见面试题

红黑树的特性:

  • 每个结点或是红色或是黑色;
  • 根结点是黑色;
  • 每个叶结点是黑的;
  • 如果一个结点是红的,则它的两个儿子均是黑色;
  • 每个结点到其子孙结点的所有路径上包含相同数目的黑色结点。
  1. 为何map和set的插入删除效率比其他序列容器高
  • 因为不需要内存拷贝和内存移动
  1. 为何map和set每次Insert之后,以前保存的iterator不会失效?
  • 因为插入操作只是结点指针换来换去,结点内存没有改变。而iterator就像指向结点的指针,内存没变,指向内存的指针也不会变。
  1. 当数据元素增多时(从10000到20000),map的set的查找速度会怎样变化?
  • RB-TREE用二分查找法,时间复杂度为logn,所以从10000增到20000时,查找次数从log10000=14次到log20000=15次,多了1次而已。
  1. map 、set、multiset、multimap的特点
  • set和multiset会根据特定的排序准则自动将元素排序,set中元素不允许重复,multiset可以重复。
  • map和multimap将key和value组成的pair作为元素,根据key的排序准则自动将元素排序(因为红黑树也是二叉搜索树,所以map默认是按key排序的),map中元素的key不允许重复,multimap可以重复。
  • map和set的增删改查速度为都是logn,是比较高效的。
  1. 为何map和set的插入删除效率比其他序列容器高,而且每次insert之后,以前保存的iterator不会失效?
  • 存储的是结点,不需要内存拷贝和内存移动。
  • 插入操作只是结点指针换来换去,结点内存没有改变。而iterator就像指向结点的指针,内存没变,指向内存的指针也不会变。
  1. 为何map和set不能像vector一样有个reserve函数来预分配数据?
  • 在map和set内部存储的已经不是元素本身了,而是包含元素的结点。也就是说map内部使用的Alloc并不是map<Key, Data, Compare, Alloc>声明的时候从参数中传入的Alloc。
  1. set的底层实现实现为什么不用哈希表而使用红黑树?
  • set中元素是经过排序的,红黑树也是有序的,哈希是无序的
  • 如果只是单纯的查找元素的话,那么肯定要选哈希表了,因为哈希表在的最好查找时间复杂度为O(1),并且如果用到set中那么查找时间复杂度的一直是O(1),因为set中是不允许有元素重复的。而红黑树的查找时间复杂度为O(lgn)
  1. hash_map与map的区别?什么时候用hash_map,什么时候用map? 构造函数:hash_map需要hash function和等于函数,而map需要比较函数(大于或小于)。

存储结构:hash_map以hashtable为底层,而map以RB-TREE为底层。

总的说来,hash_map查找速度比map快,而且查找速度基本和数据量大小无关,属于常数级别。而map的查找速度是logn级别。但不一定常数就比log小,而且hash_map还有hash function耗时。

如果考虑效率,特别当元素达到一定数量级时,用hash_map。

考虑内存,或者元素数量较少时,用map。

  1. 迭代器失效的问题

插入操作:

  • 对于vector和string,如果容器内存被重新分配,iterators,pointers,references失效;如果没有重新分配,那么插入点之前的iterator有效,插入点之后的iterator失效;
  • 对于deque,如果插入点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,deque的迭代器失效,但reference和pointers有效;
  • 对于list和forward_list,所有的iterator,pointer和refercnce有效。

删除操作:

  • 对于vector和string,删除点之前的iterators,pointers,references有效;off-the-end迭代器总是失效的;
  • 对于deque,如果删除点位于除front和back的其它位置,iterators,pointers,references失效;当我们插入元素到front和back时,off-the-end失效,其他的iterators,pointers,references有效;
  • 对于list和forward_list,所有的iterator,pointer和refercnce有效。
  • 对于关联容器map来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用,否则会导致程序无定义的行为。
  1. 线程不安全的情况
  • 在对同一个容器进行多线程的读写、写操作时;
  • 在每次调用容器的成员函数期间都要锁定该容器;
  • 在每个容器返回的迭代器(例如通过调用begin或end)的生存期之内都要锁定该容器;
  • 在每个在容器上调用的算法执行期间锁定该容器。

参考资料

http://www.cplusplus.com/reference/stl/ https://blog.csdn.net/qq_23350817/article/details/87930715 https://blog.csdn.net/bizhu12/article/details/6769976             http://c.biancheng.net/view/7385.html
https://blog.csdn.net/daaikuaichuan/article/details/80717222

转自:https://mp.weixin.qq.com/s/JWAestcpQTlp2fb1Q45AQg

37 个 Python Web 开发框架总结

来自公众号:早起Python
大家好,我是早起。

用了 2 周的时间整理了 Python 中所有的网站开发库(下文简称:Web 框架),供大家学习参考。

最全整理!37 个 Python Web 开发框架总结
Q:Web 框架到底是什么?
A:Web 框架主要用于网站开发。开发者在基于 Web 框架实现自己的业务逻辑。Web 框架实现了很多功能,为实现业务逻辑提供了一套通用方法。
Q:Web 框架有什么作用?
A:使用 Web 框架,很多的业务逻辑外的功能不需要自己再去完善,而是使用框架已有的功能就可以。Web 框架使得在进行 网站开发的时候,减少了工作量。
Q:我们为什么要使用 Web 框架?
A:Web 框架主要用于网站开发,可以实现数据的交互和业务功能的完善。使用 Web 框架进行 网站开发的时候,在进行数据缓存、数据库访问、数据安全校验等方面,不需要自己再重新实现,而是将业务逻辑相关的代码写入框架就可以。
库排序是按照使用人数和文档的活跃度为参考进行的,建议大家使用排名靠前的框架,因为它们的文档更齐全,技术积累要更多,社区更繁盛,能得到更好的支持,这样在遇到自己无法解决的问题,可以更快更高效的找到解答的方案。

1. Django 框架

官 网:

https://github.com/django/django.git

 

优 点:是一个高层次 Python Web 开发框架,特点是开发快速、代码较少、可扩展性强。Django 采用 MTV(Model、Template、View)模型组织资源,框架功能丰富,模板扩展选择最多。对于专业人员来说,Django 是当之无愧的 Python 排名第一的 Web 开发框架。

缺 点:包括一些轻量级应用不需要的功能模块,不如 Flask 轻便。过度封装很多类和方法,直接使用比较简单,但改动起来比较困难。相比于 C,C++ 性能,Djang 性能偏低。模板实现了代码和样式完全分离,不允许模板里出现 Python 代码,灵活度不够。另外学习曲线也相对陡峭。

最全整理!37 个 Python Web 开发框架总结

2. Flask 框架
官 网:
https://flask.palletsprojects.com/en/2.0.x/
 
优 点:Flask 是一个 Python Web 开发的微框架,严格来说,它仅提供 Web 服务器支持,不提供全栈开发支持。然而,Flask 非常轻量、非常简单,基于它搭建 Web 系统都以分钟来计时,特别适合小微原型系统的开发。花少时间、产生可用系统,是非常划算的选择。
缺 点:对于大型网站开发,需要设计路由映射的规则,否则导致代码混乱。对新手来说,容易使用低质量的代码创建 “不良的 web 应用程序”。
最全整理!37 个 Python Web 开发框架总结
3. Pyramid 框架
官 网: 
https://trypyramid.com/
优 点:是一个扩展性很强且灵活的 Python Web 开发框架。上手十分容易,比较适合中等规模且边开发边设计的场景。Pyramid 不提供绝对严格的框架定义,根据需求可以扩展开发,对高阶程序员十分友好。
缺 点:国内知名度不高,高级用法需要通过阅读源代码获取灵感。默认使用 Chameleon 模板,灵活度没有成为一个要素。
最全整理!37 个 Python Web 开发框架总结
4. web.py 框架

官 网:

https://webpy.org/

优 点:正如其名,web.py 是一个采用 Python 作为开发语言的 Web 框架,简单且强大。俄罗斯排名第一的 Yandex 搜索引擎基于这个框架开发,Guido van Rossum 认为这是最好的 Python Web 框架,还需要说别的吗?有事实作证、有大牛认可,用起来吧!

缺 点:Web.py 并未像其他框架一样保持与 Python 3 兼容性的最新状态。这不仅意味着缺乏对异步语法的支持,还意味着缺少对已弃用的函数的错误。此外,目前尚不清楚维护者是否有计划在 Python 2 到达其支持生命周期结束后保持 Web.py 的最新状态。

最全整理!37 个 Python Web 开发框架总结
5. Tornado 框架

官 网:

https://www.tornadoweb.org/en/stable/
优 点:Tornado 是一个基于异步网络功能库的 Web 开发框架,因此,它能支持几万个开放连接,Web 服务高效稳定。可见,Tornado 适合高并发场景下的 Web 系统,开发过程需要采用 Tornado 提供的框架,灵活性较差,确定场景后再考虑使用不迟。
缺 点:Tornado 5.0 改进了与 Python 的本机异步功能的集成。因此不再支持 Python 3.3,并且 Python 3.5 用户必须使用 Python 3.5.2 或更高版本。Tornado 6.0 将需要 Python 3.5 及更高版本,并将完全放弃 Python 2 支持。
最全整理!37 个 Python Web 开发框架总结

6. TurboGears 框架

官 网:
https://www.turbogears.org/
优 点:TurboGears 最新版本 试图解决 TurboGears 最初版本、Django、Rails 等框架缺陷,它支持多数据库、JavaScript 工具集、多种数据交换格式等,当然,它开发非常高效,扩展性很强。试问它与 Django 哪个更好?还真不好说,Django 社区更为活跃或许更重要。
缺 点:TurboGears 的一些缺点包括管理功能较为基础,较少的第三方应用程序以及只有初级的文档。TurboGears 的一个重大挑战是它的压倒性。因为它混搭的其他组件都提供无限的灵活性, 当试图决定何种路径来完成一个任务时它是过于复杂的。
最全整理!37 个 Python Web 开发框架总结
7. CherryPy 框架
官 网:
https://cherrypy.org/
优 点:CherryPy 能够让开发者按照其他面向对象程序相似的设计方法开发 Web 系统,进而采用最少的代码、最简洁的方式。CherryPy 已经开发了 10 年之久,稳定性较好,非常适合小规模 Web 系统和纯粹的 Python 程序员。感受到了什么?Python 大有一统江湖之势!拭目以待。
缺 点:只包含为 web 页面提供服务所需的少量内容外,它应该尽可能地让人感觉它不像 “web 框架”,而是像任何其他类型的 Python 应用程序一样。CherryPy 包含的位用作低级构建块。包括会话标识符和 cookie 处理,但不包括 HTML 模板。像 Bottle 一样,CherryPy 提供了一种将路由映射到磁盘上的目录以供静态文件服务的方法。
最全整理!37 个 Python Web 开发框架总结

8. Falcon 框架

官 网:
http://falconframework.org/
优 点:Falcon 是一个支持大规模微服务 API 或移动 App 后端响应的 Web 开发框架,它完全基于 Python 并提供了非常高的性能、可靠性和可扩展性。Falcon 定位独特且特色鲜明,对于 App 开发者,后端系统构建不妨考虑 Falcon,十之八九是不会后悔的。
缺 点:Falcon 缺点是其打包模块有点太少,有路由,中间件,钩子,除此之外就不提供其他功能了(裸壳)。额外其他功能,比如验证等都需要开发人员来开发扩展。因为其设计中就假设用于构建 REST API。
最全整理!37 个 Python Web 开发框架总结
9. Asgineer 框架
官 网:
https://asgineer.readthedocs.io/
优 点:Asgineer 是一种编写异步 Web 应用程序的工具,使用尽可能少的抽象,同时仍然提供友好的 API。只需几分钟即可阅读指南和参考资料!在 Uvicorn 上运行 Asgineer 时,它是可用的最快的 Web 框架之一。它支持 http 长轮询、服务器端事件 (SSE) 和 websockets。并且具有以正确(且快速)的方式为资产提供服务的实用程序。
缺 点:中文文档匮乏。
10. Bottle 框架
官 网:
https://bottlepy.org/docs/dev/
优 点:Bottle 是一个用于 Python 的快速、简单和轻量级的 WSGI 微型网络框架。它作为单个文件模块分发,除了 Python 标准库之外没有任何依赖项。支持干净和动态的 URL。快速和 Pythonic 内置模板引擎,支持 mako、jinja2 和 cheetah 模板。方便地访问表单数据、文件上传、cookie、标题和其他与 HTTP 相关的元数据。
缺 点:Bottle 极简主义的一个后果是有些功能根本就不存在。不支持表单验证,包括 CSRF 保护等功能。如果要构建支持高度用户交互的 Web 应用程序,则需要自己添加它们。
最全整理!37 个 Python Web 开发框架总结

11. Emmett 框架

官 网:
https://emmett.sh/
优 点:编写优雅的代码,专注于您的产品,你应该把更多的时间花在你的产品而不是底层框架上。Emmett 是发明家的框架,因为它旨在简化您的开发过程,其语法简单、易于学习和理解。不要在路由器和不舒服的模式之间浪费时间:每次为应用程序写下路由时,一切都清楚地呈现在您的眼前。
缺 点:中文文档匮乏。
最全整理!37 个 Python Web 开发框架总结
12. apidaora 框架
官 网:
https://github.com/dutradda/apidaora
优 点:使用类型注释将请求 / 响应声明为数据类和字典,使用 jsondaora 验证输入数据,最快的 python api 框架之一,可以在任何 asgi 服务器上运行。
缺 点:中文文档匮乏。
最全整理!37 个 Python Web 开发框架总结
13. Quixote 框架
官 网:
http://www.quixote.ca/
优 点:Quixote 是一个使用 Python 编写基于 Web 的应用程序的框架。它的目标是按此顺序实现灵活性和高性能。Quixote 应用程序的结构往往类似于传统应用程序。格式化网页的逻辑由 Python 类和函数组成。Quixote 不强制分离表示逻辑和 “后端” 逻辑。相反,我们鼓励您使用传统技术。例如,一种解决方案是将表示逻辑放在其自己的子包中。
缺 点:虽然 Quixote 得到积极维护,但发布频率较低。最近开发的重点是修复错误、提高标准合规性和增加 Quixote 的灵活性。
最全整理!37 个 Python Web 开发框架总结
14. reahl 框架
官 网:
https://www.reahl.org/
优 点:reahl 是一个可以让开发者完全用 Python 语言编写 Web 应用的开放框架,采用 Python 作为唯一开发语言是其最大特色。此外,reahl 提供了相当清晰且简明的开发组建,对于开发一般功能性 Web 系统足够清晰且可行。
缺 点:提供抽象词汇时,无法掌握一些更高级的用户界面效果,目前不允许使用 Sass 构建 Bootstrap 的修改版本,并支持更高级的用户界面模式
最全整理!37 个 Python Web 开发框架总结
15. hug 框架
官 网:
https://hug.rest/
优 点:Hug 是一个 Python 3 API 开发框架。它的设计使软件工程师可以一次开发 API,然后在需要时再使用它。Hug 框架通过提供各种接口来简化 API 开发。这就是为什么该框架被称为 Python 3 中最快的框架之一的主要原因。
缺 点:它使用以前的 WSGI 标准,这是一个同步框架,所以它不能处理像 WebSockets 和其他异步请求,不管怎么说,它仍然有非常好的性能表现。
最全整理!37 个 Python Web 开发框架总结

16. BlackSheep 框架

官 网:
https://github.com/Neoteroi/BlackSheep
优 点:BlackSheep 是一个异步 Web 框架,用于使用 Python 构建基于事件的 Web 应用程序。它的灵感来自 Flask、ASP.NET Core 和 Yury Selivanov 的工作。
缺 点:中文文档匮乏。
最全整理!37 个 Python Web 开发框架总结
17. Index.py 框架
官 网:
https://index-py.aber.sh/stable/
优 点:实现了 ASGI3 接口并使用 Radix Tree 进行路由查找。是最快的 Python web 框架之一。一切特性都服务于快速开发高性能的 Web 服务。大量正确的类型注释,灵活且高效的路由系统,可视化 API 接口与在线调试,支持 Server-sent events 与 WebSocket,自带一键部署命令 (基于 uvicorn 与 gunicorn),可使用任何可用的 ASGI 生态。
缺 点:Index.py 不内置数据库、缓存等其他功能。选择困难者请直接使用 Django。
最全整理!37 个 Python Web 开发框架总结
18. Sanic 框架
官 网:
https://github.com/sanic-org/sanic
优 点:Sanic 是一个 Python 3.7+ web 服务器和 web 框架,它的编写速度很快。它允许使用 Python 3.5 中添加的 async/await 语法,这使您的代码无阻塞且快速。该项目的目标是提供一种简单的方法来启动和运行一个高性能的 HTTP 服务器,该服务器易于构建、扩展和最终扩展。
缺 点:就功能方面 Sanic 模仿 Flask,比如通过共享 Blueprints 的概念,微小的子应用程序,允许开发人员在更大的应用程序中拆分和组织其代码。对于光光是数据增删改查 CRUD 应用,Sanic 也不是一个好的选择。
最全整理!37 个 Python Web 开发框架总结
19. Starlette 框架
官 网:
https://pypi.org/project/starlette/
优 点:Starlette 是一个轻量级的 ASGI 框架 / 工具包,非常适合构建性能异步服务,它是生产就绪的,并为您提供以下内容:令人印象深刻的表现。WwbSocket 支持。GraphQL 支持。进程内后台任务。启动和关闭事件。基于请求构建的测试客户端。CORS、GZip、静态文件、流响应。会话和 Cookie 支持。100% 的测试覆盖率。100% 类型注释代码库。零硬依赖。
缺 点:Starlette 提供了所有基本的 Web 微框架功能。但是它不提供自动数据验证,序列化或 API 文档。
最全整理!37 个 Python Web 开发框架总结
20. Fastapi 框架
官 网:
https://fastapi.tiangolo.com/
优 点:FastAPI 是一个现代、快速(高性能)的 Web 框架,用于基于标准 Python 类型提示使用 Python 3.6+ 构建 API。非常高的性能,与 NodeJS 和 Go 相当(感谢 Starlette 和 Pydantic)。可用的最快的 Python 框架之一。减少大约 40% 的人为(开发人员)引发的错误。简短,简单,直观,健壮。
缺 点:本身不带模板语法,需要安装模板语法
最全整理!37 个 Python Web 开发框架总结
21. Responder 框架
官 网:
https://python-responder.org/
优 点:是 kennethreitz 新开发的一个项目,是一个基于 Python 的 HTTP 服务框架。底层用了 Starlette 的框架,Starlette 是一款轻量级的 ASGI 框架 / 工具包, 可以用 Starlette 构建高性能的异步 IO 服务。相对于 Starlette , responder 对开发者更加友好一点。kennethreitz 写了 requests 号称 “HTTP for Humans” 更加清楚这一点。事实上比较下来,responder 也比 Starlette 优雅很多。
缺 点:可能是新项目, 打包还不太完善。就目前版本而言,少许代码还不能正常工作。但接口形式已经定义完毕,期待后面版本会加上去。
22. Molten 框架
官 网:
https://moltenframework.com/
优 点:Molten 是一个最小的、可扩展的、快速的和高效的框架,用于使用 Python 构建 HTTP API。计划自动验证请求,代码更容易维护。实例可自动序列化,包含 API 在现实世界中通常要求的各种功能,广泛使用 PEP484 类型提示和 mypy。
缺 点:它没有使用像第三方库(如 Pydantic)提供数据验证,序列化和文档,它有自己的库。因此,这些数据类型定义将不太容易重用。依赖注入系统需要对依赖项进行预注册,并且将基于已声明的类型解决依赖问题。因此,不可能声明多个组件来提供一个特定的类型。
最全整理!37 个 Python Web 开发框架总结
23. Clastic 框架
官 网:
https://github.com/mahmoud/clastic
优 点:一个功能强大的 Python Web 框架,可简化显式开发实践,同时消除全局状态。Clastic 是完全用 Python 语言编写的,增强可重用性和可测试性,主动 URL 路由检查,更好地控制应用程序初始化,改进的中间件范例,没有全局状态,ORM 用法,更容易的 WSGI 集成。
缺 点:Clastic 没有构建全局状态;URL 路由规则参数和视图函数签名不匹配,在 Clastic 中,这种不匹配会在构建应用程序时引发异常。
24. Aiohttp 框架
官 网:
https://docs.aiohttp.org/en/stable/
优 点:用于 asyncio 和 Python 的异步 HTTP 客户端 / 服务器。支持客户端和 HTTP 服务器,支持开箱即用的服务器 WebSockets 和客户端 WebSockets,没有回调地狱。Web 服务器具有中间件、信号和可插入路由。
缺 点:根据 RFC 7231 aiohttp 2.0 版本后做了接受 HEAD 请求的调整,使用之前版本并且用 add_ get () 添加的请
求,如果使用 HEAD 方法访问会返回 405。如果处理器会写入很多响应体内容,你可以在执行 HEAD 方法时跳过处理响应体内容以提高执行效率。
最全整理!37 个 Python Web 开发框架总结
25. Masonite 框架
官 网:
https://docs.masoniteproject.com/
优 点:现代和以开发人员为中心的 Python Web 框架致力于实现实际的 batteries,包括具有许多开箱即用功能的开发人员工具,具有极其可扩展的架构。Masonite 非常适合初学者开发人员进入他们的第一个 Web 应用程序,以及需要充分利用 Masonite 的全部潜力来完成他们的应用程序的经验丰富的开发人员。Masonite 努力实现从安装到部署的快速简便,因此开发人员可以尽可能快速有效地从概念到创建。将其用于您的下一个 SaaS!尝试一次,你就会坠入爱河。
缺 点:对于 Masonite 来说,最大的” 竞争对手” 是 Django, 因为社区正在竭尽全力将框架推向容易,令人愉悦的下一件大事。与 Django 相比,Masonite 仍然是婴儿,期待它未来的表现。
最全整理!37 个 Python Web 开发框架总结
26. Quart 框架
官 网:
https://pgjones.gitlab.io/quart/
优 点:Quart 是一个 Python 网络微框架。使用 Quart,您可以呈现和提供 HTML 模板,编写(RESTful)JSON API,服务 WebSockets,流请求和响应数据,几乎可以通过 HTTP 或 WebSocket 协议做任何事情。
缺 点:它仅支持 Python 3.7+。
最全整理!37 个 Python Web 开发框架总结
27. Tonberry 框架
官 网:
https://github.com/Ayehavgunne/Tonberry
优 点:一个符合 ASGI 的 Web 微框架,采用基于类的路由方法。受 CherryPy 的影响但与 asyncio 兼容。一个名为 Qactuar 的配套 ASGI 服务器是从这个目前正在进行的项目中产生的。
缺 点:在使用拉取请求,请使用自动格式化程序 iSort 和 Black 并使用 MyPy 验证代码。这是在不消除某些类型错误的情况下,并不总是可以使 MyPy 完全满意,但是至少尽你所能并谨慎使用注释 `# type: ignore`。最后,尝试,尽可能提供最准确的类型提示。
最全整理!37 个 Python Web 开发框架总结
28. cyclone 框架
官 网:
https://cyclone.io/
优 点:Cyclone 是 Python 的 Web 服务器框架,它将 Tornado API 实现为 Twisted 协议。Twisted 是一个事件驱动的 Python 网络编程框架。它是最成熟的非阻塞 I/O 库之一,可供公众使用。Tornado 是 FriendFeed 网络服务器的开源版本,它是最流行和最快速的 Python 网络服务器之一,具有用于构建网络应用程序的非常不错的 API。除了丰富的功能集之外,Cyclone 还解决了 C10K 问题。
缺 点:Cyclone 不再支持 python 2.x 。
29. Klein 框架
官 网:
https://github.com/twisted/klein
优 点:Klein 是一个微框架,用于使用 Python 开发可用于生产的 Web 服务。它是 “微” 的,因为它具有类似于 Bottle 和 Flask 的非常小的 API。它不是 “微”,因为它依赖于标准库之外的东西。这主要是因为它建立在广泛使用和经过良好测试的组件上,如 Werkzeug 和 Twisted。
缺 点:也许有点过时了。
最全整理!37 个 Python Web 开发框架总结
30. nameko 框架
官 网:
https://github.com/nameko/nameko
优 点:AMQP RPC 和事件(发布 – 订阅),HTTPGET、POST 和 websockets,CLI 实现简单快速的开发,用于单元和集成测试的实用程序。
缺 点:nameko 微服务出错不会自动打印错误日志,需要加上监控相关的依赖,计算密集型任务导致任务重试。
最全整理!37 个 Python Web 开发框架总结
31. CubicWeb 框架
官 网:
https://www.cubicweb.org/
优 点:CubicWeb 是一种语义 Web 应用程序框架,在 LGPL 下获得许可,它使开发人员能够通过重用组件(称为立方体)并遵循众所周知的面向对象设计原则来有效地构建 Web 应用程序。满足常见需求的可重用组件(数据模型和视图)库,Python 编程语言的强大功能和灵活性,用于存储后端的 SQL 数据库、LDAP 目录、Subversion 和 Mercurial 的可靠性。
缺 点:要在 CubicWeb 应用程序中获取或操作持久数据,可以使用关系查询语言(RQL),RQL 提供了一种高度分离的路径来相互关联各种数据源。但是,随着它的实现,通过手动构建查询作为字符串,它可能会让习惯于 ORM 的开发人员感到过时。使用 CubicWeb 还有其他障碍。首先,设置可能很麻烦。另一个潜在的问题是缺少本机模板引擎;生成 HTML 留给开发人员。CubicWeb 支持 Python 3,但 Twisted 等模块本身并未完全移植。
最全整理!37 个 Python Web 开发框架总结
32. Zope2 框架
官 网:
https://zope.readthedocs.io/en/latest/
优 点:Zope 2 是一款基于 Python 的 Web 应用框架,是所有 Python Web 应用程序、工具的鼻祖,是 Python 家族一个强有力的分支。Zope 2 的 “对象发布” 系统非常适合面向对象开发方法,并且可以减轻开发者的学习曲线,还可以帮助你发现应用程序里一些不好的功能。
缺 点:Zope 不适用于简单的 RESTful API(每 Bottle 或 Flask),甚至不适用于具有交互性的基本网站(à la Django),由于 Zope 的尺寸和复杂性,安装需要一些工作;这不是简单地将源解压缩到项目子文件夹中的问题。
33. Web2py 框架
官 网:
http://web2py.com/
优 点:Web2py 是一个用 Python 语言编写的免费的开源 Web 框架,旨在敏捷快速的开发 Web 应用,具有快速、可扩展、安全以及可移植的数据库驱动的应用,遵循 LGPLv3 开源协议。Web2py 提供一站式的解决方案,整个开发过程都可以在浏览器上进行,提供了 Web 版的在线开发,HTML 模版编写,静态文件的上传,数据库的编写的功能。其它的还有日志功能,以及一个自动化的 admin 接口。
缺 点:Web2py 的一个重要限制是它仅与 Python 2.x 兼容。首先,这意味着 Web2py 无法使用 Python 3 的异步语法。如果你依赖于 Python 3 独有的外部库,那么你就不走运了。但是,正在开展使 Web2py Python 3 兼容的工作,并且在撰写本文时它已接近完成
最全整理!37 个 Python Web 开发框架总结
34. Pylons 框架
官 网:
https://docs.pylonsproject.org/projects/pylons-webframework/en/latest/
优 点:Pylons 是一个开放源代码的 Web 应用框架,使用 python 语言编写。它对 WSGI 标准进行了扩展应用,提升了重用性且将功能分割到独立的模块中。Pylons 是最新的 Web 应用框架中的典型,类似于 Django 和 TurboGears。Pylons 受 Ruby on Rails 影响很深:它的两个组件,Routes 和 WebHelpers 是 Rails 特性的 Python 实现。
缺 点:Pylons 已与 repoze.bfg 合并,现在处于仅维护模式。强烈建议新项目从新合并的 Web 框架金字塔开始。
最全整理!37 个 Python Web 开发框架总结

35. Cubes 框架

官 网:
https://pythonhosted.org/cubes/
优 点:Cubes 是一个轻量级的 Python 框架和工具集,用于开发报告和分析应用程序、在线分析处理 (OLAP)、多维分析和浏览聚合数据。它是 Data Brewery 的一部分。
缺点:不再为每个多维数据集提供隐式聚合 record_count。它必须明确定义为聚合:旧的后端架构是有限制的。它只允许使用一个商店,必须在服务器启动之前知道模型,不可能从远程来源获取模型。
36. Dpark 框架
官 网:
https://github.com/Liangchengdeye/Dpark
优 点:DPark 是 Spark 的 Python 克隆版本,是一个 Python 实现的分布式计算框架,可以非常方便地实现大规模数据处理和低延时的迭代计算。该计算框架类似于 MapReduce,但是比其更灵活,可以用 Python 非常方便地进行分布式计算,并且提供了更多的功能,以便更好地进行迭代式计算。DPark 由国内的豆瓣公司开发实现和负责维护,据豆瓣公司的描述,目前豆瓣公司内部的绝大多数数据分析都使用 DPark 完成,整个项目也正趋于完善。
缺 点:Dpark 使用的是进程,在 Python 中,由于 GIL (Global Interpreter Lock) 的存在,即使在多核机器上使用多个线程,这些线程之间也没有办法真正地实现并发执行。
37. Buildbot 框架
官 网:
https://buildbot.python.org/all/#/
优 点:Buildbot 是 python 实现的开源持续构建和持续交付工具,为 Python, Mozilla, Chromium, WebKit 等知名项目使用。Buildbot 可以直接使用 python 包,轻松拥有上万库,具备强大的扩展能力。如果你觉得 Jenkins 已经轻松地满足你的需求,你不需要 Buildbot。如果你在 Jenkins 时觉得效率低下、扩展困难、一些用 python 等脚本可以实现的动作在 Jenkins 困难重重,那么可以看看 Buildbot。
缺 点:buildbot 对多项目支持并不好。
最全整理!37 个 Python Web 开发框架总结
— EOF —

美国人治新冠要自己付钱,所以能和病毒共存

听说最近有个新的友尽话题,就是和朋友聊新冠病毒应该清零还是共存,无数人为了这个在网上吵的面红耳赤。
没明白这有啥好吵的,因为中国在今年只有清零病毒这一个选项,根本没有选择权。
至于和病毒共存,那是明年的事情了。 
中国和病毒共存是必然的事情,就是个时间问题,这一点早在2020年欧美选择躺平,放任病毒肆意传染时就已经注定。
只有中国消灭病毒,全球其他国家都不消灭病毒,那源源不断的病毒会从四面八方蔓延到我们的国土境内,失守是早晚的事情,就算自废经济,彻底的闭关锁国也无法阻挡这一结局。
但是我们在今年只能选择清零病毒,因为今年的奥密克戎毒株杀伤力依然过大。
欧美口口声声说奥密克戎是大号流感,说中国不必警惕,那请问欧美人自己那么害怕新冠干嘛。
什么时候欧美真的全部放开了,完全不把这病毒当一回事了,老百姓和医护真的一点都不害怕了,我们再开放也不迟,这时间大概就是明年。
我前一段时间说过,再熬一段时间,明年就没疫情了,因为今年秋天进化出来的新毒王传染性一定会再度上升,毒性一定会再度下降。
有人说病毒的变异是随机方向的,可能变弱也可能变强,未必一定如我所愿。
这话对,也不对。
病毒的变异确实是随机的,但进化的方向却是固定的。
变异体不是每年才出一个,以新冠如今恐怖的感染量来看,一天可能会出几十个变异体。
但绝大部分变异体都是没有价值的,传染不了几个人就会被淘汰掉。
消灭它们的不是人类,是最强大的那个毒株。
赢者通吃,最强毒株占据了所有的生态位之后,弱小毒株是没有任何活路可言的。
传染能力最强,杀伤力最弱的毒株,会成为唯一胜者。
如果两款毒株传染能力一样,但第一个杀伤力强,10%的患者有极其明显的咳嗽症状,健康人吓的不敢靠近,最终导致1%的人死亡。第二个杀伤力极弱,只有1%的患者有极其明显的咳嗽症状,最终导致0.1%的人死亡。
很明显,第二个毒株会非常迅速的消灭掉第一个毒株,因为它99%的感染者毫无症状却能到处散播病毒,健康人避开他们很困难。
症状越低,毒株传染越容易,奥密克戎厉害就厉害在无症状感染者比例特别特别的高,防不胜防。
无症状比例高会导致难以提防,但你连症状都没有,死亡率当然会很低。
因此,毒株进化的方向是固定的,每一代的毒王杀伤力都会更低,因为只有这样的毒王才有资格淘汰掉前一任毒王,否则就只是一个失败的变异体而已。
欧美今年都快扛不住了,宣传机器到处说奥密克戎只是大号感冒,一旦下一代毒王杀伤力再度弱化,他们必然放开全部防疫措施,时间极大概率就是明年。
这样的经济再容忍今年一年已经是欧美的极限了,欧美绝不可能再忍两年。
一旦欧美全部放开,彻底消除了新冠的经济影响,中国再观望几个月确定死亡率真的很低之后,也会跟着放开。
但,无论如何不是今年。
今年鼓吹共存,口口声声说要我们中国人重新认识病毒,反对清零的那是什么人? 都是一群双标公知。
美国人治新冠要自己付钱,所以能和病毒共存
同样的一群人,在2020年初武汉疫情时,大肆鼓吹什么这不是死亡304人,是死了一个人这件事发生了304次,还写的文采飞扬。 
美国人治新冠要自己付钱,所以能和病毒共存
美国那边已经死了98万人,他从来不说死了一个人这件事在美国发生了98万次。 
美国人治新冠要自己付钱,所以能和病毒共存
这个人前后的说法截然相反,但其实屁股从来没有变。
公知说什么都振振有词,就是怕合订本。
美国爹说病毒很可怕,那他就说病毒很可怕,美国爹说人类应该立即和病毒共存,那他就说人类应该和病毒共存。
而且不能拖,要立即共存,等明年都不行,一切以美国爹的旨意为准。
欧美能和病毒共存,那是因为欧美国情特殊,不代表中国也行。
最特殊的一点,就是欧美医疗系统永不崩溃。
美国可以做到日增100万感染者依然保持医疗系统的顺利运行,而中国日增几千人医疗系统就处于崩溃边缘了。
为什么美国可以做到医疗系统绝不崩溃而中国不行?
很简单,因为美国医院根本不收轻症,直接让你回家睡觉喝开水,不病到快死都不让你住院,那医疗系统当然不会崩溃。
为什么美国的老百姓这么听话,你让我回家我就回家,这要是一口气喘不上来死家里了怎么办?都知道新冠病情恶化的很迅速,有时候毫无征兆,你凭什么不让我住院?
以中国的国情,你让我居家我就居家,你让不住院我就不住院了?
你敢不让我住院,我回家就全网敲锣。
美国老百姓怎么就这么听话?难道美国老百姓不怕死?很明显住院的人生存率更高,哪怕只是轻症。
和住院相比,居家会增大你万分之一的死亡率,请问你是否同意居家治疗?
美国人同意,所以美国的医疗资源没有被挤兑,所以美国可以和病毒共存。
中国要想和病毒共存,保证自己医疗资源不被挤兑是最起码的事情。
美国人宁可增大自己死亡率也不住院,最大的原因不是因为心疼政府,心疼医院,而是因为住院要收费,且很贵。
在中国治疗新冠是免费的,无论花多少钱都是免费。
很多中国人似乎都忘了,治疗新冠是要钱的,而且要很多钱。
轻症就算了,要是住进ICU,一天2瓶白蛋白要850元,丙球蛋白每天4800元,这光药费就5000多元,加上医疗服务费和ICU病床费,一天一万起步。
如果上了ECMO急救,开机起步价5万元,运行一天再付2万块,其他药费和医疗服务费另算。 
美国人治新冠要自己付钱,所以能和病毒共存
中国政府于2020年1月22日下发文件,新冠患者的所有治疗全部免费,无条件免费。
就在这一天之前,武汉刚刚发生了一起悲剧,一名来自农村的新冠患者,是个孕妇,其丈夫砸锅卖铁,掏空所有积蓄并举债凑出来的20余万,在ICU病房仅仅一周多就花完了,没钱后当场去世,第二天国家宣布新冠全部免费,次日武汉封城。
此事曾被广为报道,这是一起悲剧,但也能侧面反映出新冠治疗到底有多贵。 
美国人治新冠要自己付钱,所以能和病毒共存
在很长一段时间里,中国新冠患者享受的是世界上最顶级的医疗待遇。
该上ICU就上ICU,甚至该上ECMO就上ECMO,根本不在乎花多少钱,只要能把人救回来就行,砸上百万才救回来的案例比比皆是。
能享受如此顶级的待遇,最大的原因是很多时候全省就一个新冠患者,这个人的出现能直接惊动省委书记,全省不惜一切代价来保他,尽量让他不死。
代价再大,如果只有几个人,那也不大。
欧美一开始也想全民免费来着,但没过几天看到日增感染几千人,几万人,几十万人,议案还没拟好送交议会讨论就直接当废纸扔了。
日增几个人,免费根本无所谓。
日增几万人,几十万人要是还搞免费,十个美国也受不了。
在美国得了新冠要治病,政府是不出钱的,按普通疾病处理,你该花多少钱就花多少钱,和你得流感没有区别。
这笔医药费你要是没买保险,那就全部自负,费用很贵很贵,贵的离谱,通常来说美国医院会直接拒收,看着你死在外面,因为知道你付不起医药费。 
美国人治新冠要自己付钱,所以能和病毒共存
如果你有保险,那保险公司会负担大头,但你肯定要自付一部分。
平时花的保险费多,自付就少一点,平时花的保险费少,自付就多一点。
但很多美国人,连哪怕只有10%的自付都承担不起,因为住院治疗新冠的费用实在是太贵了。 
美国人治新冠要自己付钱,所以能和病毒共存
到底有多贵?
美国新冠患者珍妮特·门德斯在纽约接受采访时说,自己新冠的医药费高达40万美元,其中自付7.5万美元。
美国平均年收入3万美元,珍妮特仅自付费用就相当于2年的全国平均收入,家庭元气大伤,要是没医保就可以原地等死了。
珍妮特的账单并不是最贵的。
加州居民杰夫因新冠住院11天,医疗费71万美元。 
美国人治新冠要自己付钱,所以能和病毒共存
还有更贵的,医疗费达110万美元,按10%自付都要11万美元之巨。 
美国人治新冠要自己付钱,所以能和病毒共存
当然,美国保险业很发达,各类产品五花八门,有完全不要你自付的,也有自付金额上限1万美元的,但条件越好的保险价格越贵,就好像你买车险的时候勾选了不计免赔保费会贵一截是一个道理。
一年保费两三万美元,和人均年收入一样高的医疗保险,你舍得买吗?
绝大多数美国人都不舍得买这么好的商业医疗险,性价比是他们的第一考虑因素,还有3000万人什么保险都没有。
对于绝大多数买了普通医疗险的美国人来说,进医院治新冠是要付钱的,哪怕自付只有20%或者10%,那也是要付钱的。
重症监护室床位费每天1万美元,药费3000美元,医护服务费7000美元,收你2万美元不贵吧,自付只要10%,收你2000美元不多吧。 
美国人治新冠要自己付钱,所以能和病毒共存
一天自付2000美元是什么概念?反正大部分美国人是根本付不起的。
就算是普通病房,一天连床位费带药费医疗费只要几千美元,自付10%只要几百美元,对大部分美国人来说也是个天文数字。
因此,美国老百姓乖乖居家治疗。
美国医生绝不反对你住院治疗,只是告诉你这样没必要而已,美国政府也绝不拦着你去医院,反而欢迎你去。
只要你愿意一天自付几百美元的医药费,你想住多久多可以,普通型新冠住院自付个4000美元就搞定了。
但很多普通美国人考虑了一下自己的钱包之后,哪怕有医保,也自愿居家喝开水。
人家真的是自愿居家的,你让他去医院住院他都能和你急眼,绝不是政府强迫,更不是医院赶人,人家就是要居家。
但如果你要帮他付医药费,那他连夜就会去医院躺着。
美国政府如果今天承诺新冠治疗免费,那明天美国的医疗系统就会瘫痪。
只要不承诺新冠治疗免费,那就永远不会瘫痪。
英国确诊30万例的时候,修建了英国版的方舱医院“南丁格尔医院”,里面设了4000张床位,结果怎么都没病人愿意去,最后收治了50多人就草草谢幕。
我花那么多钱,就住这玩意?那我还不如居家治疗。
要是免费我就住,但你又不免费。
美国政府承诺的唯一免费项目就是新冠检测,但就算是这个也在2022年被砍掉了,现在的美国人检测新冠要自费,无医保的3000万美国人每次核酸检测费用100美元到195美元,有医保的可以找保险公司按比例报销。 
美国人治新冠要自己付钱,所以能和病毒共存
而很多中国新冠病人,到出院了都不知道自己花了多少钱,医生压根不和你聊这个。
香港学大陆新冠免费,结果疫情一个压不住,医疗系统直接瘫痪了,因为大家都想住院,医院直接爆满后开始建方舱医院,但很快感染人数就是百万级,方舱医院那点容量根本不够用了,现在弄的一片糟,也不知道该怎么办了。
在香港公立医院瘫痪的时候,可以自费治疗的私立医院却门庭冷落,很少有患者选择。
香港嘉诺撒医院拥有医疗等级极高的负压病房,可以有效收治新冠病人,床位费公开报价是每日10万港元(8.08万元人民币)。
香港一百万阳性患者,公立医院彻底瘫痪,这家医院总共只有3张负压病床,至今却只使用了2张,还有1张闲置,医疗资源高度富裕,毫无瘫痪迹象,因为没几个人出得起这个钱。
很多大陆公知张口闭口就是新冠患者非负压病房不住,好像普通病房连垃圾都不算,也不知道他们住不住得起香港嘉诺撒医院的负压病床。
当初武汉火神山医院才1000张床位,为什么要举全国之力建造?
因为火神山医院里的这1000张床位,全部都是负压病床。
然后,全部免费。
这种集全国之力的高等级医疗待遇且免费,只适用于少量感染者出现的情况,一旦感染者人数略多必定陷入医疗瘫痪。
所以,我们现阶段不能学习欧美和病毒共存的做法,除非我们治疗新冠不免费。
上海一个小区,只出现了13个确诊,全小区都濒临崩溃,一线工作者已经很努力很努力了,努力程度远超普通人的想象。
这几天疯传一个视频,是上海市第一人民医院的医护人员被封控在小区后和居委会书记的对话,大家可以好好听一听,感受下一线抗疫人员到底有多难,这还是举全国之力支援上海的前提下。
如果真和病毒共存,每个城市各管各的,还要执行新冠免费政策,那中国基层组织和医疗系统必定崩溃。 
另外,新冠的治疗费不管是政府付,保险付,还是自付,钱不是天上掉下来的,账单总是要有人付的,羊毛都是出在羊身上。
不管医疗保险报销70%还是90%,其实100%的医疗费都是由人民群众自付的。
这就是中国新冠费用全部政府支付的逻辑,与其群众出不如自己出了,但前提条件是感染人数特别少。
美国感染人数特别多,但新冠治疗总费用也没有特别高,那是因为他们依靠巨额的自付费用把群众都赶出医院“自愿”居家治疗了,只治疗少数重症,每个人只在医院待几天,那全社会总治疗费用自然就压下去了。
殊途同归,但在奥密克戎毒株面前我不想居家喝开水。
如果中国认为奥密克戎是大号感冒,那治疗新冠就应该和感冒一样,医保付一部分自己付一部分,但要是真这么做了,现阶段中国民众能答应?自费部分都承担不起的中国人又该怎么办?
真当这东西是感冒,欧美人就不应该如此怕死,得了新冠也照样上班,真到那一步我们再考虑和病毒共存也不迟。
善战者无赫赫之功,压的敌人不敢开战,然后觉得他打仗所以就没有功劳,这是不对的。
中国治疗新冠代价极小,因此就觉得感染新冠没有损失,自己还年轻,千分之一的死亡率也落不到自己头上,这也是不对的。
不死,并不代表你不要付医药费。
美国人治新冠愿意自己付钱,你愿意吗?
不愿意,也没钱,还怕死,那你拿什么和病毒共存。
什么时候美国人得了新冠连治都不治了,99%的人能照常上班,什么时候我们和病毒共存也不迟。
赢得了开头,我们也要赢得了结尾。
在人类和新冠的战争里,牺牲主力由欧美人担任就行了,中国人只愿承担人类生命代价的零头。

转自:https://mp.weixin.qq.com/s/2BGQBzgOB9y8sH7ftnZ6dg

奥密克戎究竟想告诉上海什么?

· 秦朔 | 文  关注秦朔朋友圈  ID:qspyq2015 ·

一直被视为抗疫模范生的上海,此刻正在巨大的疫情压力下努力前行。

我4月6日中午开始写这篇文章。在前一天即5日0—24时,上海新增本土新冠肺炎确诊病例311例和无症状感染者16766例。

据市卫生健康委通报,截至6日9时,这次疫情全市已累计排查到在沪密切接触者134745人,均已落实管控,其中117885人核酸检测结果为阴性,其余正在检测中。

在老家的母亲看到上海的成千上万的数据,说“吓死人了”,嘱咐我们一定不要出门。很多朋友包括外地一些政府部门的朋友也问,上海到底怎么了?

不用回避,这确实是上海多年来少见的一次危机。危机在多个层面显现,加上疫情已开始外溢,引发了不少负面舆情,上海的形象饱受争议。

虽然每个上海人在疫情中的处境并不完全一样,因此各有各的感受和情绪,但我相信,大家有几点共识是一样的:希望疫情早点过去;希望保护好人民特别是相对弱势群体的安全与健康;感谢方方面面的付出特别是第一线人员的辛劳;希望今后再发生不测危机时,上海能应对得更好。

作为新上海人的一员,我也有几点思考和大家分享。我的出发点是,奥密克戎想给我们上一堂怎样的课?究竟想告诉上海什么?我们2500万人,不能白上了这一堂已经上了几十天还没上完的大课。

奥密克戎究竟想告诉上海什么?思考之一:海纳百议,虚心学习

上海的城市精神之一是海纳百川。这一次,我们要海纳百议,海纳百论。要真心实意地,虚己受人,从谏如流,千万不要浪费这场危机。

网上有很多建议,也有很多批评、嘲讽的声音,有的很难听,甚至很极端,我们都要听,要归纳整理,当成教材。

上海已经很久没有听到什么异议了,平时听到的基本都是好的。这当然是因为上海各项工作做得确实有声有色,在长三角乃至全国的各项中心功能发挥得很不错,但慢慢地,也会让我们觉得上海就是一座固若金汤的城市,一座台风来了都总会绕着走的魔都。

上海从2003年抗击SARS开始,建立了“横向到边、纵向到底”的网格化管理雏形,现在加上数字化精准管控,以及极富专业精神的医疗和公共卫生队伍,之前大大小小十几次抗疫都做得非常精准,所以我们都很放心,觉得上海的管控能力“独步天下”。

在这种情况下,谁来教育我们呢?自我教育是很难的,奥密克戎这股外力就来了。它要拿中国最大的城市、管理水平最高的城市之一上海来上课。上给我们看看,看看有多少问题。

上海要做一座学习型城市,学习型是进行时不是完成时,而且必然也是将来时。

“不要过分陶醉于我们人类对自然界的胜利。对于每一次这样的胜利,自然界都对我们进行报复。每一次胜利在第一步都确实取得了我们预期的结果,但是在第二和第三步都有了完全不同的、出乎预料的影响,它常常把第一个结果重新消除。”恩格斯这段话,值得好好体会。

奥密克戎究竟想告诉上海什么?

奥密克戎究竟想告诉上海什么?思考之二:居安思危,补牢短板

上海也是一座追求卓越的城市。我们想到上海,往往都是百年风华的外滩和陆家嘴的三件套,是高大上的摩登生活方式,是中西荟萃人才汇聚。但这次奥密克戎危机,则让我们深刻意识到安而不忘危,存而不忘亡,治而不忘乱的重要性,没有了安全,一座空空荡荡的城市,对我们每个人也就失去了意义。

要居安思危,企业管理中有一种行之有效的办法是通过“情景规划”,制定预案。1971年壳牌石油成立了情景规划小组,发展出“能源危机”的情景,想象一旦西方的石油公司失去对世界石油供给的控制,会发生什么,该怎样应对。他们随之开始在更多地区进行多元化油气资源的投资。1973年至1974年冬,石油输出国组织宣布石油禁运,壳牌石油成为唯一能抵抗危机的大型石油公司,从当时“七姐妹”(世界七大石油公司)中的“丑小鸭”一跃成为第二大石油公司。我们的城市管理和公共服务,也可以作为借鉴。

此次疫情也让我们看到了不少短板,而在过去可能是被忽略的。

就我和亲戚朋友的交流看,在全域静态管理后,保供应相对困难的地方,多是出租人口比较密集的地方。我一个朋友的小区发现一套房里住着14个快递小哥,由于人口登记不全,就拿不到什么物资分配。也有一些地方保供应不及时,物资发放不到位,和基层组织要管理的人口太多,捉襟见肘有关。遇到危机时,我们城市就会很强地凸显出不少弱势的、有无力感的群体,他们居住地环境的照片发在网上,甚至被认为是“四五线城市”。

要服务上海这样的大城市,需要很多快递员,骑手,便利店超市的打工人,保安保洁保姆,交通、物流、建筑、城管、医护等行业的临时工,他们已是社会离不开的群体,但关于这些非传统单位就业者的保障、管理、服务、培训等都很不够,估计相关信息也很不完整。这是疫情防控中的一个难题,也是城市进行有效管理的短板。这里特别要说的是,补好这块短板,绝不是要抑制他们,而是要切实了解他们的实际情况、行为规律、急难愁盼,更精细地服务好他们。因为上海时刻都离不了他们。

从推动共同富裕的角度看,关心和帮助城市中相对弱势的群体,也是非常必要的。

奥密克戎究竟想告诉上海什么?思考之三:夯实基础,人人有责

此次疫情,基层人员压力极大,一线矛盾也最多,他们身心俱疲,依然坚持奋战,他们就是城市治理的基本盘。在当下,各级干部要切实为基层减负、分忧、增能、打气,但控制住疫情之后,则要认真研判此次基层出现和暴露的问题,从权责、任务、流程、条件、能力、支持等方方面面夯实。“基础不牢,地动山摇”,是为至理。

从我个人的观察看,此次抗疫上半场,还是存在一些不够严、不够紧、不够细的地方,有些滴滴答答。例如3月12日开始就组织开展了三轮核酸测试和一次抗原检测,但相当一部分措施没有完全落实下去,封控措施不严格,筛选筛查的覆盖面不全,有的封控小区明确足不出户,但小区内还在自由活动,还有跳广场舞的,有的密接、次密接者还可以跑出家门。有的核酸检测缺乏秩序,间隔太近。有的采样一结束,没等结果出来,任由大家自由活动。3月27日晚的浦东抢菜也是一个教训。

这些问题本质上还是我们大家都有些轻敌,或者真的把上海当成了“金钟罩”。现在我们才意识到,除非每个人都成为基础,都严格遵守秩序和规定,否则城市是没有什么“金钟罩”的,那些“金钟罩”没有我们去协防可能都是窟窿。

基层工作人员就那么多,不可能什么都看到、管到、服务到,只有人人有责,自觉担责,自己当好自己的守护神,自己把自己当成城市的第一道防线,基层才会减少压力,把精力主要用于应对大的困难和压力上。

这不是说基层自身的问题不要去反思,但我想大家都看得清楚,我们一起努力,一起紧一紧,才能最后一起赢得自由。这是一场团体赛,每个地方都不能掉队。

我们小区有300多户人家,此次抗疫中大家有一点感到特别温暖,就是互助。虽然只是在检测、志愿者服务、社区团购等过程中有所接触,但得知谁家有困难,立即就有人主动提出帮助,不少人相约疫情后要互相走动走动,志愿者也相约将来都穿上“大白”去聚餐。这种自发的、内生的力量,可以也应该成为上海今后夯实治理基础的强大助力。

奥密克戎究竟想告诉上海什么?

奥密克戎究竟想告诉上海什么?思考之四:科学抗疫,未雨绸缪

此次疫情,围绕清零还是共存,以及如何清零、如何共存,有很多的争议。很多内容(如公众号“知识分子”)的水平非常高,摆事实,讲道理,这也是一次很好的科普。

我想当下我们的共同任务,就是众志成城,坚决按照中央和市委、市政府的要求,强有力地推进应检尽检、应收尽收、应隔尽隔、应治尽治,坚决打赢疫情防控这场大仗硬仗。上海也正在这样,团结一心,共克时艰。

与此同时,我也经常在想,如果我就是奥密克戎,现在出现在上海,和2020年初新型冠状病毒(2019-nCoV)出现在武汉,其含义会一样吗?2020年的病毒致病性很强,现在的奥密克戎变异株,扩散性很强、很快,但致病性相比起来已经很弱了。这就是身在上海,我们对病毒本身并不怎么紧张的原因。这也是有客观数据和一线医务专家的现身说法为证的。

病毒早于人类出现在地球上,它在不同时间出现在不同空间,折磨人也在训练人,教育人,让人变得更聪明,更有智慧。《淮南子》有云,“积力之所举,则无不胜也;众智之所为,则无不成也”。我们在举全国、全市、全民之力这方面很突出,但病毒只是考验我们这一种“力”吗?不是,它还在考验我们的“智”,考验我们在非常多的经济社会发展目标中的统筹和平衡能力。

这是我最近一直苦思的问题。病毒的生物属性在变异,那么我们未来如何抗疫,也应开始进行科学的、综合的、辩证的、实事求是的讨论,未雨绸缪。抗击奥密克戎当然是政治,但为患者治疗其他疾病难道不是政治,解决就业和经济发展不是政治?有些地方一个感染者也没有就开始封闭,这到底是讲政治还是不讲政治?

我们当然要高度重视病毒的社会和政治属性,即病毒对社会和政治的不良影响,但从根本上说,毕竟是生物属性决定政治属性,如果生物属性发生了变化,我们对其的政治理解也要进行调整。这种调整决不是躺平,而是如中央所说的,“努力用最小的代价实现最大的防控效果,最大限度减少疫情对经济社会发展的影响”。

有人说,人命关天,时代的一粒尘落到个体头上就是一座山,但也可以说,时代不是只有奥密克戎这一粒尘,有很多尘,落到个体头上都是一座山,一个社会要可持续发展,要跨越无数山,不能只把这一粒尘当成山,而别的山都看作尘。

最后想和大家说的是:

我坚信上海这座伟大的城市和上海人民一定能从这次的疫情危机中学习到很多,并化危为机,我们的集体反思,应该成为上海今后继续前进的坚固基石。

陀斯妥耶夫斯基说,“我只担心一件事,我怕我配不上自己所受的苦难”。希望上海今天的付出,会是未来的财富。

在此次抗疫过程中,上海也有很多宝贵的探索。虽然由于我们一些滴滴答答的疏漏,使得这种探索的意义打了折扣,但我们仍需鼓励医学专家继续探索集中化、精准化、分布式相结合的道路。这也是上海应该为中国展开的探索。

饱经沧桑的上海,依然是我们热爱和珍惜的家园。

奥密克戎究竟想告诉上海什么?

转自:https://mp.weixin.qq.com/s/3Kd42yyO6yIp_IXft87geQ