• 代码： #include <iostream> using namespace std; int max_ = 0; int sum = 0; int sum_temp = 0; int count_ = 0; //n is the length of arr,m is the number of your split. int func(int* arr,int n,......
 代码：
#include <iostream>
using namespace std;
int max_ = 0;
int sum = 0;
int sum_temp = 0;
int count_ = 0;
//n is the length of arr,m is the number of your split.
int func(int* arr,int n,int m)
{
for(int i=0;i<n;++i)//this loop is to get the max and sum,is also the floor and ceil of next loop.
{
if(arr[i]>max_)
{
max_ = arr[i];
}
sum+=arr[i];
}
for(int j=max_;j<=sum;j++)//between the max and sum,we make a loop to get the smallest number that meets the condition.
{
for(int k=0;k<n;k++)
{
sum_temp += arr[k];
if (sum_temp > j)//when the current sum of this part is bigger than j,we split.
{
count_++;
sum_temp = arr[k];
}
}
if(count_ < m)//when count_ < m,we say this j meets the condition.Because since you can split it by count_ times,
// and count_<=m-1,so you must be able to split it by m-1 times,and of course get m parts, the sum of each part <=j.
{
return j;
}

else
{
count_ = 0;// if there is no count_<m,we set the count_ 0.and j+=1,begin next loop,until we get the smallest j
//that meets the condition.
sum_temp = 0;
}
}
return sum;//if all cases can't meet the condition,so the sum of arr will be returned.
}
int main(){
int array_test[5] = {1,4,2,3,5};
cout<<func(array_test,5,3);
return 0;
}

参考： https://blog.csdn.net/seniusen/article/details/80539553
展开全文
• 题目： 给 n 个正整数 a_1,…,a_n，...问所有分割方案中分割分数的最小值是多少？ 输入描述：第一行依次给出正整数 n, m。第二行依次给出n 个正整数a1,...,ana1,...,an。 示例： 输入5 31 4 2 3...

题目：
给 n 个正整数 a_1,…,a_n， 将 n 个数顺序排成一列后分割成 m 段，每一段的分数被记为这段内所有数的和，该次分割的分数被记为 m 段分数的最大值。问所有分割方案中分割分数的最小值是多少？

输入描述： 第一行依次给出正整数 n, m。 第二行依次给出n 个正整数 a1,...,ana1,...,an。  示例：

输入 5 3 1 4 2 3 5 输出 5
说明 分割成 1 4 | 2 3 | 5 的时候三段的分数都为 5，得到分割分数的最小值。  备注： 对于所有的数据，有 m <= n <= 100000,0 <= aiai <= 2^39。  思路
将 n 个数划分为 n 段，分割分数即为 n 个正整数的最大值；将 n 个数划分为 1 段，分割分数即为 n 个正整数的和；若分为 m 段，则分割分数一定介于最大值与和之间。因此采用二分查找，每次取一个值对序列进行划分，若能划分出 m 段，使得每段的和都小于这个数值，则满足划分，反之不满足，直至找到一个最小的满足划分的值为止。 代码实现如下：

#include <iostream>
using namespace std;

int Judge(int data[], int sum, int m, int n);
int Binary_Search(int data[], int left, int right, int m, int n);

int main()
{
int n = 0, m = 0;
cin >> n >> m;

int data[n];
int max_num = 0;
int sum = 0;

int i = 0;

for(i = 0; i < n ; i++)
{
cin >> data[i];
if (data[i] > max_num)
{
max_num = data[i];
}
sum += data[i];
}

cout << Binary_Search(data, max_num, sum, m, n);

return 0;
}

int Binary_Search(int data[], int left, int right, int m, int n)
{
int mid = 0;

while (left < right)
{
mid = left + (right - left) / 2;
if (Judge(data, mid, m, n)) //满足划分，继续向左寻找
{
right = mid;//不减是因为无法确保减一之后的数是否满足划分
}
else    //不满足划分，继续向右寻找
{
left = mid + 1;
}
}

return left;
}

int Judge(int data[], int mid, int m, int n)
{
int cnt = 0;
int sum = 0;

for (int i = 0; i < n; i++)
{
if (sum + data[i] > mid) //累加和大于 mid，进行一次划分
{
sum = data[i];
cnt++;
if (cnt > m - 1)    //划分次数大于 m-1,不满足划分
{
return 0;
}
}
else
{
sum += data[i];
}
}

return 1;
}

个人见解，如有错误，欢迎指正与交流！
获取更多精彩，请关注「seniusen」!

转载于:https://www.cnblogs.com/seniusen/p/9143685.html
展开全文
• ## 图像分割综述

万次阅读 多人点赞 2019-07-09 22:03:48
图像分割是计算机视觉研究中的一个经典难题，已经成为图像理解领域关注的一个热点，图像分割是图像分析的第一步，是计算机视觉的基础，是图像理解的重要组成部分，同时也是图像处理中最困难的问题之一。所谓图像分割...
本文作者净浩泽，公众号：计算机视觉life，编辑成员

图像分割是计算机视觉研究中的一个经典难题，已经成为图像理解领域关注的一个热点，图像分割是图像分析的第一步，是计算机视觉的基础，是图像理解的重要组成部分，同时也是图像处理中最困难的问题之一。所谓图像分割是指根据灰度、彩色、空间纹理、几何形状等特征把图像划分成若干个互不相交的区域，使得这些特征在同一区域内表现出一致性或相似性，而在不同区域间表现出明显的不同。简单的说就是在一副图像中，把目标从背景中分离出来。对于灰度图像来说，区域内部的像素一般具有灰度相似性，而在区域的边界上一般具有灰度不连续性。 关于图像分割技术，由于问题本身的重要性和困难性，从20世纪70年代起图像分割问题就吸引了很多研究人员为之付出了巨大的努力。虽然到目前为止，还不存在一个通用的完美的图像分割的方法，但是对于图像分割的一般性规律则基本上已经达成的共识，已经产生了相当多的研究成果和方法。
本文对于目前正在使用的各种图像分割方法进行了一定的归纳总结，由于笔者对于图像分割的了解也是初窥门径，所以难免会有一些错误，还望各位读者多多指正，共同学习进步。

传统分割方法
这一大部分我们将要介绍的是深度学习大火之前人们利用数字图像处理、拓扑学、数学等方面的只是来进行图像分割的方法。当然现在随着算力的增加以及深度学习的不断发展，一些传统的分割方法在效果上已经不能与基于深度学习的分割方法相比较了，但是有些天才的思想还是非常值得我们去学习的。 1.基于阈值的分割方法 阈值法的基本思想是基于图像的灰度特征来计算一个或多个灰度阈值，并将图像中每个像素的灰度值与阈值作比较，最后将像素根据比较结果分到合适的类别中。因此，该方法最为关键的一步就是按照某个准则函数来求解最佳灰度阈值。 阈值法特别适用于目标和背景占据不同灰度级范围的图。 图像若只有目标和背景两大类，那么只需要选取一个阈值进行分割，此方法成为单阈值分割；但是如果图像中有多个目标需要提取，单一阈值的分割就会出现作物，在这种情况下就需要选取多个阈值将每个目标分隔开，这种分割方法相应的成为多阈值分割。  如图所示即为对数字的一种阈值分割方法。 阀值分割方法的优缺点：
计算简单，效率较高；只考虑像素点灰度值本身的特征，一般不考虑空间特征，因此对噪声比较敏感，鲁棒性不高。 从前面的介绍里我们可以看出，阈值分割方法的最关键就在于阈值的选择。若将智能遗传算法应用在阀值筛选上，选取能最优分割图像的阀值，这可能是基于阀值分割的图像分割法的发展趋势。 2.基于区域的图像分割方法 基于区域的分割方法是以直接寻找区域为基础的分割技术，基于区域提取方法有两种基本形式：一种是区域生长，从单个像素出发，逐步合并以形成所需要的分割区域；另一种是从全局出发，逐步切割至所需的分割区域。 区域生长 区域生长是从一组代表不同生长区域的种子像素开始，接下来将种子像素邻域里符合条件的像素合并到种子像素所代表的生长区域中，并将新添加的像素作为新的种子像素继续合并过程，知道找不到符合条件的新像素为止（小编研一第一学期的机器学习期末考试就是手写该算法 T.T），该方法的关键是选择合适的初始种子像素以及合理的生长准则。 区域生长算法需要解决的三个问题： （1）选择或确定一组能正确代表所需区域的种子像素； （2）确定在生长过程中能将相邻像素包括进来的准则； （3）指定让生长过程停止的条件或规则。 区域分裂合并 区域生长是从某个或者某些像素点出发，最终得到整个区域，进而实现目标的提取。而分裂合并可以说是区域生长的逆过程，从整幅图像出发，不断的分裂得到各个子区域，然后再把前景区域合并，得到需要分割的前景目标，进而实现目标的提取。其实如果理解了上面的区域生长算法这个区域分裂合并算法就比较好理解啦。 四叉树分解法就是一种典型的区域分裂合并法，基本算法如下： （1）对于任一区域，如果H(Ri)=FALSE就将其分裂成不重叠的四等分； （2）对相邻的两个区域Ri和Rj，它们也可以大小不同（即不在同一层），如果条件H(RiURj)=TURE满足，就将它们合并起来； （3）如果进一步的分裂或合并都不可能，则结束。 其中R代表整个正方形图像区域，P代表逻辑词。 区域分裂合并算法优缺点： （1）对复杂图像分割效果好； （2）算法复杂，计算量大； （3）分裂有可能破怪区域的边界。 在实际应用当中通常将区域生长算法和区域分裂合并算法结合使用，该类算法对某些复杂物体定义的复杂场景的分割或者对某些自然景物的分割等类似先验知识不足的图像分割效果较为理想。 分水岭算法 分水岭算法是一个非常好理解的算法，它根据分水岭的构成来考虑图像的分割，现实中我们可以想象成有山和湖的景象，那么一定是如下图的，水绕山山围水的景象。 分水岭分割方法，是一种基于拓扑理论的数学形态学的分割方法，其基本思想是把图像看作是测地学上的拓扑地貌，图像中每一点像素的灰度值表示该点的海拔高度，每一个局部极小值及其影响区域称为集水盆，而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面，刺穿一个小孔，然后把整个模型慢慢浸入水中，随着浸入的加深，每一个局部极小值的影响域慢慢向外扩展，在两个集水盆汇合处构筑大坝，即形成分水岭。 分水岭对微弱边缘具有良好的响应，图像中的噪声、物体表面细微的灰度变化都有可能产生过度分割的现象，但是这也同时能够保证得到封闭连续边缘。同时，分水岭算法得到的封闭的集水盆也为分析图像的区域特征提供了可能。
3.基于边缘检测的分割方法
基于边缘检测的图像分割算法试图通过检测包含不同区域的边缘来解决分割问题。它可以说是人们最先想到也是研究最多的方法之一。通常不同区域的边界上像素的灰度值变化比较剧烈，如果将图片从空间域通过傅里叶变换到频率域，边缘就对应着高频部分，这是一种非常简单的边缘检测算法。 边缘检测技术通常可以按照处理的技术分为串行边缘检测和并行边缘检测。串行边缘检测是要想确定当前像素点是否属于检测边缘上的一点，取决于先前像素的验证结果。并行边缘检测是一个像素点是否属于检测边缘高尚的一点取决于当前正在检测的像素点以及与该像素点的一些临近像素点。 最简单的边缘检测方法是并行微分算子法，它利用相邻区域的像素值不连续的性质，采用一阶或者二阶导数来检测边缘点。近年来还提出了基于曲面拟合的方法、基于边界曲线拟合的方法、基于反应-扩散方程的方法、串行边界查找、基于变形模型的方法。  边缘检测的优缺点： （1）边缘定位准确； （2）速度快； （3）不能保证边缘的连续性和封闭性； （4）在高细节区域存在大量的碎边缘，难以形成一个大区域，但是又不宜将高细节区域分成小碎片； 由于上述的（3）（4）两个难点，边缘检测只能产生边缘点，而非完整意义上的图像分割过程。这也就是说，在边缘点信息获取到之后还需要后续的处理或者其他相关算法相结合才能完成分割任务。 在以后的研究当中，用于提取初始边缘点的自适应阈值选取、用于图像的层次分割的更大区域的选取以及如何确认重要边缘以去除假边缘将变得非常重要。
结合特定工具的图像分割算法
基于小波分析和小波变换的图像分割方法
小波变换是近年来得到的广泛应用的数学工具，也是现在数字图像处理必学部分，它在时间域和频率域上都有量高的局部化性质，能将时域和频域统一于一体来研究信号。而且小波变换具有多尺度特性，能够在不同尺度上对信号进行分析，因此在图像分割方面的得到了应用， 二进小波变换具有检测二元函数的局部突变能力，因此可作为图像边缘检测工具。图像的边缘出现在图像局部灰度不连续处，对应于二进小波变换的模极大值点。通过检测小波变换模极大值点可以确定图像的边缘小波变换位于各个尺度上，而每个尺度上的小波变换都能提供一定的边缘信息，因此可进行多尺度边缘检测来得到比较理想的图像边缘。  上图左图是传统的阈值分割方法，右边的图像就是利用小波变换的图像分割。可以看出右图分割得到的边缘更加准确和清晰 另外，将小波和其他方法结合起来处理图像分割的问题也得到了广泛研究，比如一种局部自适应阈值法就是将Hilbert图像扫描和小波相结合，从而获得了连续光滑的阈值曲线。
基于遗传算法的图像分割
​ 遗传算法（Genetic Algorithms，简称GA）是1973年由美国教授Holland提出的，是一种借鉴生物界自然选择和自然遗传机制的随机化搜索算法。是仿生学在数学领域的应用。其基本思想是，模拟由一些基因串控制的生物群体的进化过程，把该过程的原理应用到搜索算法中，以提高寻优的速度和质量。此算法的搜索过程不直接作用在变量上，而是在参数集进行了编码的个体，这使得遗传算法可直接对结构对象（图像）进行操作。整个搜索过程是从一组解迭代到另一组解，采用同时处理群体中多个个体的方法，降低了陷入局部最优解的可能性，并易于并行化。搜索过程采用概率的变迁规则来指导搜索方向，而不采用确定性搜索规则，而且对搜索空间没有任何特殊要求（如连通性、凸性等），只利用适应性信息，不需要导数等其他辅助信息，适应范围广。 ​ 遗传算法擅长于全局搜索，但局部搜索能力不足，所以常把遗传算法和其他算法结合起来应用。将遗传算法运用到图像处理主要是考虑到遗传算法具有与问题领域无关且快速随机的搜索能力。其搜索从群体出发，具有潜在的并行性，可以进行多个个体的同时比较，能有效的加快图像处理的速度。但是遗传算法也有其缺点：搜索所使用的评价函数的设计、初始种群的选择有一定的依赖性等。要是能够结合一些启发算法进行改进且遗传算法的并行机制的潜力得到充分的利用，这是当前遗传算法在图像处理中的一个研究热点。
基于主动轮廓模型的分割方法
​ 主动轮廓模型（active contours）是图像分割的一种重要方法，具有统一的开放式的描述形式，为图像分割技术的研究和创新提供了理想的框架。在实现主动轮廓模型时，可以灵活的选择约束力、初始轮廓和作用域等，以得到更佳的分割效果，所以主动轮廓模型方法受到越来越多的关注。 ​ 该方法是在给定图像中利用曲线演化来检测目标的一类方法，基于此可以得到精确的边缘信息。其基本思想是，先定义初始曲线C，然后根据图像数据得到能量函数，通过最小化能量函数来引发曲线变化，使其向目标边缘逐渐逼近，最终找到目标边缘。这种动态逼近方法所求得的边缘曲线具有封闭、光滑等优点。  ​ 传统的主动轮廓模型大致分为参数主动轮廓模型和几何主动轮廓模型。参数主动轮廓模型将曲线或曲面的形变以参数化形式表达，Kass等人提出了经典的参数活动轮廓模型即“Snake”模型，其中Snake定义为能量极小化的样条曲线，它在来自曲线自身的内力和来自图像数据的外力的共同作用下移动到感兴趣的边缘，内力用于约束曲线形状，而外力则引导曲线到特征此边缘。参数主动轮廓模型的特点是将初始曲线置于目标区域附近，无需人为设定曲线的的演化是收缩或膨胀，其优点是能够与模型直接进行交互，且模型表达紧凑，实现速度快；其缺点是难以处理模型拓扑结构的变化。比如曲线的合并或分裂等。而使用水平集（level set）的几何活动轮廓方法恰好解决了这一问题。
基于深度学习的分割
1.基于特征编码（feature encoder based）
在特征提取领域中VGGnet和ResNet是两个非常有统治力的方法，接下来的一些篇幅会对这两个方法进行简短的介绍
a.VGGNet
​ 由牛津大学计算机视觉组合和Google DeepMind公司研究员一起研发的深度卷积神经网络。它探索了卷积神经网络的深度和其性能之间的关系，通过反复的堆叠33的小型卷积核和22的最大池化层，成功的构建了16~19层深的卷积神经网络。VGGNet获得了ILSVRC 2014年比赛的亚军和定位项目的冠军，在top5上的错误率为7.5%。目前为止，VGGNet依然被用来提取图像的特征。  ​ VGGNet的优缺点
由于参数量主要集中在最后的三个FC当中，所以网络加深并不会带来参数爆炸的问题；多个小核卷积层的感受野等同于一个大核卷积层（三个3x3等同于一个7x7）但是参数量远少于大核卷积层而且非线性操作也多于后者，使得其学习能力较强VGG由于层数多而且最后的三个全连接层参数众多，导致其占用了更多的内存（140M）
b.ResNet
​ 随着深度学习的应用，各种深度学习模型随之出现，虽然在每年都会出现性能更好的新模型，但是对于前人工作的提升却不是那么明显，其中有重要问题就是深度学习网络在堆叠到一定深度的时候会出现梯度消失的现象，导致误差升高效果变差，后向传播时无法将梯度反馈到前面的网络层，使得前方的网络层的参数难以更新，训练效果变差。这个时候ResNet恰好站出来，成为深度学习发展历程中一个重要的转折点。 ​ ResNet是由微软研究院的Kaiming He等四名华人提出，他们通过自己提出的ResNet Unit成功训练出来152层的神经网络并在ILSVRC2015比赛中斩获冠军。ResNet语义分割领域最受欢迎且最广泛运用的神经网络.ResNet的核心思想就是在网络中引入恒等映射，允许原始输入信息直接传到后面的层中，在学习过程中可以只学习上一个网络输出的残差（F(x)），因此ResNet又叫做残差网络。、  使用到ResNet的分割模型：
Efficient Neural Network（ENet）：该网络类似于ResNet的bottleNeck方法；ResNet-38：该网络在训练or测试阶段增加并移除了一些层，是一种浅层网络，它的结构是ResNet+FCN；full-resolution residual network(FRRN)：FRRN网络具有和ResNet相同优越的训练特性，它由残差流和池化流两个处理流组成；AdapNey：根据ResNet-50的网络进行改进，让原本的ResNet网络能够在更短的时间内学习到更多高分辨率的特征； …… ResNet的优缺点： 1）引入了全新的网络结构（残差学习模块），形成了新的网络结构，可以使网络尽可能地加深； 2）使得前馈/反馈传播算法能够顺利进行，结构更加简单； 3）恒等映射地增加基本上不会降低网络的性能； 4）建设性地解决了网络训练的越深，误差升高，梯度消失越明显的问题； 5）由于ResNet搭建的层数众多，所以需要的训练时间也比平常网络要长。
2.基于区域选择（regional proposal based）
Regional proposal 在计算机视觉领域是一个非常常用的算法，尤其是在目标检测领域。其核心思想就是检测颜色空间和相似矩阵，根据这些来检测待检测的区域。然后根据检测结果可以进行分类预测。 在语义分割领域，基于区域选择的几个算法主要是由前人的有关于目标检测的工作渐渐延伸到语义分割的领域的，接下来小编将逐步介绍其个中关系。
Stage Ⅰ： R-CNN
伯克利大学的Girshick教授等人共同提出了首个在目标检测方向应用的深度学习模型：Region-based Convolutional Neural Network（R-CNN）。该网络模型如下图所示，其主要流程为：先使用selective search算法提取2000个候选框，然后通过卷积网络对候选框进行串行的特征提取，再根据提取的特征使用SVM对候选框进行分类预测，最后使用回归方法对区域框进行修正。  R-CNN的优缺点：
是首个开创性地将深度神经网络应用到目标检测的算法；使用Bounding Box Regression对目标检测的框进行调整；由于进行特征提取时是串行，处理耗时过长；Selective search算法在提取每一个region时需要2s的时间，浪费大量时间
Stage Ⅱ：Fast R-CNN
​ 由于R-CNN的效率太低，2015年由Ross等学者提出了它的改进版本：Fast R-CNN。其网络结构图如下图所示（从提取特征开始，略掉了region的选择）Fast R-CNN在传统的R-CNN模型上有所改进的地方是它是直接使用一个神经网络对整个图像进行特征提取，就省去了串行提取特征的时间；接着使用一个RoI Pooling Layer在全图的特征图上摘取每一个RoI对应的特征，再通过FC进行分类和包围框的修正。  Fast R-CNN的优缺点
节省了串行提取特征的时间；除了selective search以外的其它所有模块都可以合在一起训练；最耗时间的selective search算法依然存在。
Stage Ⅲ：Faster R-CNN
2016年提出的Faster R-CNN可以说有了突破性的进展（虽然还是目标检测哈哈哈），因为它改变了它的前辈们最耗时最致命的部位：selective search算法。它将selective search算法替换成为RPN，使用RPN网络进行region的选取，将2s的时间降低到10ms，其网络结构如下图所示：  Faster R-CNN优缺点：
使用RPN替换了耗时的selective search算法，对整个网络结构有了突破性的优化；Faster R-CNN中使用的RPN和selective search比起来虽然速度更快，但是精度和selective search相比稍有不及，如果更注重速度而不是精度的话完全可以只使用RPN；
Stage Ⅴ：Mask Scoring R-CNN
3.基于RNN的图像分割
Recurrent neural networks（RNNs）除了在手写和语音识别上表现出色外，在解决计算机视觉的任务上也表现不俗，在本篇文章中我们就将要介绍RNN在2D图像处理上的一些应用，其中也包括介绍使用到它的结构或者思想的一些模型。 RNN是由Long-Short-Term Memory（LSTM）块组成的网络，RNN来自序列数据的长期学习的能力以及随着序列保存记忆的能力使其在许多计算机视觉的任务中游刃有余，其中也包括语义分割以及数据标注的任务。接下来的部分我们将介绍几个使用到RNN结构的用于分割的网络结构模型：
1.ReSeg模型
ReSeg可能不被许多人所熟知，在百度上搜索出的相关说明与解析也不多，但是这是一个很有效的语义分割方法。众所周知，FCN可谓是图像分割领域的开山作，而RegNet的作者则在自己的文章中大胆的提出了FCN的不足：没有考虑到局部或者全局的上下文依赖关系，而在语义分割中这种依赖关系是非常有用的。所以在ReSeg中作者使用RNN去检索上下文信息，以此作为分割的一部分依据。  该结构的核心就是Recurrent Layer，它由多个RNN组合在一起，捕获输入数据的局部和全局空间结构。 优缺点：
充分考虑了上下文信息关系；使用了中值频率平衡，它通过类的中位数(在训练集上计算)和每个类的频率之间的比值来重新加权类的预测。这就增加了低频率类的分数，这是一个更有噪声的分割掩码的代价，因为被低估的类的概率被高估了，并且可能导致在输出分割掩码中错误分类的像素增加。
2.MDRNNs（Multi-Dimensional Recurrent Neural Networks）模型
传统的RNN在一维序列学习问题上有着很好的表现，比如演讲（speech）和在线手写识别。但是 在多为问题中应用却并不到位。MDRNNs在一定程度上将RNN拓展到多维空间领域，使之在图像处理、视频处理等领域上也能有所表现。 该论文的基本思想是：将单个递归连接替换为多个递归连接，相应可以在一定程度上解决时间随数据样本的增加呈指数增长的问题。以下就是该论文提出的两个前向反馈和反向反馈的算法。
4.基于上采样/反卷积的分割方法
卷积神经网络在进行采样的时候会丢失部分细节信息，这样的目的是得到更具特征的价值。但是这个过程是不可逆的，有的时候会导致后面进行操作的时候图像的分辨率太低，出现细节丢失等问题。因此我们通过上采样在一定程度上可以不全一些丢失的信息，从而得到更加准确的分割边界。 接下来介绍几个非常著名的分割模型：
a.FCN(Fully Convolutional Network)
是的！讲来讲去终于讲到这位大佬了，FCN！在图像分割领域已然成为一个业界标杆，大多数的分割方法多多少少都会利用到FCN或者其中的一部分，比如前面我们讲过的Mask R-CNN。 在FCN当中的反卷积-升采样结构中，图片会先进性上采样（扩大像素）；再进行卷积——通过学习获得权值。FCN的网络结构如下图所示：  当然最后我们还是需要分析一下FCN，不能无脑吹啦~ 优缺点：
FCN对图像进行了像素级的分类，从而解决了语义级别的图像分割问题；FCN可以接受任意尺寸的输入图像，可以保留下原始输入图像中的空间信息；得到的结果由于上采样的原因比较模糊和平滑，对图像中的细节不敏感；对各个像素分别进行分类，没有充分考虑像素与像素的关系，缺乏空间一致性。
2.SetNet
SegNet是剑桥提出的旨在解决自动驾驶或者智能机器人的图像语义分割深度网络，SegNet基于FCN，与FCN的思路十分相似，只是其编码-解码器和FCN的稍有不同，其解码器中使用去池化对特征图进行上采样，并在分各种保持高频细节的完整性；而编码器不使用全连接层，因此是拥有较少参数的轻量级网络：

图像分割是计算机视觉研究中的一个经典难题，已经成为图像理解领域关注的一个热点，图像分割是图像分析的第一步，是计算机视觉的基础，是图像理解的重要组成部分，同时也是图像处理中最困难的问题之一。所谓图像分割是指根据灰度、彩色、空间纹理、几何形状等特征把图像划分成若干个互不相交的区域，使得这些特征在同一区域内表现出一致性或相似性，而在不同区域间表现出明显的不同。简单的说就是在一副图像中，把目标从背景中分离出来。对于灰度图像来说，区域内部的像素一般具有灰度相似性，而在区域的边界上一般具有灰度不连续性。 关于图像分割技术，由于问题本身的重要性和困难性，从20世纪70年代起图像分割问题就吸引了很多研究人员为之付出了巨大的努力。虽然到目前为止，还不存在一个通用的完美的图像分割的方法，但是对于图像分割的一般性规律则基本上已经达成的共识，已经产生了相当多的研究成果和方法。
本文对于目前正在使用的各种图像分割方法进行了一定的归纳总结，由于笔者对于图像分割的了解也是初窥门径，所以难免会有一些错误，还望各位读者多多指正，共同学习进步。

SetNet的优缺点：
保存了高频部分的完整性；网络不笨重，参数少，较为轻便；对于分类的边界位置置信度较低；对于难以分辨的类别，例如人与自行车，两者如果有相互重叠，不确定性会增加。 以上两种网络结构就是基于反卷积/上采样的分割方法，当然其中最最最重要的就是FCN了，哪怕是后面大名鼎鼎的SegNet也是基于FCN架构的，而且FCN可谓是语义分割领域中开创级别的网络结构，所以虽然这个部分虽然只有两个网络结构，但是这两位可都是重量级嘉宾，希望各位能够深刻理解~
5.基于提高特征分辨率的分割方法
在这一个模块中我们主要给大家介绍一下基于提升特征分辨率的图像分割的方法。换一种说法其实可以说是恢复在深度卷积神经网络中下降的分辨率，从而获取更多的上下文信息。这一系列我将给大家介绍的是Google提出的DeepLab 。 DeepLab是结合了深度卷积神经网络和概率图模型的方法，应用在语义分割的任务上，目的是做逐像素分类，其先进性体现在DenseCRFs（概率图模型）和DCNN的结合。是将每个像素视为CRF节点，利用远程依赖关系并使用CRF推理直接优化DCNN的损失函数。 在图像分割领域，FCN的一个众所周知的操作就是平滑以后再填充，就是先进行卷积再进行pooling,这样在降低图像尺寸的同时增大感受野，但是在先减小图片尺寸（卷积）再增大尺寸（上采样）的过程中一定有一些信息损失掉了，所以这里就有可以提高的空间。 接下来我要介绍的是DeepLab网络的一大亮点：Dilated/Atrous Convolution，它使用的采样方式是带有空洞的采样。在VGG16中使用不同采样率的空洞卷积，可以明确控制网络的感受野。  图a对应3x3的1-dilated conv，它和普通的卷积操作是相同的；图b对应3x3的2-dilated conv，事迹卷积核的尺寸还是3x3（红点），但是空洞为1，其感受野能够达到7x7；图c对应3x3的4-dilated conv，其感受野已经达到了15x15.写到这里相信大家已经明白，在使用空洞卷积的情况下，加大了感受野，使每个卷积输出都包含了较大范围的信息。 这样就解决了DCNN的几个关于分辨率的问题： 1）内部数据结构丢失；空间曾计划信息丢失； 2）小物体信息无法重建； 当然空洞卷积也存在一定的问题，它的问题主要体现在以下两方面： 1）网格效应 加入我们仅仅多次叠加dilation rate 2的 3x3 的卷积核则会出现以下问题  我们发现卷积核并不连续，也就是说并不是所有的像素都用来计算了，这样会丧失信息的连续性； 2）小物体信息处理不当 我们从空洞卷积的设计背景来看可以推测出它是设计来获取long-ranged information。然而空洞步频选取得大获取只有利于大物体得分割，而对于小物体的分割可能并没有好处。所以如何处理好不同大小物体之间的关系也是设计好空洞卷积网络的关键。
6.基于特征增强的分割方法
基于特征增强的分割方法包括：提取多尺度特征或者从一系列嵌套的区域中提取特征。在图像分割的深度网络中，CNN经常应用在图像的小方块上，通常称为以每个像素为中心的固定大小的卷积核，通过观察其周围的小区域来标记每个像素的分类。在图像分割领域，能够覆盖到更大部分的上下文信息的深度网络通常在分割的结果上更加出色，当然这也伴随着更高的计算代价。多尺度特征提取的方法就由此引进。 在这一模块中我先给大家介绍一个叫做SLIC，全称为simple linear iterative cluster的生成超像素的算法。 首先我们要明确一个概念：啥是超像素？其实这个比较容易理解，就像上面说的“小方块”一样，我们平常处理图像的最小单位就是像素了，这就是像素级（pixel-level）；而把像素级的图像划分成为区域级（district-level）的图像，把区域当成是最基本的处理单元，这就是超像素啦。 算法大致思想是这样的，将图像从RGB颜色空间转换到CIE-Lab颜色空间，对应每个像素的（L，a，b）颜色值和（x，y）坐标组成一个5维向量V[l, a, b, x, y],两个像素的相似性即可由它们的向量距离来度量，距离越大，相似性越小。 算法首先生成K个种子点，然后在每个种子点的周围空间里搜索距离该种子点最近的若干像素，将他们归为与该种子点一类，直到所有像素点都归类完毕。然后计算这K个超像素里所有像素点的平均向量值，重新得到K个聚类中心，然后再以这K个中心去搜索其周围与其最为相似的若干像素，所有像素都归类完后重新得到K个超像素，更新聚类中心，再次迭代，如此反复直到收敛。 有点像聚类的K-Means算法，最终会得到K个超像素。 Mostahabi等人提出的一种前向传播的分类方法叫做Zoom-Out就使用了SLIC的算法，它从多个不同的级别提取特征：局部级别：超像素本身；远距离级别：能够包好整个目标的区域；全局级别：整个场景。这样综合考虑多尺度的特征对于像素或者超像素的分类以及分割来说都是很有意义的。 接下来的部分我将给大家介绍另一种完整的分割网络：PSPNet：Pyramid Scene Parsing Network 论文提出在场景分割是，大多数的模型会使用FCN的架构，但是FCN在场景之间的关系和全局信息的处理能力存在问题，其典型问题有：1.上下文推断能力不强；2.标签之间的关系处理不好；3.模型可能会忽略小的东西。 本文提出了一个具有层次全局优先级，包含不同子区域时间的不同尺度的信息，称之为金字塔池化模块。 该模块融合了4种不同金字塔尺度的特征，第一行红色是最粗糙的特征–全局池化生成单个bin输出，后面三行是不同尺度的池化特征。为了保证全局特征的权重，如果金字塔共有N个级别，则在每个级别后使用1×1 1×11×1的卷积将对于级别通道降为原本的1/N。再通过双线性插值获得未池化前的大小，最终concat到一起。其结构如下图：  最终结果就是，在融合不同尺度的feature后，达到了语义和细节的融合，模型的性能表现提升很大，作者在很多数据集上都做过训练，最终结果是在MS-COCO数据集上预训练过的效果最好。  为了捕捉多尺度特征，高层特征包含了更多的语义和更少的位置信息。结合多分辨率图像和多尺度特征描述符的优点，在不丢失分辨率的情况下提取图像中的全局和局部信息，这样就能在一定程度上提升网络的性能。
7.使用CRF/MRF的方法
首先让我们熟悉熟悉到底啥是MRF的CRF的。 MRF全称是Marcov Random Field，马尔可夫随机场，其实说起来笔者在刚读硕士的时候有一次就有同学在汇报中提到了隐马尔可夫、马尔可夫链啥的，当时还啥都不懂，小白一枚（现在是准小白hiahia），觉得马尔可夫这个名字贼帅，后来才慢慢了解什么马尔科夫链呀，马尔可夫随机场，并且在接触到图像分割了以后就对马尔科夫随机场有了更多的了解。 MRF其实是一种基于统计的图像分割算法，马尔可夫模型是指一组事件的集合，在这个集合中，事件逐个发生，并且下一刻事件的发生只由当前发生的事件决定，而与再之前的状态没有关系。而马尔可夫随机场，就是具有马尔可夫模型特性的随机场，就是场中任何区域都只与其临近区域相关，与其他地方的区域无关，那么这些区域里元素（图像中可以是像素）的集合就是一个马尔可夫随机场。 CRF的全称是Conditional Random Field，条件随机场其实是一种特殊的马尔可夫随机场，只不过是它是一种给定了一组输入随机变量X的条件下另一组输出随机变量Y的马尔可夫随机场，它的特点是埃及设输出随机变量构成马尔可夫随机场，可以看作是最大熵马尔可夫模型在标注问题上的推广。 在图像分割领域，运用CRF比较出名的一个模型就是全连接条件随机场（DenseCRF），接下来我们将花费一些篇幅来简单介绍一下。 CRF在运行中会有一个问题就是它只对相邻节点进行操作，这样会损失一些上下文信息，而全连接条件随机场是对所有节点进行操作，这样就能获取尽可能多的临近点信息，从而获得更加精准的分割结果。 在Fully connected CRF中，吉布斯能量可以写作：  我们重点关注二元部分：  其中k(m)为高斯核，写作：  该模型的一元势能包含了图像的形状，纹理，颜色和位置，二元势能使用了对比度敏感的的双核势能，CRF的二元势函数一般是描述像素点与像素点之间的关系，鼓励相似像素分配相同的标签，而相差较大的像素分配不同标签，而这个“距离”的定义与颜色值和实际相对距离有关，这样CRF能够使图像尽量在边界处分割。全连接CRF模型的不同就在于其二元势函数描述的是每一个像素与其他所有像素的关系，使用该模型在图像中的所有像素对上建立点对势能从而实现极大地细化和分割。 在分割结果上我们可以看看如下的结果图：  可以看到它在精细边缘的分割比平常的分割方法要出色得多，而且文章中使用了另一种优化算法，使得本来需要及其大量运算的全连接条件随机场也能在很短的时间里给出不错的分割结果。 至于其优缺点，我觉得可以总结为以下几方面：
在精细部位的分割非常优秀；充分考虑了像素点或者图片区域之间的上下文关系；在粗略的分割中可能会消耗不必要的算力；可以用来恢复细致的局部结构，但是相应的需要较高的代价。 OK，那么本次的推送就到这里结束啦，本文的主要内容是对图像分割的算法进行一个简单的分类和介绍。综述对于各位想要深入研究的看官是非常非常重要的资源：大佬们经常看综述一方面可以了解算法的不足并在此基础上做出改进；萌新们可以通过阅读一篇好的综述入门某一个学科，比如今天的内容就是图像分割。 谢谢各位朋友们的观看！
推荐阅读
如何从零开始系统化学习视觉SLAM？ 从零开始一起学习SLAM | 为什么要学SLAM？ 从零开始一起学习SLAM | 学习SLAM到底需要学什么？ 从零开始一起学习SLAM | SLAM有什么用？ 从零开始一起学习SLAM | C++新特性要不要学？ 从零开始一起学习SLAM | 为什么要用齐次坐标？ 从零开始一起学习SLAM | 三维空间刚体的旋转 从零开始一起学习SLAM | 为啥需要李群与李代数？ 从零开始一起学习SLAM | 相机成像模型 从零开始一起学习SLAM | 不推公式，如何真正理解对极约束? 从零开始一起学习SLAM | 神奇的单应矩阵 从零开始一起学习SLAM | 你好，点云 从零开始一起学习SLAM | 给点云加个滤网 从零开始一起学习SLAM | 点云平滑法线估计 从零开始一起学习SLAM | 点云到网格的进化 从零开始一起学习SLAM | 理解图优化，一步步带你看懂g2o代码 从零开始一起学习SLAM | 掌握g2o顶点编程套路 从零开始一起学习SLAM | 掌握g2o边的代码套路 零基础小白，如何入门计算机视觉？ SLAM领域牛人、牛实验室、牛研究成果梳理 我用MATLAB撸了一个2D LiDAR SLAM 可视化理解四元数，愿你不再掉头发 最近一年语义SLAM有哪些代表性工作？ 视觉SLAM技术综述 汇总 | VIO、激光SLAM相关论文分类集锦 研究SLAM，对编程的要求有多高？ 2018年SLAM、三维视觉方向求职经验分享 2018年SLAM、三维视觉方向求职经验分享 深度学习遇到SLAM | 如何评价基于深度学习的DeepVO，VINet，VidLoc？ 视觉SLAM关键方法总结 SLAM方向公众号、知乎、博客上有哪些大V可以关注？ SLAM实验室 SLAM方向国内有哪些优秀公司？ SLAM面试常见问题 SLAM相关领域数据集调研 从零开始一起学习SALM-ICP原理及应用 解放双手——相机与IMU外参的在线标定 目标检测
展开全文
• ## 小甲鱼零基础入门学习python笔记

万次阅读 多人点赞 2019-08-14 11:06:30
score = int(input('请输入一个分数：')) if 100 >= score >= 90: print('A') elif 90 > score >= 80: print('B') elif 80 > score >= 60: print('C') elif 60 > score >= 0: print('D') else: print('输入错误！...
小甲鱼老师零基础入门学习Python全套资料百度云(包括小甲鱼零基础入门学习Python全套视频+全套源码+全套PPT课件+全套课后题及Python常用工具包链接、电子书籍等）请往我的资源（https://download.csdn.net/download/qq_32809093/13099592）查看
目录：
000 愉快的开始 001 我和Python的第一次亲密接触 002 用Python设计第一个游戏 003 小插曲之变量和字符串 004 改进我们的小游戏 005 闲聊之Python的数据类型 006 Pyhon之常用操作符 007 了不起的分支和循环 008 了不起的分支和循环2 009 了不起的分支和循环3 010 列表：一个打了激素的数组 011列表：一个打了激素的数组2 012列表：一个打了激素的数组3 013元组：戴上了枷锁的列表 014字符串：各种奇葩的内置方法 015字符串：格式化 016 序列！序列！ 017函数：Python的乐高积木 018 函数：灵活即强大 019函数：我的地盘听我的（局部变量与全局变量） 020函数：内嵌函数和闭包 021函数：lambda表达式 022 函数：递归是神马 023 递归：这帮小兔崽子 024 递归：汉诺塔 025 字典：当索引不好用时 026 字典：当索引不好用时2 027 集合：在我的世界里，你就是唯一 028 文件：因为懂你，所以永恒 029 文件：一个任务 030 文件系统：介绍一个高大上的东西 031 永久存储：腌制一缸美味的泡菜(pickle) 032 异常处理：你不可能总是对的 033 异常处理：你不可能总是对的2 034 丰富的else语句及简洁的with语句 035 图形用户界面入门：EasyGui 036 类和对象：给大家介绍对象 037 类和对象：面向对象编程 038 类和对象：继承 039 类和对象：拾遗 040 类和对象：一些相关的BIF 041 魔法方法：构造和析构 042 魔法方法：算术运算 043 魔法方法：算术运算2 044 魔法方法：简单定制 045 魔法方法：属性访问 046 魔法方法：描述符（Property的原理） 047 魔法方法：定制序列 048 魔法方法：迭代器 049 乱入：生成器 050 模块：模块就是程序 051 模块：__name__='__main__'、搜索路径和包 052 模块：像个极客一样去思考 053 论一只爬虫的自我修养 054 论一只爬虫的自我修养2：实战 055 论一只爬虫的自我修养3：隐藏
064 GUI的终极选择：Tkinter 065 GUI的终极选择：Tkinter2 066 GUI的终极选择：Tkinter3 067 GUI的终极选择：Tkinter4 068 GUI的终极选择：Tkinter5 069 GUI的终极选择：Tkinter6 070 GUI的终极选择：Tkinter7 071 GUI的终极选择：Tkinter8 073 GUI的终极选择：Tkinter10 074  GUI的终极选择：Tkinter11 075 GUI的终极选择：Tkinter12 076 GUI的终极选择：Tkinter13 077 GUI的终极选择：Tkinter14
078 Pygame：初次见面，请大家多多关照

000 愉快的开始
python跨平台。 应用范围：操作系统、WEB、3D动画、企业应用、云计算 大家可以学到什么：Python3的所有常用语法、面向对象编程思维、运用模块进行编程、游戏编程、计算机仿真
Python 是脚本语言脚本语言(Scripting language)是电脑编程语言，因此也能让开发者藉以编写出让电脑听命行事的程序。以简单的方式快速完成某些复杂的事情通常是创造脚本语言的重要原则，基于这项原则，使得脚本语言通常比 C 语言、C++语言 或 Java 之类的系统编程语言要简单容易。也让脚本语言另有一些属于脚本语言的特性： •  语法和结构通常比较简单 •  学习和使用通常比较简单 •  通常以容易修改程序的“解释”作为运行方式，而不需要“编译” •  程序的开发产能优于运行性能 一个脚本可以使得本来要用键盘进行的相互式操作自动化。一个 Shell 脚本主要由原本需要在命令行输入的命令组成，或在一个文本编辑器中，用户可以使用脚本来把一些常用的操作组合成一组串行。主要用来书写这种脚本的语言叫做脚本语言。很多脚本 语言实际上已经超过简单的用户命令串行的指令，还可以编写更复杂的程序。
IDLE 是一个 Python Shell，shell 的意思就是“外壳”，基本上来说，就是一个通过键入文本与程序交互的途径！像我们 Windows 那个 cmd 窗口，像 Linux 那个黑乎乎的命令窗口，他们都是 shell，利用他们，我们就可以给操作系统下达命令。同样的，我们可以利用 IDLE 这个 shell 与 Python 进行互动。
注：在 Python 中不能把两个完全不同的东西加在一起，比如说数字和文本
如果我需要在一个字符串中嵌入一个双引号，正确的做法是：你可以利用反斜杠（\）对双引号转义：\"，或者用单引号引起这个字符串
001 我和Python的第一次亲密接触
从IDLE启动Python
•IDLE是一个Python Shell，shell的意思就是“外壳”，基本上来说，就是一个通过键入文本与程序交互的途径！
•我们看到>>>这个提示符，Ta的含义是告诉你，Python已经准备好了，在等着你键入Python指令呢。
•好了，大家试试在IDLE里输入：
>>>print (“I love fishc.com”)

•我们尝试点儿新的东西，我们输入
>>>print(5+3)

•或者直接输入
>>>5+3

•不妨在试试计算
>>>1234567890987654321*987654321012345678

•还有我们可以将两个字符串“相加”在一起，这种做法叫做拼接字符串
>>>print("well water"+"river")

•先试试
>>> print("I love fishc.com"*2)

•和
>>> print("I love fishc.com\n"* 2)

002 用Python设计第一个游戏
实例1：
print("---------我爱鱼C工作室----------") temp = input("不妨猜一下小甲鱼现在心里想的是哪个数字:") guess = int(temp) if guess == 8:     print("我草，你是小甲鱼心里的蛔虫嘛？！")     print("哼，猜中了也没有奖励！") else:     print("猜错了，小甲鱼现在心里想的是8！")     print("游戏结束，不玩啦")

BIF 就是 Built-in Functions，内置函数。为了方便程序员快速编写脚本程序（脚本就是要编程速度快快快！！！），Python 提供了非常丰富的内置函数，我们只需要直接调用即可，例如 print() 的功能是“打印到屏幕”，input() 的作用是接收用户输入。
在 Python 或 IDLE 中，输入 dir(__builtins__) 可以看到 Python 提供的内置方法列表（注意，builtins 前后是两个下划线哦）其中小写的就是 BIF。如果想具体查看某个BIF 的功能，比如 input()，可以在 shell 中输入 help(input)，就会得到这个 BIF 的功能描述。哦，答案应该是 68 个
>>> dir(__builtins__)  查看 Python 提供的内置方法列表

>>> help(input)  查看input的具体使用说明

注：
只有当标识符已经赋值后（ Python 的变量是不用先声明的）才能在代码中使用，未赋值的标识符直接使用会导致运行时错误
缩进是 Python 的灵魂
Python 不允许 if 条件中赋值，所以 if c = 1: 会报错！

003 小插曲之变量和字符串
插曲之变量
•变量名就像我们现实社会的名字，把一个值赋值给一个名字时，Ta会存储在内存中，称之为变量（variable），在大多数语言中，都把这种行为称为“给变量赋值”或“把值存储在变量中”。
•不过Python与大多数其他计算机语言的做法稍有不同，Ta并不是把值存储在变量中，而更像是把名字贴在值的上边。
•所以有些Python程序员会说“Python”没有“变量”，只有“名字”。

需要注意的地方
•在使用变量之前，需要对其先赋值。
•变量名可以包括字母、数字、下划线，但变量名不能以数字开头。
•字母可以是大写或小写，但大小写是不同的。也就是说fishc和FishC对于Python来说是完全不同的两个名字
•等号（=）是赋值的意思，左边是名字，右边是值，不可写反咯。
插曲之字符串
•到目前为止，我们所认知的字符串就是引号内的一切东西，我们也把字符串叫做文本，文本和数字是截然不同的，咱看例子：>>>5+8
>>> '5'+'8'

•要告诉Python你在创建一个字符串，就要在字符两边加上引号，可以是单引号或者双引号，Python女士表示不挑剔。但必须成对，你不能一边单引号，另一边却花心的用上双引号结尾。
•如果字符串中需要出现单引号或双引号怎么办？
–例如我想打印字符串：Let’s go!
•有两种方法，第一种比较常用，就是使用我们的转义符号（\）对字符串中的引号进行转义：
>>> 'Let\'s go!'

原始字符串
•好像反斜杠是一个好东西，但不妨试试打印：
>>> str = 'C:\now'

•我们可以用反斜杠对自身进行转义：
>>> str = 'C:\\now'

•原始字符串的使用非常简单，只需要在字符串前边加一个英文字母r即可(则都会以原始字符串输出)：
>>>str = r'C:\now'

长字符串
•如果希望得到一个跨越多行的字符串，例如：
我爱鱼C，
正如我爱小甲鱼，
久久不肯散去……
这我们就需要使用到三重引号字符串！

004 改进我们的小游戏
•第一个改进要求：猜错的时候程序提示用户当前的输入比答案大了还是小了

与操作and

•第二个改进要求：程序应该提供多次机会给用户猜测，专业点来讲就是程序需要重复运行某些代码。
条件分支

while循环

实例1：找8
temp = input("请输入一个数据：") guess = int(temp) i=0 while guess != 8 and i < 3:     i = i + 1     temp = input("哎呀，猜错了，请重新输入吧：")     guess = int(temp)     if guess == 8:         print("我草，你是小甲鱼心里的蛔虫嘛？")         print("哼，猜对了也没有奖励")     else:         if guess > 8:             print("哥，大了大了~~")         else:             print("嘿，小了！小了！！") print("游戏结束，不玩啦~~")

•random模块里边有一个函数叫做：randint()，Ta会返回一个随机的整数。
实例2：找随机数
import random#导入随机数函数 secret = random.randint(1,5)#随机生成1到5的一个随机数 temp = input("请输入一个1-5的数据：") guess = int(temp) i=0 while guess != secret and i < 6:     i = i + 1     guess = int(temp)     if guess == secret:         print("我草，你是小甲鱼心里的蛔虫嘛？")         print("哼，猜对了也没有奖励")     else:         if guess > secret:             print("哥，大了大了~~")         else:             print("嘿，小了！小了！！")               temp = input("请重新输入吧：") print("游戏结束，不玩啦~~")

005 闲聊之Python的数据类型
Python的一些数值类型：整型、布尔类型（True与False）、浮点型、e记法、复数类型等
e记法（e4相当于10的四次方，e-10相当于10的-10次方）

类型转换

字符型转换为整型

其它同上
type()函数(可查看变量类型)

isinstance()函数（用来判断两个输入参数类型是否一致）

006 Pyhon之常用操作符
算术操作符

注：python中 \ 为除法， \\ 为整除 ，% 为取余
幂运算（3的二次方）

3的二次方后取负
注：先幂运算、然后乘除、后加减、后逻辑
3的负二次方

比较操作符

逻辑操作符

优先级问题

007 了不起的分支和循环
打飞机游戏框架：
加载背景音乐
播放背景音乐（设置单曲循环）
我方飞机诞生
while True:     if 用户是否点击了关闭按钮：        推出程序             interval += 1;     if interval == 50:        interval = 0;        小飞机诞生     小飞机移动一个位置     屏幕刷新          if 用户鼠标产生移动：        我方飞机中心位置 = 用户鼠标位置        屏幕刷新             if 我方飞机与小飞机发生肢体冲突：        我方挂，播放撞机音乐        修改我方飞机图案        打印“Game over"        停止背景音乐，最好淡出

008 了不起的分支和循环2
•现在小甲鱼来考考大家：
–按照100分制，90分以上成绩为A，80到90为B，60到80为C，60以下为D，写一个程序，当用户输入分数，自动转换为ABCD的形式打印。
score = int(input('请输入一个分数：')) if 100 >= score >= 90:     print('A') elif 90 > score >= 80:     print('B') elif 80 > score >= 60:     print('C') elif 60 > score >= 0:     print('D') else:     print('输入错误！')
条件表达式（三元操作符）
有了这个三元操作符的条件表达式，你可以使用一条语句来完成以下的条件判断和赋值操作：
x, y = 4, 5
if x < y:
small = x
else:
small = y
例子可以改进为
small = x if x < y else y    #如果x小于y，则small等于x，否则等于y
断言（assert）
•assert这个关键字我们称之为“断言”，当这个关键字后边的条件为假的时候，程序自动崩溃并抛出AssertionError的异常。
•举个例子：
>>> assert 3 > 4

•一般来说我们可以用Ta再程序中置入检查点，当需要确保程序中的某个条件一定为真才能让程序正常工作的话，assert关键字就非常有用了。

009 了不起的分支和循环3
while循环
while 条件：
循环体
for循环
•虽然说Python是由C语言编写而来的，但是Ta的for循环跟C语言的for循环不太一样，Python的for循环显得更为智能和强大！
•语法：
for 目标 in 表达式：
循环体
每次取FishC中一个字符及空格输出

range()函数
•语法：range( [strat],[stop],[step] )
–这个BIF有三个参数，其中用中括号括起来的两个表示这两个参数是可选的。
–step=1表示第三个参数的值默认值是1。setp为每步距离
–range这个BIF的作用是生成一个从start参数的值开始到stop参数的值结束的数字序列。

break语句（结束本层循环）
实例：
bingo = '小甲鱼是帅哥' answer = input('请输入小甲鱼最想听的一句话：')
while True:     if answer == bingo:         break     answer = input('抱歉，错了，请重新输入（答案正确才能退出游戏）：')
print('哎哟，帅哦~') print('您真是小甲鱼肚子里的蛔虫啊^_^')

continue语句（当前位置结束本次循环，重新开始下次循环）
实例：
for i in range(10):     if i%2 != 0:         print(i)         continue     i += 2     print(i)

010 列表：一个打了激素的数组
列表：一个打了激素的数组

创建列表
•创建一个普通列表

•创建一个混合列表

•创建一个空列表

向列表添加元素

append()函数向列表末尾添加一个元素

extend()函数向列表末尾添加多个元素

insert(n,xxx)函数向列表中第n个元素前插入一个元素
注：0表示第一个元素

011列表：一个打了激素的数组2
从列表中获取元素
•跟数组一样，我们可以通过元素的索引值（index）从列表获取单个元素，注意，列表索引值是从 0 开始的。

从列表删除元素

remove()函数表示从列表中删除某个元素

del()函数也表示从列表中删除某个元素

pop()函数从列表中取出最后一个元素

列表分片（Slice）
•利用索引值，每次我们可以从列表获取一个元素，但是我们总是贪心的，如果一次性需要获取多个元素，有没有办法实现呢？利用列表分片，我们可以简单的实现这个要求。
member[0:2]表示从第1个元素开始拷贝，一共拷贝两个元素，即member[0]和member[1]
列表的拷贝

012列表：一个打了激素的数组3
列表的一些常用操作符
•比较操作符

•逻辑操作符

•连接操作符

•重复操作符

•成员关系操作符

关于分片“拷贝”概念的补充

>>> dir(list)可查看所有列表的操作函数

count()函数可计算列表中相同元素个数

index()函数可索引列表元素

reverse()将列表中元素倒序

sort()将列表中元素从小到大排序

关于分片“拷贝”概念的补充

注：list13=list11相当于多了个指向列表的标签，list12 = list[:]是实实在在的拷贝
013元组：戴上了枷锁的列表
•由于和列表是近亲关系，所以元组和列表在实际使用上是非常相似的。
•我们这节课主要通过讨论元组和列表到底有什么不同来学习元组，酱紫大家就不会觉得老是重复一样的内容。
•我们主要从以下几个点来讨论学习：
–创键和访问一个元组
创建元组（括号可以没有，但逗号一定要有）

访问元组前两个元素

–更新和删除一个元组
更新一个元组
注：其并未对原元组进行修改，而是生成了一个新的元组，并贴上temp名字标签而已。原元组由于标签没有了，则会被自动回收。
删除一个元组

–元组相关的操作符
注：元组不允许修改和删除。
014字符串：各种奇葩的内置方法

015字符串：格式化

由于花括号被解释掉，所以不打印后面中文

字符串格式化符号含义

将ASCII码97对应的字符输出

格式化整数

格式化操作符辅助命令

5表示输出为五位数
Python 的转义字符及其含义

016 序列！序列！
•列表、元组和字符串的共同点
–都可以通过索引得到每一个元素
–默认索引值总是从0开始
–可以通过分片的方法得到一个范围内的元素的集合
–有很多共同的操作符（重复操作符、拼接操作符、成员关系操作符）
使用list方法

元组转换为列表
注：元组为小括号，列表为中括号。
max() 返回序列或者参数集合中的最大值

min() 返回序列或者参数集合中的最小值
sum(iterable[,start=0]) 返回序列iterable和可选参数start的总和

sorted()将元素从小到大重新排列

reversed()将元素倒序排列
注：元组是不可以修改和删除的，所以不可以直接对元组使用sorted与reversed命令
enumerate()将每个元素插入枚举

zip()返回由各个参数的序列组成的元组

017函数：Python的乐高积木
定义一个函数和调用

018 函数：灵活即强大
形参和实参
>>> def MyFirstFunction(name):
'函数定义过程中的name是叫形参'
#因为Ta只是一个形式，表示占据一个参数位置
print('传递进来的' + name + '叫做实参，因为Ta是具体的参数值！')
>>> MyFirstFunction('小甲鱼')
传递进来的小甲鱼叫做实参，因为Ta是具体的参数值！
关键字参数

默认参数(即形参中给定默认值，则在未给实参时会以默认值输出)

收集参数

019函数：我的地盘听我的
函数与过程
再谈谈返回值
如果有返回值，函数则返回对应值；如果没有，则返回None
可以返回多个值

019函数：我的地盘听我的（局部变量与全局变量）
def discounts(price, rate):     final_price = price * rate     old_price = 88 #这里试图修改全局变量     print('修改后old_price的值是：', old_price)     return final_price
old_price = float(input('请输入原价：')) rate = float(input('请输入折扣率：')) new_price = discounts(old_price, rate) print('修改后old_price的值是：', old_price) print('打折后价格是：', new_price)

global可将局部变量声明为全局变量

020函数：内嵌函数和闭包
内嵌函数

闭包（closure）

注：使用nonlocal语句将x强制为不是局部变量
021函数：lambda表达式

lambda表达式的作用
•Python写一些执行脚本时，使用lambda就可以省下定义函数过程，比如说我们只是需要写个简单的脚本来管理服务器时间，我们就不需要专门定义一个函数然后再写调用，使用lambda就可以使得代码更加精简。
•对于一些比较抽象并且整个程序执行下来只需要调用一两次的函数，有时候给函数起个名字也是比较头疼的问题，使用lambda就不需要考虑命名的问题了。
•简化代码的可读性，由于普通的屌丝函数阅读经常要跳到开头def定义部分，使用lambda函数可以省去这样的步骤。
过滤函数filter可筛选出非零元素

筛选出奇数
注：lambda x:x%2用来判断是否为奇，x为奇则输出1，否则输出0；range(10)可生成0-9的10个整数，filter用来筛选非零元素；如果为偶数，则被筛选掉；如果为奇数，则保留，但输出的是rang(10)产生的原始数，因为lambda只是用来判断是否为奇偶
range生成的0-9给了x，x经过2倍运算后再赋值给x
022 函数：递归是神马
汉诺塔游戏

树结构的定义

谢尔宾斯基三角形

递归求阶乘
•写一个求阶乘的函数
–正整数阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。
–例如所给的数是5，则阶乘式是1×2×3×4×5，得到的积是120，所以120就是4的阶乘。
•假设我们n的值传入是5，那么：

实例：求阶乘
def factorial(n):     result = n     for i in range(1, n):         result *= i
return result
number = int(input('请输入一个正整数：')) result = factorial(number) print("%d 的阶乘是：%d"  % (number, result))#格式化为整数类型

实例2：递归求阶乘
def factorial(n):     if n == 1:         return 1     else:         return n * factorial(n-1)
number = int(input('请输入一个正整数：')) result = factorial(number) print("%d 的阶乘是：%d" % (number, result))

023 递归：这帮小兔崽子
坑爹的兔子

斐波那契数列的迭代实现
我们都知道兔子繁殖能力是惊人的，如下图：

我们可以用数学函数来定义：

课间练习：假设我们需要求出经历了20个月后，总共有多少对小兔崽子？（迭代 vs 递归）
def fab(n):     n1 = 1     n2 = 1     n3 = 1
if n < 1:         print('输入有误！')         return -1
while (n-2) > 0:         n3 = n2 + n1         n1 = n2         n2 = n3         n -= 1          return n3
result = fab(20) if result != -1:     print('总共有%d对小兔崽子诞生！' % result)

斐波那契数列的递归实现

递归实现（递归计算时间将拉长）
def fab(n):     if n < 1:         print('输入有误！')         return -1
if n == 1 or n == 2:         return 1     else:         return fab(n-1) + fab(n-2)
result = fab(35) if result != -1:     print('总共有%d对小兔崽子诞生！' % result)
注：迭代计算时间远比递归少，因为递归要循环出入栈
024 递归：汉诺塔
递归求解汉诺塔

•对于游戏的玩法，我们可以简单分解为三个步骤
–将前63个盘子从X移动到Y上。
–将最底下的第64个盘子从X移动到Z上。
–将Y上的63个盘子移动到Z上。
•问题一：将X上的63个盘子借助Z移到Y上；
•问题二：将Y上的63个盘子借助X移到Z上。

•对于游戏的玩法，我们可以简单分解为三个步骤
–将前63个盘子从X移动到Y上。
–将最底下的第64个盘子从X移动到Z上。
–将Y上的63个盘子移动到Z上。
•问题一：将X上的63个盘子借助Z移到Y上；
•问题二：将Y上的63个盘子借助X移到Z上。
实例：
def hanoi(n, x, y, z):     if n == 1:         print(x, ' --> ', z)     else:         hanoi(n-1, x, z, y) # 将前n-1个盘子从x移动到y上         print(x, ' --> ', z) # 将最底下的最后一个盘子从x移动到z上         hanoi(n-1, y, x, z) # 将y上的n-1个盘子移动到z上
n = int(input('请输入汉诺塔的层数：')) hanoi(n, 'X', 'Y', 'Z')

025 字典：当索引不好用时
映射

创建和访问字典

>>> dict4 = dict(小甲鱼='让编程改变世界',李宁='一切皆有可能') >>> dict4 {'小甲鱼': '让编程改变世界', '李宁': '一切皆有可能'}
>>> dict4['爱迪生'] = '天才是99%的汗水加1%的灵感' >>> dict4 {'小甲鱼': '让编程改变世界', '李宁': '一切皆有可能', '爱迪生': '天才是99%的汗水加1%的灵感'}
026 字典：当索引不好用时2
fromkey()方法用于创建并返回一个新的字典。它有两个参数，第一个参数是字典的键；第二个参数是可选的，是传入键的值。如果不提供，默认是None
>>> dict1 = {} >>> dict1.fromkeys((1,2,3)) {1: None, 2: None, 3: None} >>> dict2 = {} >>> dict2.fromkeys((1,2,3),"Number") {1: 'Number', 2: 'Number', 3: 'Number'} >>> dict3 = {} >>> dict3.fromkeys((1,2,3),('one','two','three')) {1: ('one', 'two', 'three'), 2: ('one', 'two', 'three'), 3: ('one', 'two', 'three')}
访问字典的方法有key()、values()和items()
key()用于返回字典中的键，value()用于返回字典中所有的值，item()当然就是返回字典中所有的键值对（也就是项）
>>> dict1 = dict1.fromkeys(range(5),'赞') >>> dict1.keys() dict_keys([0, 1, 2, 3, 4]) >>> dict1.values() dict_values(['赞', '赞', '赞', '赞', '赞']) >>> dict1.items() dict_items([(0, '赞'), (1, '赞'), (2, '赞'), (3, '赞'), (4, '赞')])
get()方法提供了更宽松的方式去访问字典项，当键不存在的时候，get()方法并不会报错，只是默默第返回一个None，表示啥都没找到：
>>> dict1.get(10) >>> dict1.get(4) '赞'
如果希望找不到数据时返回指定的值，可以在第二个参数设置对应的默认返回值：
>>> dict1.get(32,'木有') '木有'
如果不知道一个键是否在字典中，可以使用成员资格操作符（in 或 not in）来判断 >>> 31 in dict1 False >>> 4 in dict1
clear()可清空一个字典
>>> dict1 {0: '赞', 1: '赞', 2: '赞', 3: '赞', 4: '赞'} >>> dict1.clear() >>> dict1 {}
copy()方法是复制字典（全拷贝）
>>> a = {1:'one',2:'two',3:'three'} >>> b = a.copy() >>> id(a) 52448840 >>> id(b) 52503624 >>> a[1] = 'four' >>> a {1: 'four', 2: 'two', 3: 'three'} >>> b {1: 'one', 2: 'two', 3: 'three'}
pop()是给定键弹出对应的值，popitem()是随机弹出一个项
>>> a.pop(2) 'two' >>> a {1: 'four', 3: 'three'} >>> a.popitem() (1, 'four') >>> a {3: 'three'}
setdefault()方法与get()方法相似，但setdefault()在字典中找不到相应的键值时会自动添加
>>> a = {1:'one',2:'two',3:'three'} >>> a.setdefault(2) 'two' >>> a.setdefault(4) >>> a {1: 'one', 2: 'two', 3: 'three', 4: None}
update()方法可以更新字典
>>> a = {1:'one','小白':None}
>>> b = {'小白':'狗'} >>> a.update(b) >>> a {1: 'one', '小白': '狗'}
027 集合：在我的世界里，你就是唯一
字典的表亲--集合（在python3中，如果用大括号括起一堆数字但没有体现映射关系，那么就会认为这堆玩意儿就是个集合）
>>> num1 = {} >>> type(num1) <class 'dict'> >>> num2 = {1,3,4} >>> type(num2) <class 'set'>
集合中的元素都是唯一的（集合会自动帮我们把重复的数据清理掉，集合是无序的，所以不能试图去索引集合中的某一个元素）
>>> num = {1,2,3,4,5,5,4,3,2,1} >>> num {1, 2, 3, 4, 5}
如何创建一个集合有两种方法：1、直接把一堆元素用大括号括起来；2、用set()
•一种是直接把一堆元素用花括号括起来
>>> set1 = {'小甲鱼','小鱿鱼','小甲鱼'}
•一种是使用set()工厂函数
>>> set2 = set(['小甲鱼','小鱿鱼','小甲鱼']) >>> set1 == set2 True
课堂搞搞看
要求：去掉列表中重复的元素
[0, 1, 2, 3, 4, 5, 5, 3, 1]
方法一、
>>> list1 = [1,2,3,4,5,5,3,1,0]
>>> temp = list1[:] >>> list1.clear() >>> list1 [] >>> for each in temp:     if each not in list1:         list1.append(each) #append()表示向列表中添加元素
方法二、
>>> list1 = list(set(list1)) >>> list1 [0, 1, 2, 3, 4, 5]
#set(list1)先将list1列表转变为集合， list(set(list1))再讲集合转变为列表
如何访问集合中的值
由于集合中的元素是无序的，所以并不能像序列那样用下标来进行访问，但是可以使用迭代把集合中的数据一个个读取出来
•可以使用for把集合中的数据一个个读取出来
>>> set1 = {1,2,3,4,5,4,3,2,1,0} >>> for each in set1:     print(each,end = ' ')
0 1 2 3 4 5
•也可以通过in和not in判断一个元素是否在集合中已经存在
>>> 0 in set1 True >>> 8 in set1 False
>>> set1.add(6) >>> set1 {0, 1, 2, 3, 4, 5, 6} >>> set1.remove(5) >>> set1 {0, 1, 2, 3, 4, 6}
不可变集合（把元素给froze冰冻起来）(像元组一样不能随意地增加或删除集合中的元素)

028 文件：因为懂你，所以永恒
大多数u程序都是：首先接收输入数据，然后按照要求进行处理，最后输出数据

虽然当前数据放在内存中存取的速度要比硬盘中快，但一旦断电则会丢失，所以尽量ctrl+s保持到硬盘中

什么是文件

打开文件
open(file, mode='r', buffering=-1, encoding=None,errors=None, newline=None, closefd=True, opener=None)
open()的第一个参数是传入的文件名，第二个参数是指定文件的打开模式

文件对象方法
>>> f = open("D:\\python3.3.2\Hello.txt") >>> f <_io.TextIOWrapper name='D:\\python3.3.2\\Hello.txt' mode='r' encoding='cp936'> >>> f.read() "A. HISTORY OF THE SOFTWARE\n==========================\n\nPython was created in the early 1990s by Guido van Rossum at Stichting\nMathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands\nas a successor of a language called ABC.  Guido remains Python's\nprincipal author, although it includes many contributions from others.\n\nIn 1995, Guido continued his work on Python at the Corporation for\nNational Research Initiatives (CNRI, see http://www.cnri.reston.va.us)\nin Reston, Virginia where he released several versions of the\nsoftware." >>> f.close() >>> f = open("D:\\python3.3.2\Hello.txt") >>> f.read(5) 'A. HI' >>> f.tell()   #返回当前光标所在文件的位置 5 >>> f.readline() 'STORY OF THE SOFTWARE\n'将f放入到列表
>>> f = open("D:\\python3.3.2\Hello.txt",'w')#w模式写入会覆盖已存在的文件（即原文件内容全部被删除），a模式则在末尾追加写入 >>> f.write('who are you')          #返回的是写入的字符数 11 >>> f.close()

029 文件：一个任务
•任务：将文件（record.txt）中的数据进行分割并按照以下规律保存起来：
–小甲鱼的对话单独保存为boy_*.txt的文件（去掉“小甲鱼:”）
–小客服的对话单独保存为girl_*.txt的文件（去掉“小客服:”）
–文件中总共有三段对话，分别保存为boy_1.txt, girl_1.txt，boy_2.txt, girl_2.txt, boy_3.txt, gril_3.txt共6个文件（提示：文件中不同的对话间已经使用“==========”分割）
test1：
f = open("record.txt")
boy = [] girl = [] count = 1
for each_line in f:     if each_line[:6] != '======':#判断是否连续读到六个=         (role,line_spoken) = each_line.split(':',1)#split以:进行字符切割，         #将切得到的两部分内容依次存放在role与line_spoken中         if role == '小甲鱼':             boy.append(line_spoken)#将小甲鱼说的内容添加到列表boy中         if role == '小客服':             girl.append(line_spoken)#将小客服说的内容添加到列表girl中     else:         file_name_boy = 'boy_' + str(count) + '.txt'         file_name_girl = 'girl_' + str(count) + '.txt'
boy_file = open(file_name_boy,'w')#以w模式新建一个以file_name_boy命名的txt文件         girl_file = open(file_name_girl,'w')#并贴上boy_file的标签
boy_file.writelines(boy)#将列表boy中的内容写入到boy_file文件中         girl_file.writelines(girl)
boy_file.close()#关闭boy_file文件         girl_file.close()
boy = []#清空列表boy         girl = []         count += 1
file_name_boy = 'boy_' + str(count) + '.txt' file_name_girl = 'girl_' + str(count) + '.txt'
boy_file = open(file_name_boy,'w') girl_file = open(file_name_girl,'w')
boy_file.writelines(boy) girl_file.writelines(girl)
boy_file.close() girl_file.close()#记得关闭文件

test2：

def save_file(boy,girl,count):     file_name_boy = 'boy_' + str(count) + '.txt'     file_name_girl = 'girl_' + str(count) + '.txt'
boy_file = open(file_name_boy,'w')     girl_file = open(file_name_girl,'w')
boy_file.writelines(boy)     girl_file.writelines(girl)
boy_file.close()     girl_file.close()
def split_file(file_name):     f = open(file_name)
boy = []     girl = []     count = 1
for each_line in f:         if each_line[:6] != '======':             (role,line_spoken) = each_line.split(':',1)#split以:进行字符切割，             #将切得到的两部分内容依次存放在role与line_spoken中             if role == '小甲鱼':                 boy.append(line_spoken)             if role == '小客服':                 girl.append(line_spoken)         else:             save_file(boy,girl,count)
boy = []             girl = []             count += 1
save_file(boy,girl,count)     f.close()
split_file('record.txt')
030 文件系统：介绍一个高大上的东西
os模块中关于文件/目录常用的函数使用方法
>>> import os >>> os.getcwd() 'D:\\python3.3.2\\小甲鱼python\\python程序\\第二十九课'
>>> os.listdir('D:\\python3.3.2\\小甲鱼python\\python程序\\第二十九课') ['boy_1.txt', 'boy_2.txt', 'boy_3.txt', 'girl_1.txt', 'girl_2.txt', 'girl_3.txt', 'record.txt', 'test.py', 'test2.py']

os.path模块中关于路径常用的函数使用方法

>>> os.path.getsize('python.exe')  #获取文件的尺寸，返回值以字节为单位
031 永久存储：腌制一缸美味的泡菜(pickle)
python提供了一个标准的模块pickle，可以非常容易地将列表、字典这类复杂的数据类型存储为文件。它几乎可以把所有python的对象都转化为二进制的形式存放，这个过程称为pickling，从二进制转换回对象的过程称为unpickling
pickling过程
>>> import pickle >>> my_list = [123,3,14,'小甲鱼',['another list']]
>>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十节课\my_list.pkl','wb')  #二进制写形式打开文件 >>> pickle.dump(my_list,pickle_file) >>> pickle_file.close()

unpickling过程
>>> import pickle >>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十节课\my_list.pkl','rb')#以二进制读形式打开文件 >>> my_list = pickle.load(pickle_file) >>> print(my_list) [123, 3, 14, '小甲鱼', ['another list']]
实例：城市天气打包

>>> pickle_file = open('D:\\python3.3.2\小甲鱼python\python程序\第三十一节课\city_data.pkl','wb') >>> pickle.dump(city,pickle_file) >>> pickle_file.close()

032 异常处理：你不可能总是对的
实例1：
file_name = input('请输入需要打开的文件名:') file = open(file_name) print('文件的内容是：') for each_line in file:     print(each_line) file.close()
注：py文件与要打开的文件在同一个文件下则不需要加路径
Python标准异常总结

以下是 Python 内置异常类的层次结构：

033 异常处理：你不可能总是对的2
try-except语句
try:
检测范围
except Exception[as reason]:
出现异常（Exception）后的处理代码
实例1：
try:     f = open('TE.txt')     print(f.read())     f.close() except OSError:     print('文件打开过程中出错了！！！')

实例2：
try:     f = open('TE.txt')     print(f.read())     f.close() except OSError as reason:     print('文件打开出错原因是:\n' + str(reason))

实例3：
try:     sum = 1 + '1'     f = open('TE.txt')     print(f.read())     f.close() except OSError as reason:     print('文件打开出错原因是:\n' + str(reason)) except TypeError as reason:     print('类型出错原因是:\n' + str(reason))

实例4（多个异常统一处理）：
try:     sum = 1 + '1'     f = open('TE.txt')     print(f.read())     f.close() except(OSError, TypeError):     print('出错了')

注：try语句一旦检测到异常，剩下的语句将不会被执行
try-finally语句
try:
检测范围
except Exception[as reason]:
出现异常（Exception）后的处理代码
finally:
无论如何都会被执行的代码
实例5：
try:     f = open('test.txt')     print(f.read())     sum = 1 + '1' except (OSError,TypeError)as reason:     print('出错了\n原因是:' + str(reason)) finally:     f.close()
raise语句可以自己抛出一个异常

034 丰富的else语句及简洁的with语句
丰富的else语句
•要么怎样，要么不怎样
if 条件：     条件为真执行 else：     条件为假执行
•干完了能怎样，干不完就别想怎样
实例1：
def showMaxFactor(num):     count = num // 2#//为整除，判断是素数，只需依次判断当前数num除以1到（num // 2）都不能整除即可     while count > 1:         if num % count == 0:#判断是否整除             print('%d最大的约数是%d' % (num, count))             break#跳出循环后else并不执行         count -= 1     else:#当while循环不成立时，或者理解为while循环完全被执行完了，没有给中途跳出（即break）         print('%d是素数！' % num)
num = int(input('请输入一个数：')) showMaxFactor(num)
注：else与for语句搭配与while语句相同
没有问题？那就干
只要try语句块里没有出现任何异常，那么就会执行else语句块里的内容啦
实例2：
try:#尝试运行以下程序     print(int('abc')) except ValueError as reason:#如果程序有异常时     print('出错了：' + str(reason)) else:#程序无异常时     print('没有任何异常！')

实例3：
try:     print(int('123')) except ValueError as reason:     print('出错了：' + str(reason)) else:     print('没有任何异常！')

简洁的with语句（with会自动帮你关闭文件）
实例4：
try:     with open('test.txt','w') as f:         for each_line in f:             print(each_line) except (OSError,TypeError) as reason:     print('出错了\n原因是:' + str(reason))

035 图形用户界面入门：EasyGui
图形用户界面编程，也就是平时常说的GUI（Graphical User  Interface）,python有一个非常简单的GUI工具包：EasyGui
GUI的安装

导入方法一：
>>> import easygui         #导入EasyGui >>> easygui.msgbox('嗨，亦我飞也')

导入方法二：
>>> from easygui import * >>> msgbox('嗨，亦我飞也')

导入方法三（推荐使用）：
>>> import easygui as g >>> g.msgbox('嗨，亦我飞也')

显示图片（注：图片需要为GIF格式，且存放在python.exe通目录）
>>> easygui.buttonbox(msg='你喜欢以下哪种水果',title='亦我飞也',choices=('草莓','西瓜','芒果'),image='aa.gif')

实例1：
import easygui as g import sys   while 1:     g.msgbox("嗨，欢迎进入第一个界面小游戏")     msg = "请问你希望在鱼C工作室学习到什么知识呢"     title="小游戏互动"     choices=["谈恋爱","编程","OOXX","琴棋书画"]     choice=g.choicebox(msg,title,choices)       #note that we convert choice to string,in case     #the user cancelled the choice,and we got None     g.msgbox("你的选择是:"+str(choice),"结果")     msg="你希望重新开始小游戏吗?"     title=" 请选择"     if g.ccbox(msg,title):  #show a Contiue/Cancel dialog         pass #user chose Contonue     else:         sys.exit(0)  #user chose Cancel

修改窗口大小（choicebox）

修改文字大小（PROPORTIONAL_FONT）

036 类和对象：给大家介绍对象
给大家介绍对象
把乱七八糟的数据扔进列表里，称数据层面的封装
把常用的代码段打包成一个函数，称语句层面的封装
把数据和代码都封装在一起，称对象层面的封装

对象 = 属性 + 方法
对象可以从静态（属性）与动态（方法）两个特征来描述

OO（面向对象）的特征

继承
class Turtle: # Python 中的类名约定以大写字母开头     """关于类的一个简单例子"""     # 属性     color = 'green'     weight = 10     legs = 4     shell = True     mouth = '大嘴'
# 方法     def climb(self):         print("我正在很努力的向前爬......")
def run(self):         print("我正在飞快的向前跑......")
def bite(self):         print("咬死你咬死你！！")
def eat(self):         print("有得吃，真满足^_^")
def sleep(self):         print("困了，睡了，晚安，Zzzz")
调用类中的方法：
>>> tt = Turtle()     #声明tt对象继承Turtle() >>> tt.climb() 我正在很努力的向前爬...... >>> tt.bite() 咬死你咬死你！！
定义一个带列表类MyList，将list2对象继承于它，则列表的功能继承它的对象都可以使用
>>> class MyList(list):     pass
>>> list2 = MyList()
>>> list2.append(5) >>> list2.append(6)
>>> list2.append(1) >>> list2 [5, 6, 1] >>> list2.sort() >>> list2 [1, 5, 6]
多态（下例中都调用的名字相同的方法，但实现不一样）
>>> class A:     def fun(self):         print('我是小A。。。')
>>> class B:     def fun(self):         print('我是小B。。。')
>>> a = A() >>> b = B() >>> a.fun() 我是小A。。。 >>> b.fun() 我是小B。。。
037 类和对象：面向对象编程
self是什么？
Python的self其实就相当于C++的this指针。由同一个类可以生产无数对象，当一个对象的方法被调用的时候，对象会将自身的引用作为第一个参数传给该方法，那么python就知道需要操作哪个对象的方法了。
>>> class Ball:     def setName(self,name):         self.name = name     def kick(self):         print('我叫%s,该死的，谁踢我。。。' % self.name)
>>> a = Ball()
>>> a.setName('球A') >>> b = Ball()
>>> b.setName('球B')
>>> a.kick() 我叫球A,该死的，谁踢我。。。 >>> b.kick() 我叫球B,该死的，谁踢我。。。
你听说过Python的魔法方法吗？
python的这些具有魔法的方法，总是被双下划线所包围，例如__init__()，即构造方法，也称构造函数，这个方法会在对象被创建时自动调用。其实，实例化对象时是可以传入参数的，这些参数会自动传入__init__()方法中，可以通过重写这个方法来自定义对象的初始化操作
实例：
>>> class Ball():     def __init__(self,name):         self.name = name     def kick(self):         print('我叫%s,该死的，谁踢我。。。' % self.name)
>>> b = Ball('小土豆') >>> b.kick() 我叫小土豆,该死的，谁踢我。。。
公有和私有？python内部采用了一种叫 name mangling(名字改编)的技术
默认上对象的属性和方法都是公开的，可以直接通过点操作符（.）进行访问：
>>> class Person:     name = '亦我飞也'
>>> p = Person() >>> p.name '亦我飞也'
为了实现定义私有变量，只需要在变量名或函数名前加上"__"两个下划线，那么这个函数或变量就会变成私有的了：
私有变量不可以直接由外部访问
>>> class Person:     __name = '亦我飞也'
>>> p = Person() >>> p.__nameTraceback (most recent call last):   File "<pyshell#65>", line 1, in <module>     p.__name AttributeError: 'Person' object has no attribute '__name'
室友变量可以由内部（内部函数）进行访问
>>> class Person:     __name = '亦我飞也'     def getName(self):         return self.__name
>>> p = Person() >>> p.__nameTraceback (most recent call last):   File "<pyshell#72>", line 1, in <module>     p.__name AttributeError: 'Person' object has no attribute '__name' >>> p.getName() '亦我飞也'
其实，name mangling(名字改编)技术，只是把双下划线开头的变量进行了改名而已。实际上在外部使用“_类名__变量名“即可访问双下划线开头的私有变量了：
>>> p._Person__name '亦我飞也'
038 类和对象：继承
继承
子类                              父类
class DerivedClassName(BaseClassName):
……
实例：一个子类可以继承它的父类的所有属性和方法
>>> class Parent:     def hello(self):         print('正在调用父类的方法。。。')
>>> class Child(Parent):    #子类继承父类     pass     #直接往下执行
>>> p = Parent() >>> p.hello() 正在调用父类的方法。。。 >>> c = Child() >>> c.hello() 正在调用父类的方法。。。
如果子类中定义与父类同名的方法或属性，则会自动覆盖父类对应的方法和属性（即子类方法属性改变，父类是不变的）
>>> class Child(Parent):     def hello(self):         print('正在调用子类的方法')
>>> c = Child() >>> c.hello() 正在调用子类的方法 >>> p.hello() 正在调用父类的方法。。。
实例2：
import random as r class Fish:     def __init__(self):         self.x = r.randint(0,10)         self.y = r.randint(0,10)
def move(self):         self.x -= 1         print('我的位置是：',self.x,self.y)
class Goldfish(Fish):     pass
class Garp(Fish):     pass
class Shark(Fish):     def __init__(self):         self.hungry = True
def eat(self):         if self.hungry:             print('吃货的梦想就是天天有的吃')             self.hungry = False         else:             print('太撑了，吃不下了！')
>>> fish = Fish() >>> fish.move() 我的位置是： -1 10 >>> fish.move() 我的位置是： -2 10 >>> goldfish = Goldfish() >>> goldfish.move() 我的位置是： 2 3 >>> goldfish.move() 我的位置是： 1 3 >>> shark = Shark() >>> shark.eat() 吃货的梦想就是天天有的吃 >>> shark.eat() 太撑了，吃不下了！ >>> shark.move()    #报错原因时因为子类重写构造函数，覆盖了父类D的构造函数Traceback (most recent call last):   File "<pyshell#9>", line 1, in <module>     shark.move()   File "D:\python3.3.2\小甲鱼python\python程序\第三十八节课\fish.py", line 8, in move     self.x -= 1 AttributeError: 'Shark' object has no attribute 'x'
注：继承父类属性的子类，其变量值只属于当前子类，是子类的局部变量
报错修改部分解决方法一：调用未绑定的父类方法

>>> shark = Shark() >>> shark.move() 我的位置是： 2 1 >>> shark.move() 我的位置是： 1 1
报错修改部分解决方法二：使用super函数（super函数会帮我们自动找到基类的方法，而且还自动为我们传入self参数）

>>> shark = Shark() >>> shark.move() 我的位置是： 1 1 >>> shark.move() 我的位置是： 0 1
多重继承
class DerivedClassName(Base1, Base2, Base3):
……
实例：子类c同时继承基类Base1和基类Base2
>>> class Base1:     def fool1(self):         print('我是fool1,我为Base1代言。。。')
>>> class Base2:     def fool2(self):         print('我是fool2,我为Base2代言。。。')
>>> class C(Base1,Base2):     pass
>>> c = C() >>> c.fool1() 我是fool1,我为Base1代言。。。 >>> c.fool2() 我是fool2,我为Base2代言。。。
039 类和对象：拾遗
组合（将需要的类一起进行实例化并放入新的类中）
实例：
class Turtle:     def __init__(self,x):         self.num = x
class Fish:     def __init__(self,x):         self.num = x
class Pool:     def __init__(self,x,y):         self.turtle = Turtle(x)         self.fish = Fish(y)
def print_num(self):         print('水池里一共有乌龟 %d 条,鱼 %d 条' % (self.turtle.num,self.fish.num))
>>> pool = Pool(5,2) >>> pool.print_num() 水池里一共有乌龟 5 条,鱼 2 条
•现在要求定义一个类，叫水池，水池里要有乌龟和鱼。
类、类对象和实例对象
以下例子可见，对实例对象c的count属性赋值后，就相当于覆盖了类对象C的count属性。如果没有赋值覆盖，那么引用的是类对象的count属性
>>> a = C() >>> b = C() >>> c = C() >>> print(a.count,b.count,c.count) 0 0 0 >>> c.count += 10 >>> print(a.count,b.count,c.count) 0 0 10 >>> C.count += 100 >>> print(a.count,b.count,c.count) 100 100 10
另外，如果属性的名字跟方法名相同，属性会覆盖方法：
>>> class C:     def x(self):         print('X-man')
>>> c = C() >>> c.x() X-man >>> c.x = 1              #新定义对象c的一个x属性，并赋值为1 >>> c.x 1 >>> c.x()     #可见，方法x()已经被属性x给覆盖了Traceback (most recent call last):   File "<pyshell#8>", line 1, in <module>     c.x() TypeError: 'int' object is not callable
结论：不要试图在一个类里边定义出所有能想到的特性和方法，应该利用继承和组合机制来进行扩展；用不同的词性命名，如属性名用名词、方法名用动词，并使用骆驼命名法等。
到底什么是绑定？
实例1：（python严格要求需要有实例才能被调用，即绑定概念）
>>> class BB:     def printBB():        #缺少self，导致无法绑定具体对象         print('no zuo no die')
>>> BB.printBB() no zuo no die >>> bb = BB() >>> bb.printBB()        #出现错误原因是由于绑定机制，自动把bb对象作为第一个参数传入Traceback (most recent call last):   File "<pyshell#15>", line 1, in <module>     bb.printBB() TypeError: printBB() takes 0 positional arguments but 1 was given
•Python严格要求方法需要有实例才能被调用，这种限制其实就是Python所谓的绑定概念。
040 类和对象：一些相关的BIF
一些相关的BIF
issubclass(class, classinfo)  如果第一个参数（class）是第二个参数（classinfo）的一个子类，则返回True，否则返回False
>>> class A:     pass
>>> class B(A):     pass
>>> issubclass(B,A) True >>> issubclass(B,B)   #一个类被认为是其自身的子类 True >>> issubclass(B,object)      # object是所有类的基类 True >>> class C:     pass
>>> issubclass(B,C) False
isinstance(object, classinfo)  如果第一个参数（object）是第二个参数（classinfo）的实例对象，则返回True，否则返回False
>>> issubclass(B,C)       注：第一个参数如果不是对象，则永远返回False False >>> b1 = B() >>> isinstance(b1,B) True >>> isinstance(b1,C) False >>> isinstance(b1,A) True >>> isinstance(b1,(A,B,C)) True
hasattr(object, name)  用来测试一个对象里是否有指定的属性，第一个参数（object）是对象，第二个参数（name）是属性名（属性的字符串名字）
>>> class C:     def __init__(self,x=0):         self.x = x
>>> c1 = C() >>> hasattr(c1,'x')    #注意，属性名要用引号括起来True

getattr(object, name[, default])  返回对象指定的属性值，如果指定的属性不存在，则返回default(可选参数);若没有设置default参数，则抛出异常
>>> getattr(c1,'x') 0 >>> getattr(c1,'y')Traceback (most recent call last):   File "<pyshell#25>", line 1, in <module>     getattr(c1,'y') AttributeError: 'C' object has no attribute 'y'
setattr(object, name, value)  可以设置对象中指定属性的值，如果指定的属性不存在，则会新建属性并赋值 >>> setattr(c1,'y','FishC') >>> getattr(c1,'y') 'FishC'
delattr(object, name)  用于删除对象中指定的属性，如果属性不存在，抛出异常。
>>> delattr(c1,'y') >>> delattr(c1,'Z')Traceback (most recent call last):   File "<pyshell#30>", line 1, in <module>     delattr(c1,'Z') AttributeError: Z
property(fget=None, fset=None, fdel=None, doc=None)  用来通过属性设置属性，第一个参数是获取属性的方法名，第二个参数是设置属性的方法名，第三个参数是删除属性的方法名
>>> class C:     def __init__(self,size =10):         self.size = size     def getSize(self):         return self.size     def setSize(self,value):         self.size = value     def delSize(self):         del self.size     x=property(getSize,setSize,delSize)

>>> c = C() >>> c.x         #调用getSize() 10 >>> c.x = 12      #调用SetSize() >>> c.x 12 >>> c.size 12 >>> del c.x      #调用DelSize() >>> c.sizeTraceback (most recent call last):   File "<pyshell#53>", line 1, in <module>     c.size AttributeError: 'C' object has no attribute 'size'
041 魔法方法：构造和析构

__init__(self[, ...]) 方法是类在实例化成对象的时候首先会调用的一个方法
>>> class Rectangle:     def __init__(self,x,y):         self.x = x         self.y = y     def getPeri(self):         return (self.x + self.y) * 2     def getArea(self):         return self.x * self.y>>> rect = Rectangle(5,2) >>> rect.getPeri() 14 >>> rect.getArea() 10
注：__init__()方法的返回值一定是None
其实，__new__()才是在一个对象实例化时候所调用的第一个方法，它的第一个参数是这个类（cla）,而其他的参数会直接传递给__init__()方法
__new__(cls[, ...])
>>> class CapStr(str):     def __new__(cls,string):         string = string.upper()         return str.__new__(cls,string)
>>> a = CapStr('hello world') >>> a 'HELLO WORLD
__del__(self)  当对象将要被销毁的时候，这个方法就会被调用。但要注意，并非del x就相当于调用x.__del__()，__del__()方法是当垃圾回收机制回收这个对象的时候才调用的。
>>> class C:     def __init__(self):         print('我是__init__方法，我被调用了...')     def __del__(self):         print('我是__del__方法，我被调用l...')
>>> c1 = C()     #创建对象c1 我是__init__方法，我被调用了... >>> c2 = c1 >>> c3 = c2 >>> del c1 >>> del c2 >>> del c3   #删除c3时，对象c1才会彻底被删除（即没有标签指向对象c1时，其才会被回收） 我是__del__方法，我被调用l...
042 魔法方法：算术运算
python2.2以后，对类和类型进行了统一，做法就是讲int()、float()、str()、list()、tuple()这些BIF转换为工厂函数（类对象）：
>>> type(len) <class 'builtin_function_or_method'>            #普通的BIF >>> type(int) <class 'type'>             #工厂函数（类对象），当调用它们的时候，其实就是创建了一个相应的实例对象 >>> type(dir) <class 'builtin_function_or_method'> >>> type(list) <class 'type'>
>>> a = int('123')        #创建一个相应的实例对象a >>> b = int('345') >>> a + b              #python在两个对象进行相加操作 468

举个例子，下面定义一个比较特立独行的类：
>>> class New_int(int):     def __add__(self,other):         return int.__sub__(self,other)     def __sub__(self,other):         return int.__add__(self,other)
>>> a = New_int(3) >>> b = New_int(5) >>> a + b    #两个对象相加，触发 __add__(self,other)方法 -2 >>> a - b 8 >>>
实例2：
>>> class New_int(int):     def __add__(self,other):         return (int(self) + int(other))       #将self与other强制转换为整型，所以不会出现两个对象相加触发__add__()方法     def __sub__(self,other):         return (int(self) - int(other))
>>> a = New_int(3) >>> b = New_int(5) >>> a + b 8
043 魔法方法：算术运算2
实例1：
>>> class int(int):     def __add__(self,other):         return int.__sub__(self,other)
>>> a = int(3) >>> b = int(2) >>> a + b 1
反运算：

>>> a + b
实例：
>>> class Nint(int):     def __radd__(self,other):         return int.__sub__(self,other)
>>> a = Nint(5) >>> b = Nint(3) >>> a + b      #由于a对象默认有__add__()方法，所以b的__radd__()没有执行 8
实例2：
>>> class Nint(int):     def __radd__(self,other):         return int.__sub__(self,other)
>>> b = Nint(5) >>> 3 + b         #由于3无__add__()方法，所以执行b的反运算__radd__(self,other)方法，其中self是b对象 2
注：在重写反运算魔法方法时，一定要注意顺序问题。
增量赋值运算：

比较操作符：

其它操作符：

044 魔法方法：简单定制
简单定制
•基本要求：
–定制一个计时器的类
–start和stop方法代表启动计时和停止计时
–假设计时器对象t1，print(t1)和直接调用t1均显示结果
–当计时器未启动或已经停止计时，调用stop方法会给予温馨的提示
–两个计时器对象可以进行相加：t1 + t2
–只能使用提供的有限资源完成
你需要这些资源
•使用time模块的localtime方法获取时间
–扩展阅读：time 模块详解（时间获取和转换）
有关time模块的localtime方法获取时间（参考：
•time.localtime返回struct_time的时间格式
•表现你的类：__str__ 和 __repr__
实例：
import time as t   #导入时间模块，调用对象t
class Mytimer():     def __init__(self):         self.unit = ['年','月','天','小时','分钟','秒']         self.prompt = "未开始计时"         self.lasted = []         self.begin = 0  #属性         self.end = 0     def __str__(self):         return self.prompt
__repr__ = __str__
def __add__(self,other):   #重写加法操作符，运行时间相加         prompt = "总共运行了"         result = []         for index in range(6):             result.append(self.lasted[index] + other.lasted[index])             if result[index]:                 prompt += (str(result[index]) + self.unit[index])         return prompt                                 #开始计时     def start(self):    #方法，属性名和方法名不能相同         if not self.stop:             self.prompt = ("提示：请先调用stop()停止计时！")         else:             self.begin = t.localtime()             print('计时开始...')
#停止计时     def stop(self):         if not self.begin:             print('提示：请先调用start()进行计时！')         else:             self.end = t.localtime()             self._calc()             print('计时结束！')
#内部方法，计算运行时间     def _calc(self):         self.prompt = "总共运行了"         for index in range(6):             self.lasted.append(self.end[index] - self.begin[index])             if self.lasted[index]:                 self.prompt += (str(self.lasted[index]) + self.unit[index])         #为下一轮计时初始化变量         self.begin = 0         self.end = 0
>>> t1 = Mytimer() >>> t1.stop() 提示：请先调用start()进行计时！ >>> t1.start() 计时开始... >>> t1.stop() 计时结束！ >>> t1 总共运行了4秒 >>> t2 = Mytimer() >>> t2.start() 计时开始... >>> t2.stop() 计时结束！ >>> t2 总共运行了4秒 >>> t1 + t2 '总共运行了8秒'
进阶定制
•如果开始计时的时间是（2022年2月22日16:30:30），停止时间是（2025年1月23日15:30:30），那按照我们用停止时间减开始时间的计算方式就会出现负数（3年-1月1天-1小时），你应该对此做一些转换。
•现在的计算机速度都非常快，而我们这个程序最小的计算单位却只是秒，精度是远远不够的。
045 魔法方法：属性访问
属性访问
•__getattr__(self, name)
–定义当用户试图获取一个不存在的属性时的行为
•__getattribute__(self, name)
–定义当该类的属性被访问时的行为
•__setattr__(self, name, value)
–定义当一个属性被设置时的行为
•__delattr__(self, name)
–定义当一个属性被删除时的行为
实例1：
class C:     def __getattribute__(self, name):         print('getattribute')         # 使用 super() 调用 object 基类的 __getattribute__ 方法         return super().__getattribute__(name)
def __setattr__(self, name, value):         print('setattr')         super().__setattr__(name, value)
def __delattr__(self, name):         print('delattr')         super().__delattr__(name)
def __getattr__(self, name):         print('getattr')
>>> c = C() >>> c.x getattribute getattr >>> c.x = 1 setattr >>> c.x getattribute 1 >>> del c.x delattr >>> setattr(c,'y','Yellow') setattr
练习要求
•写一个矩形类，默认有宽和高两个属性；
•如果为一个叫square的属性赋值，那么说明这是一个正方形，值就是正方形的边长，此时宽和高都应该等于边长。
实例2：
class Rectangle:     def __init__(self, width=0, height=0):         self.width = width         self.height = height
def __setattr__(self, name, value):#一发生赋值操作，则会触发__setattr__()魔法方法         if name == 'square':#判断name属性是否为正方形             self.width = value             self.height = value         else:             self.__dict__[name] = value
def getArea(self):         return self.width * self.height
>>> r1 = Rectangle(4,5) >>> r1.getArea() 20 >>> r1.square = 10 >>> r1.getArea() 100
046 魔法方法：描述符（Property的原理）
描述符
•描述符就是将某种特殊类型的类的实例指派给另一个类的属性。
•__get__(self, instance, owner)
–用于访问属性，它返回属性的值
•__set__(self, instance, value)
–将在属性分配操作中调用，不返回任何内容
•__delete__(self, instance)
–控制删除操作，不返回任何内容
实例：
>>> class MyDecriptor:     def __get__(self,instance,owner):         print("getting...",self,instance,owner)     def __set__(self,instance,value):         print("setting...",self,instance,value)     def __delete__(self,instance):         print("deleting...",self,instance)

>>> class Test:     x = MyDecriptor()   #取Mydecriptor类的实例指派给Test类的属性x
>>> test = Test() >>> test.x getting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98> <class '__main__.Test'> >>> test <__main__.Test object at 0x000000000335EF98> >>> test.x = "X-man" setting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98> X-man >>> del test.x deleting... <__main__.MyDecriptor object at 0x00000000033467F0> <__main__.Test object at 0x000000000335EF98>

实例2：
>>> class MyProperty:     def __init__(self,fget = None,fset = None,fdel = None):         self.fget = fget         self.fset = fset         self.fdel = fdel     def __get__(self,instance,owner):         return self.fget(instance)     def __set__(self,instance,value):         self.fset(instance,value)     def __delete__(self,instance):         self.fdel(instance)
>>> class C:     def __init__(self):         self._x = None     def getX(self):         return self._x     def setX(self,value):         self._x = value     def delX(self):         del self._x     x = MyProperty(getX,setX,delX)
>>> c = C() >>> c.x = "HELLOW" >>> c.x 'HELLOW' >>> c._x 'HELLOW' >>> del c.x >>> c._xTraceback (most recent call last):   File "<pyshell#70>", line 1, in <module>     c._x AttributeError: 'C' object has no attribute '_x'
练习要求
•先定义一个温度类，然后定义两个描述符类用于描述摄氏度和华氏度两个属性。
•要求两个属性会自动进行转换，也就是说你可以给摄氏度这个属性赋值，然后打印的华氏度属性是自动转换后的结果。
实例3：
ss Celsius:  #摄氏度描述符类     def __init__(self,value = 26.0):#self为描述符类自身(此为摄氏度描述符类)的实例（此为cel)         self.value = float(value)     def __get__(self,instance,owner):#instance是这个描述符的拥有者所在的类的实例（此为temp)         return self.value     def __set__(self,instance,value):#owner是这个描述符的拥有者所在的类本身(此为温度类）         self.value = float(value)
class Fahrenheit:   #华氏度描述符类     def __get__(self,instance,owner):         return instance.cel * 1.8 +32  #摄氏度转华氏度     def __set__(self,instance,value):         instance.cel = ((float)(value)- 32)/ 1.8   #华氏度转摄氏度          class Temperature:   #温度类     cel = Celsius()   #设置摄氏度属性（描述符类的实例指派给了温度类的属性）     fah = Fahrenheit()#设置华氏度属性
>>> temp = Temperature() >>> temp.cel 26.0 >>> temp.fah 78.80000000000001 >>> temp.fah = 78.8 >>> temp.cel 25.999999999999996
047 魔法方法：定制序列
协议是什么？
•协议（Protocols）与其他编程语言中的接口很相似，它规定你哪些方法必须要定义。然而，在Python中的协议就显得不那么正式。事实上，在Python中，协议更像是一种指南。
容器类型的协议
•如果说你希望定制的容器是不可变的话，你只需要定义__len__()和__getitem__()方法。
•如果你希望定制的容器是可变的话，除了__len__()和__getitem__()方法，你还需要定义__setitem__()和__delitem__()两个方法。
练习要求
•编写一个不可改变的自定义列表，要求记录列表中每个元素被访问的次数。
class CountList:  #定义记录列表中每个元素访问次数类     def __init__(self,*args): #参数是可变类型的         self.values = [x for x in args]#将args的数据存入列表self.values中         self.count = {}.fromkeys(range(len(self.values)),0)#创建字典，初试化为0
def __len__(self):  #返回容器中元素的个数         return len(self.values)#len方法用于返回参数的长度      def __getitem__(self,key):  #获取容器中指定元素的行为，key为访问对应的键         self.count[key] += 1#每访问一次，字典键对应的键值加1         return self.values[key]
>>> c1 = CountList(1,3,5,7,9) >>> c2 = CountList(2,4,6,8,10) >>> c1[1]  #c1[1]第一次访问 3 >>> c2[2] 6 >>> c1[1] + c2[2] #c1[1]第二次访问 9 >>> c1.count {0: 0, 1: 2, 2: 0, 3: 0, 4: 0} >>> c2.count {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}
048 魔法方法：迭代器
迭代的意思类似于循环，每一次重复的过程被称为一次迭代的过程，而每一次迭代得到的结果会被用来作为下一次迭代的初始值。提供迭代方法的容器称为迭代器（如序列（列表、元组、字符串）、字典等）。
对一个容器对象调用iter()就得到它的迭代器，调用next()迭代器就会返回下一个值。入托迭代器没有值可以返回了，就会抛出异常。
•iter()
–__iter__()
•next()
–__next__()
实例1：
>>> string = "FishC" >>> it = iter(string) >>> next(it) 'F' >>> next(it) 'i' >>> next(it) 's' >>> next(it) 'h' >>> next(it) 'C' >>> next(it)Traceback (most recent call last):   File "<pyshell#8>", line 1, in <module>     next(it) StopIteration
一个容器如果是迭代器，那就必须实现__iter__()魔法方法，这个方法实际上就是返回迭代器本身。重点要实现的是__next__()魔法方法，因为它决定了迭代的规则。
实例2：
>>> class Fibs:     def __init__(self):         self.a = 0         self.b = 1     def __iter__(self):         return self     def __next__(self):         self.a,self.b = self.b,self.a + self.b         return self.a
>>> fibs = Fibs() >>> for each in fibs:     if each < 20:         print(each)     else:         break
1 1 2 3 5 8 13
实例3：

>>> class Fibs:     def __init__(self,n =20):         self.a = 0         self.b = 1         self.n = n     def __iter__(self):         return self          def __next__(self):         self.a,self.b = self.b,self.a + self.b         if self.a > self.n:             raise StopIteration         return self.a
>>> fibs = Fibs() >>> for each in fibs:     print(each)
1 1 2 3 5 8 13

>>> fibs = Fibs(10) >>> for each in fibs:     print(each)
1 1 2 3 5 8

049 乱入：生成器
所谓协同程序，就是可以运行的独立函数调用，函数可以暂停或者挂起，并在需要的时候从程序离开的地方继续或者重新开始。
生成器可以暂时挂起函数，并保留函数的局部变量等数据，然后在再次调用它的时候，从上次暂停的位置继续执行下去。
一个函数中如果有yield语句，则被定义为生成器。
实例1：
>>> def myGen():     print("生成器被执行了！")     yield 1   #暂停一次，相当于return，返回1     yield 2     #暂停一次，相当于return，返回2
>>> myG = myGen() >>> next(myG) 生成器被执行了！ 1 >>> next(myG) 2
像前面介绍的斐波那契的例子，也可以用生成器来实现：
>>> def fibs():     a = 0     b = 1     while True:         a,b = b,a + b         yield a
>>> for each in fibs():     if each > 100:         break     print(each)
1 1 2 3 5 8 13 21 34 55 89
列表推导式表达：
100以内，能被2整除，但不能被3整除的所有整数
>>> a = [i for i in range(100) if not (i % 2) and (i % 3 )] >>> a [2, 4, 8, 10, 14, 16, 20, 22, 26, 28, 32, 34, 38, 40, 44, 46, 50, 52, 56, 58, 62, 64, 68, 70, 74, 76, 80, 82, 86, 88, 92, 94, 98]
字典推导式：
10以内是否为偶数
>>> a = {i:i % 2 == 0 for i in range(10)} >>> a {0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}
集合推导式：
>>> a = {i for i in [1,2,3,3,4,5,5,5,6,7,7,8]} >>> a {1, 2, 3, 4, 5, 6, 7, 8}
元组生成器推导式：
>>> e = (i for i in range(5)) >>> next(e) 0 >>> next(e) 1 >>> next(e) 2
050 模块：模块就是程序
什么是模块
•容器 -> 数据的封装
•函数 -> 语句的封装
•类 -> 方法和属性的封装
•模块 -> 模块就是程序
命名空间
爱的宣言：世界上只有一个名字，使我这样牵肠挂肚，像有一根看不见的线，一头牢牢系在我心尖上，一头攥在你手中，这个名字就叫做鱼C工作室计算机一班的小花……
导入模块
•第一种：import 模块名
实例1：import导入模块

实例2：import导入模块

•第二种：from 模块名 import 函数名(不推荐使用)

•第三种：import 模块名 as 新名字（推荐使用）
TemperatureConversion文件：
def c2f(cal):     return cal * 1.8 + 32 def f2c(fah):     return (fah - 32)/1.8
calc文件：
import TemperatureConversion as tc  #tc为取得新名字
print("32摄氏度 = %.2f 华氏度\n" % tc.c2f(32)) print("99华氏度 = %.2f 摄氏度" % tc.f2c(99))

051 模块：__name__='__main__'、搜索路径和包
模块！模块！
实例1：为TemperatureConversion添加测试程序（TemperatureConversion被作为程序运行）
def c2f(cal):     return cal * 1.8 + 32
def f2c(fah):     return (fah - 32)/1.8
def test():     print("0摄氏度 = %.2f 华氏度\n" % c2f(0))     print("0华氏度 = %.2f 摄氏度" % f2c(0))
test()

运行calc文

当希望TemperatureConversion被调用时作为模块导入时
def c2f(cal):     return cal * 1.8 + 32
def f2c(fah):     return (fah - 32)/1.8
def test():     print("0摄氏度 = %.2f 华氏度" % c2f(0))     print("0华氏度 = %.2f 摄氏度" % f2c(0))
if __name__ == "__main__":#当此文件当做程序运行时，执行test()，否则不执行     test()
运行calc文件

•if __name__ == ‘__main__’
•搜索路径（系统会首先搜索的路径）
>>> import sys >>> sys.path ['D:\\python3.3.2\\小甲鱼python\\python程序\\第五十节课\\Temperature', 'D:\\python3.3.2\\Lib\\idlelib', 'C:\\windows\\system32\\python33.zip', 'D:\\python3.3.2\\DLLs', 'D:\\python3.3.2\\lib', 'D:\\python3.3.2', 'D:\\python3.3.2\\lib\\site-packages']
添加搜索路径：
>>> import TemperatureConversionTraceback (most recent call last):   File "<pyshell#0>", line 1, in <module>     import TemperatureConversion ImportError: No module named 'TemperatureConversion'
>>> import sys >>> sys.path.append("D:\\python3.3.2\WODE\Temperature") >>> sys.path ['', 'D:\\python3.3.2\\Lib\\idlelib', 'C:\\windows\\system32\\python33.zip', 'D:\\python3.3.2\\DLLs', 'D:\\python3.3.2\\lib', 'D:\\python3.3.2', 'D:\\python3.3.2\\lib\\site-packages', 'D:\\python3.3.2\\WODE\\Temperature'] >>> import TemperatureConversion >>> TemperatureConversion.f2c(59) 15.0
•包（package）
1.创建一个文件夹，用于存放相关的模块，文件夹的名字即包的名字；
2.在文件夹中创建一个__init__.py的模块文件，内容可以为空；
3.将相关的模块放入文件夹中

052 模块：像个极客一样去思考
使用print调用__doc__属性，可以带格式查看这个模块的简介

使用dir()可以查询到该模块定义了哪些变量、函数和类
053 论一只爬虫的自我修养

Python如何访问互联网？

•URL的一般格式为（带方括号[]的为可选项）：
protocol :// hostname[:port] / path / [;parameters][?query]#fragment
•URL由三部分组成：
–第一部分是协议：http，https，ftp，file，ed2k…
–第二部分是存放资源的服务器的域名系统或IP地址（有时候要包含端口号，各种传输协议都有默认的端口号，如http的默认端口为80）。
–第三部分是资源的具体地址，如目录或文件名等。
054 论一只爬虫的自我修养2：实战
import urllib.request
response = urllib.request.urlopen('http://placekitten.com/g/500/600')#  返回文件对象response cat_imag = response.read()
with open('cat_500_600.jpg','wb') as f:     f.write(cat_imag)

>>> response.geturl() 'http://placekitten.com/g/500/600' >>> response.info() <http.client.HTTPMessage object at 0x00000000034EAA20> >>> print(response.info()) Date: Sat, 27 Jul 2019 02:44:18 GMT Content-Type: image/jpeg Transfer-Encoding: chunked Connection: close Set-Cookie: __cfduid=d3cd08233581619b9ef8464ae93f7d5ff1564195458; expires=Sun, 26-Jul-20 02:44:18 GMT; path=/; domain=.placekitten.com; HttpOnly Access-Control-Allow-Origin: * Cache-Control: public, max-age=86400 Expires: Sun, 28 Jul 2019 02:44:18 GMT CF-Cache-Status: HIT Age: 66459 Vary: Accept-Encoding Server: cloudflare CF-RAY: 4fcb454ecc35ce6b-LHR
>>> response.getcode() 200
055 论一只爬虫的自我修养3：隐藏
•
代理
•步骤：
1. 参数是一个字典 {‘类型’:‘代理ip:端口号’}
proxy_support = urllib.request.ProxyHandler({})

2. 定制、创建一个 opener
opener = urllib.request.build_opener(proxy_support)

3a. 安装 opener
urllib.request.install_opener(opener)
3b. 调用 opener
opener.open(url)

064 GUI的终极选择：Tkinter

>>> import tkinter   #Tkinter是python默认的GUI库，导入Tkinter模块 >>>
实例1：
import tkinter as tk
root = tk.Tk()#创建一个主窗口，用于容纳整个GUI程序 root.title("FishC Demo")#设置主窗口对象的标题栏
#添加一个Label组件，可以显示文本、图标或者图片（此处显示文本） theLabel = tk.Label(root,text = "我的第二个窗口程序") theLabel.pack()#调用Label组件的pack方法，用于自动调节组件自身尺寸
root.mainloop()#执行此语句后，窗口才会显示，程序进入主事件循环

实例2：
import tkinter as tk
class App:#创建类App     def __init__(self,root):#self为指向App类的指针         #创建一个框架，然后在里面添加一个Button按钮组件，框架用来将复杂布局中按钮分组         frame = tk.Frame(root)         frame.pack(side = tk.RIGHT,padx = 10,pady = 10)#调节框架自身尺寸,此处设置为右对齐（右上角为原点），偏移（10,10)                  #创建一个按钮组件，fg(foreground)，设置前景色         #创建一个Button按钮,属性为self.hi_there，属于frame框架，按钮按下时调用self.say_hi方法         #设置前景色为黑色，背景色为白色         self.hi_there = tk.Button(frame,text = "打招呼",bg = "black",fg = "white",command = self.say_hi)         self.hi_there.pack()#自动调节自身尺寸                  #say_hi()方法定义实现        def say_hi(self):         print("互联网广大朋友们好，我是亦我飞也！")                   root = tk.Tk()#创建一个主窗口(toplever的根窗口)，并把它作为参数实例化app对象，用于容纳整个GUI程序, app = App(root)#创建类App的一个实例对象app，传入参数为root
app.mainloop()#执行此语句后，窗口才会显示，程序进入主事件循环

065 GUI的终极选择：Tkinter2
实例1：Label组件显示文字与gif图片
#导入tkinter模块的所有内容 from tkinter import *
#创建主窗口 root = Tk() #创建一个文本Label对象,文字为左对齐，离左边边框距离为10 textLabel = Label(root,                   text = "您下载的影片含有未成年人限制内容，\n请满18周岁后再点击观看！",                   justify = LEFT,padx = 10) #Label组件为左对齐 textLabel.pack(side = LEFT)
#创建一个图像Label对象 #用PhotoImage实例化一个图片对象（支持gif格式的图片） photo = PhotoImage(file = "18.gif") imgLabel = Label(root,image = photo) imgLabel.pack(side = RIGHT)
mainloop()

实例2：
例2：文字显示在图片上
#导入tkinter模块的所有内容 from tkinter import *
#创建主窗口 root = Tk()
#创建一个图像Label对象 photo = PhotoImage(file = "bg.gif") #创建一个文本Label对象 textLabel = Label(root,                   text = "学Python\n到FishC！",                   font = ("宋体",20),                   fg = "white",                   justify = LEFT,  #文字左对齐                   image = photo,                   compound = CENTER, #设置文本和图像的混合模式                   ) #文本Label对象偏移，离左窗口与上窗口都为10 textLabel.pack(side = LEFT,padx =10,pady =10)
mainloop()

实例2：Button组件
#导入tkinter模块的所有内容 from tkinter import *
def callback():     var.set("吹吧你，我才不信呢~")
#创建主窗口 root = Tk() #设置主窗口对象的标题栏 root.title("TK")
frame1 = Frame(root)#框架1 frame2 = Frame(root)#框架2
#创建一个文本Label对象,文字为左对齐 var = StringVar() var.set("您下载的影片含有未成年人限制内容，\n请满18周岁后再点击观看！") textLabel = Label(frame1,                   textvariable = var, #Button显示一个StringVar的变量                   justify = LEFT) #Label组件为左对齐 textLabel.pack(side = LEFT)
#创建一个图像Label对象 #用PhotoImage实例化一个图片对象（支持gif格式的图片） photo = PhotoImage(file = "18.gif") imgLabel = Label(root,image = photo) imgLabel.pack(side = RIGHT)
#加一个按钮 theButton = Button(frame2,text = "已满18周岁",command = callback) theButton.pack() frame1.pack(padx = 10,pady = 10) frame2.pack(padx = 10,pady = 10)
mainloop()

066 GUI的终极选择：Tkinter3
实例1：Checkbutton 组件
from tkinter import *
root = Tk() #需要一个Tkinter变量，用于表示该按钮是否被选中 v = IntVar() c = Checkbutton(root,text="测试一下",variable = v)
c.pack() #如果被选中，那么变量v被赋值为1，否则为0 #可以用个Label标签动态地给大家展示： lable = Label(root,textvariable = v) lable.pack()
mainloop()

实例2：
from tkinter import *
root = Tk()
GIRLS = ["貂蝉","王昭君","西施","杨玉环"] v = [] for girl in GIRLS:     v.append(girl)     c = Checkbutton(root,text = girl,variable = v[-1])#-1表示每次取v列表中最后一个元素，即刚加入的那个元素     c.pack(anchor = W)#W(western)向左对齐
mainloop()

from tkinter import *
root = Tk()
v = IntVar()#如果被选中，v被赋值为1，否则为0 Radiobutton(root,text = "One",variable = v,value = 1).pack(anchor = W) #value表示第一个按钮被选中时，v的值赋值给variable
Radiobutton(root,text = "Two",variable = v,value = 2).pack(anchor = W)
Radiobutton(root,text = "Three",variable = v,value = 3).pack(anchor = W)
Radiobutton(root,text = "Four",variable = v,value = 4).pack(anchor = W)
mainloop()
实例4：循环处理
from tkinter import *
root = Tk()
LANGS = [     ("Python",1),     ("Perl",2),     ("Ruby",3),     ("Lua",4)]
v = IntVar()#如果被选中，v被赋值为1，否则为0 v.set(1)#将1设置为默认值 for lang,num in LANGS:     b= Radiobutton(root,text = lang,variable = v,value = num)     b.pack(anchor = W) #value表示第一个按钮被选中时，v的值赋值给variable
mainloop()
实例5：改成按钮形式
from tkinter import *
root = Tk()
LANGS = [     ("Python",1),     ("Perl",2),     ("Ruby",3),     ("Lua",4)]
v = IntVar()#如果被选中，v被赋值为1，否则为0 v.set(1)#将1设置为默认值 for lang,num in LANGS:     b= Radiobutton(root,text = lang,variable = v,value = num,indicatoron = False)     b.pack(fill = X)#表示横向填充 #value表示第一个按钮被选中时，v的值赋值给variable
mainloop()
实例6：LabelFrame 组件
from tkinter import *
root = Tk()
group = LabelFrame(root,text = "最好的脚本语言是？",padx = 10,pady = 10)#按钮相对边框的偏移 group.pack(padx = 10,pady = 10)#框架相对边框的偏移
LANGS = [     ("Python",1),     ("Perl",2),     ("Ruby",3),     ("Lua",4)]
v = IntVar()#如果被选中，v被赋值为1，否则为0 v.set(1)#将1设置为默认值 for lang,num in LANGS:     b= Radiobutton(group,text = lang,variable = v,value = num,indicatoron = False)     b.pack(fill = X) #value表示第一个按钮被选中时，v的值赋值给variable
mainloop()

067 GUI的终极选择：Tkinter4
实例1：
from tkinter import *
root = Tk()#创建主窗口 e = Entry(root)#在主窗口中插入输入框 e.pack(padx = 20,pady = 20)
e.delete(0,END)#清空输入框 e.insert(0,"默认文本...")#设置输入框内容
mainloop()
实例2：
from tkinter import *
def button1_show():     print("作品：《%s》" % e1.get())#将e1.get()中得到的输入框1的内容格式化为字符串     print("作者：%s" % e2.get())
root = Tk()#创建主窗口
Label(root,text = "作品:",padx = 20,pady = 10).grid(row=0,column=0)#第1行第1列,偏移是相对于当前操作组件的相邻x轴或y轴的偏移距离 Label(root,text = "小甲鱼:").grid(row=1,column=0)#第1行第0列
#加两个按钮 Button1 = Button(root,text = "获取信息",command = button1_show)\           .grid(row = 2,column = 0,sticky = W,padx = 10,pady=10)#加入反斜杠可实现分行编辑,方位设置为最西边(即靠左) Button2 = Button(root,text = "退出",command = root.quit).grid(row = 2,column = 1,sticky = E,padx=10)#方位设置为最东边(即靠右)
#注：双击打开文件时退出才有效 e1.delete(0,END)#清空输入框 e1.insert(0,"零基础入门学习Python")#设置输入框内容
e2.delete(1,END)#清空输入框 e2.insert(1,"小甲鱼")#设置输入框内容
mainloop()

按下获取信息

更改输入框数据，然后按下获取信息

实例2：账号密码设置
from tkinter import *
def show():     print("作品：《%s》" % e1.get())#将e1.get()中得到的输入框1的内容格式化为字符串     print("作者：%s" % e2.get())     e1.delete(0,END)#清空输入框1     e2.delete(0,END)#清空输入框2
root = Tk()#创建主窗口 #Tkinter总共提供了三种布局组件的方法：pack()、grid()和place() #grid()方法允许你用表格的形式来管理组件的位置 #row选项代表行，coulumn选项代表列 #row = 1,column = 2表示第二行第三列（0表示第一行）
Label(root,text = "账号:").grid(row=0)#第1行 Label(root,text = "密码:").grid(row=1)#第2行 v1 = StringVar() v2 = StringVar()
#可以使用sticky选项来设置组件的位置 #使用N、E、S、W以及他们的组合NE、SE、SW、NW来表示方位
#加两个按钮 Button(root,text = "芝麻开门",command = show)\           .grid(row = 2,column = 0,sticky = W,padx = 10,pady=5)#加入反斜杠可实现分行编辑,方位设置为最西边(即靠左) Button(root,text = "退出",command = root.quit).grid(row = 2,column = 1,sticky = E,padx=10)#方位设置为最东边(即靠右)
mainloop()

实例3：验证函数validatecommand
from tkinter import *
master = Tk()
def test():     if e1.get() == "小甲鱼":         print("正确！")         return True     else:         print("错误！")         e1.delete(0, END)         return False
v = StringVar()
#focusout表示Entry组件失去焦点的时候验证，调用validatecommand的test函数
mainloop()

实例4：invalidcommand函数
from tkinter import *
master = Tk()
def test():     if e1.get() == "小甲鱼":         print("正确！")         return True     else:         print("错误！")         e1.delete(0, END)         return False
def test2():     print("我被调用了...")
v = StringVar()
#focusout表示Entry组件失去焦点的时候验证，调用validatecommand的test函数 #invalidcommand选项指定的函数只有在validatecommand的返回值为False的时候才被调用 e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test,\            invalidcommand=test2) e2 = Entry(master) e1.pack(padx=10, pady=10) e2.pack(padx=10, pady=10)
mainloop()

实例5：验证函数提供一些额外的选项
validatecommand(f,s1,s2,...)
其中，f是验证函数名，s1,s2,s3是额外的选项，这些选项会作为参数一次传给f函数。在此之前，需要调用register()方法将验证函数包装起来。
from tkinter import *
master = Tk()
v = StringVar()
def test(content, reason, name):     if content == "小甲鱼":         print("正确！")         print(content, reason, name)         return True     else:         print("错误！")         print(content, reason, name)         return False
testCMD = master.register(test) e1 = Entry(master, textvariable=v, validate="focusout", \            validat            ecommand=(testCMD, '%P', '%v', '%W')) e2 = Entry(master) e1.pack(padx=10, pady=10) e2.pack(padx=10, pady=10)
mainloop()

实例6：设计一个 计算器
from tkinter import * #计算函数 def calc():     result = int(v1.get())+int(v2.get())#强制转换为整型     v3.set(result)#将result中的内容放到v3中
#创建窗口 root = Tk() #创建窗口中的一个frame框架 frame = Frame(root) #设置框架位置并显示 frame.pack(padx = 10,pady = 10)
v1 = StringVar() v2 = StringVar() v3 = StringVar()
#注意，这里不能使用e1.get()或者v1.get()来获取输入的内容，因为validate选项 #指定为“key"的时候，有任何输入操作都会被拦截到这个函数中 #也就是说先拦截，只有这个函数返回True,那么输入的内容才会到变量里去 #所以要用%P来获取最新的输入框内容 def test(content):     if content.isdigit():         return True     else:         return False
#创建三个Entry组件 testCMD = frame.register(test) #创建2个输入组件,输入的数据赋值给v1、v2 e1 = Entry(frame, textvariable=v1,width=10, validate="key",\            validatecommand=(testCMD, '%P')) e2 = Entry(frame, textvariable=v2,width=10, validate="key",\            validatecommand=(testCMD, '%P')) #一个输出组件,设置为只读模式(readonly),v3的数据赋值给textvariable进行输出显示 e3 = Entry(frame, textvariable=v3,width=10, validate="key",\            validatecommand=(testCMD, '%P'),state="readonly") #位置设置 e1.grid(row=0,column=0,padx=10,pady=10) e2.grid(row=0,column=2,padx=10) e3.grid(row=0,column=4,padx=10)
#创建两个Label组件 Label(frame,text="+").grid(row=0,column=1) Label(frame,text="=").grid(row=0,column=3)
mainloop()
068 GUI的终极选择：Tkinter5
Listbox组件
实例1：
from tkinter import *
root = Tk()#创建主窗口
theLB = Listbox(root,setgrid = True,selectmode=EXTENDED)#创建一个空列表 theLB.pack()
#往列表里添加数据 for item in ["鸡蛋","鸭蛋","鹅蛋","李狗蛋"]:     theLB.insert(END,item)#每次在列表最后插入一个数据
#创建一个按钮,ACTIVE表示当前选中的数据 theButton = Button(root,text="删除",command = lambda x = theLB:x.delete(ACTIVE)) theButton.pack()
#theLB.delete(0,END)删除所有列表数据
mainloop()注：listbox.delete(0,END)可以删除列表中所有项目
实例2：添加height选项
from tkinter import *
root = Tk()#创建主窗口
#height=11表示可以显示11个项目 theLB = Listbox(root,setgrid = True,\                 selectmode=BROWSE,height=11)#创建一个空列表,选择模式为单选 theLB.pack()
#往列表里添加数据 for item in range(11):     theLB.insert(END,item)#每次在列表最后插入一个数据
#创建一个按钮,ACTIVE表示当前选中的数据 theButton = Button(root,text="删除",command = lambda x = theLB:x.delete(ACTIVE)) theButton.pack()
#theLB.delete(0,END)删除所有列表数据
mainloop()
Scrollbar组件
实例1：
from tkinter import *
root = Tk()#创建主窗口
sb = Scrollbar(root) sb.pack(side=RIGHT,fill=Y)
lb = Listbox(root,yscrollcommand=sb.set)#创建一个空列表 for i in range(1000):     lb.insert(END,i) lb.pack(side=LEFT,fill=BOTH)
sb.config(command = lb.yview)
mainloop()
事实上，这是一个互联互通的过程。当用户操作滚动条时，滚动条响应滚动并同时通过Listbox组件的yview()方法滚动列表框里的内容；同样，当列表框中可视范围发生改变的时候，Listbox组件通过调用Scrollbar组件的set()方法设置滚动条的最新位置。
Scale组件
Scale组件主要是通过滑块来表示某个范围内的一个数字，可以通过修改选项设置范围以及分辨率（精度）
实例1：
from tkinter import *
root = Tk()#创建主窗口 Scale(root,from_=0,to=42).pack()#创建铅锤方向滚动条 Scale(root,from_=0,to=200,orient=HORIZONTAL).pack()#创建水平方向滚动条
mainloop()

实例2：打印当前位置
from tkinter import *
def show():     print(s1.get(),s2.get())#使用get()方法获取当前滑块的位置
root = Tk()#创建主窗口 s1 = Scale(root,from_=0,to=42)#创建铅锤方向滚动条 s1.pack() s2 = Scale(root,from_=0,to=200,orient=HORIZONTAL)#创建水平方向滚动条 s2.pack()
#创建一个按钮 Button(root,text="获取位置",command=show).pack()
mainloop()

实例3：通过resolution选项控制分辨率（步长），通过tickinterval选项设置刻度
from tkinter import *
def show():     print(s1.get(),s2.get())#使用get()方法获取当前滑块的位置
root = Tk()#创建主窗口 #tickinterval表示设置刻度，即每隔多少显示一个刻度 #length表示滚动条的长度所占的像素数 #resolution用来控制分辨率（步长） s1 = Scale(root,from_=0,to=42,tickinterval=5,length=200,\            resolution=5,orient=VERTICAL)#创建铅锤方向滚动条 s1.pack() s2 = Scale(root,from_=0,to=200,tickinterval=10,\            length=600,orient=HORIZONTAL)#创建水平方向滚动条 s2.pack()
#创建一个按钮 Button(root,text="获取位置",command=show).pack()
mainloop()

069 GUI的终极选择：Tkinter6
Text组件
Text(文本)组件用于显示和处理多种任务。虽然该组件的主要目的是显示多行文本，但它常常也被用于作为简单的文本编辑器和网页浏览器使用。
实例1：插入内容
from tkinter import *
root = Tk() text = Text(root,width=30,height=2) text.pack() #INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love\n")#光标当前的位置插入
#END，对应Text组件的文本缓存区最后一个字符的下一个位置 text.insert(END,"FishC.com!")
mainloop()
实例2：插入image对象windows组件
from tkinter import *
def show():     print("哟，我被点了一下~")
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
#创建一个按钮 b1=Button(root,text="点我点我",command=show) text.window_create(INSERT,window=b1)
mainloop()

实例3：单击按钮显示一张图片
from tkinter import *
def show():     text.image_create(INSERT,image=photo)
root = Tk() text = Text(root,width=30,height=50) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
photo = PhotoImage(file='fishc.gif')
#创建一个按钮 b1=Button(root,text="点我点我",command=show) text.window_create(INSERT,window=b1)
mainloop()
Indexer用法
实例1：“line.column”
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 print(text.get(1.2,1.6))#获取第一行第2列到第一行第六列的数据
mainloop()

实例2：“line.end”
行号加上字符串".end"格式表示为该行最后一个字符的位置
实例：
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 print(text.get("1.2","1.end"))#获取第一行第2列到第一行第六列的数据
mainloop()
实例：Mark事实上就是索引，用于表示位置
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.mark_set("here","1.2")#设置光标位置为1.2 text.insert("here","插")
mainloop()

实例2：如果Mark前面的内容发生改变，Mark的位置也会跟着移动
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.mark_set("here","1.2")#设置当前光标位置为1.2 text.insert("here","插")#执行后当前光标位置（Mark位置）变成了1.3 text.insert("here","入") #text.insert("1.3","入")
mainloop()
实例3：如果Mark周围的文本被删除了，Mark仍然存在
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.mark_set("here","1.2")#设置当前光标位置为1.2 text.insert("here","插")#执行后当前光标位置变成了1.3 text.delete("1.0",END) text.insert("here","入")#here表示当前Mark的位置,如果Mark左边并没有数据则会插入到最左边
mainloop()
例4：只有mark_unset()方法可以解除Mark的封印
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.mark_set("here","1.2")#设置当前光标位置为1.2 text.insert("here","插")#执行后当前光标位置变成了1.3 text.mark_unset("here")
text.delete("1.0",END) text.insert("here","入")#here表示当前Mark的位置
mainloop()
默认插入内容是插入到Mark左侧（就是说插入一个字符后，Mark向后移动了一个字符的位置）
实例5：插入内容到Mark的右侧
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.mark_set("here","1.2")#设置当前Mark位置为1.2 text.mark_gravity("here",LEFT)
text.insert("here","插")#执行后当前Mark位置变成了1.3 text.insert("here","入")#here表示当前Mark的位置
mainloop()

070 GUI的终极选择：Tkinter7
实例1：添加Tags
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,，与1.14设置Tag样式 text.tag_config("tag1",background ="yellow",foreground="red")
mainloop()
实例2：Tags覆盖
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,，与1.14设置Tag样式 text.tag_add("tag2","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,，与1.14设置Tag样式
text.tag_config("tag1",background ="yellow",foreground="red") text.tag_config("tag2",background ="blue")
mainloop()

实例2：降低Tag优先级
from tkinter import *
root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 text.tag_add("tag1","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,，与1.14设置Tag样式 text.tag_add("tag2","1.7","1.12","1.14")#1.7(第一行第八列)到1.12,，与1.14设置Tag样式
text.tag_config("tag1",background ="yellow",foreground="red") text.tag_config("tag2",background ="blue")
text.tag_lower("tag2")#降低tag2的优先级
mainloop()
实例3：Tags事件绑定
from tkinter import * import webbrowser#导入网页模块
def show_hand_cursor(event):     text.config(cursor="arrow")
def show_arrow_cursor(event):     text.config(cursor="xterm")
def click(event):     webbrowser.open("http://www.fishc.com")      root = Tk() text = Text(root,width=30,height=5) text.pack()
mainloop()

实例4：判断内容是否发生改变
from tkinter import * import hashlib
def getSig(contents):     m = hashlib.md5(contents.encode())     return m.digest()
def check():#检查     contents = text.get(1.0,END)     if sig!=getSig(contents):         print("警报，内容发生变动")     else:         print("风平浪静")      root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入 #注意，行号从1开始，列号则从0开始 #获取文本内容 contents=text.get(1.0,END)
sig = getSig(contents)
Button(root,text="检查",command=check).pack()
mainloop()

实例5：查找操作（使用search()方法可以搜索Text组件中的内容）
from tkinter import * import hashlib
#将任何格式的索引号统一为元组（行，列）的格式输出 def getIndex(text,index):     #split这里以"."拆分字符串，将1.3拆分为字符1和3，然后通过map将字符转换为整型     return tuple(map(int,str.split(text.index(index),".")))      root = Tk() text = Text(root,width=30,height=5) text.pack()
#INSERT索引表示插入光标当前的位置 text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
#将任何格式的索引号统一为元组（行、列）的格式输出 start = 1.0 while True:     pos = text.search("o",start,stopindex=END)#从开始到结束全文搜索     if not pos:         break     print("找到了，位置是：",getIndex(text,pos))     start = pos + "+1c"#将start指向找到的字符位置的下一个字符，以便进行下一次搜索
mainloop()
Text组件内部有一个栈专门用于记录内容的每次变动，所以每次“撤销”操作就是一次弹栈操作，“恢复”就是再次压栈。
实例6：撤销
from tkinter import *
#将任何格式的索引号统一为元组（行，列）的格式输出 def show():     text.edit_undo()      root = Tk() text = Text(root,width=30,height=5,undo=True) text.pack() text.insert(INSERT,"I love FishC")
Button(root,text="撤销",command=show).pack()
mainloop()

实例7：每次撤销一个字符
from tkinter import *
def callback(event):     text.edit_separator()
def show():     text.edit_undo()#执行撤回操作      root = Tk()
#autoseparators表示一次完整的操作结束后自动插入“分隔符”，此处设置为False text = Text(root,width=30,height=5,autoseparators=False,undo=True,maxundo=10) text.pack()
text.insert(INSERT,"I love FishC!") text.bind('<Key>',callback)#每次有输入就插入一个“分隔符”
Button(root,text="撤销",command=show).pack()
mainloop()

071 GUI的终极选择：Tkinter8
Canvas（画布）组件
一个可以让你随心所欲绘制界面的组件。通常用于显示和编辑图形，可以用它来绘制直线、图形、多边形，甚至是绘制其他组件。
实例1：
from tkinter import * root = Tk() #创建canvas对象框，设置其宽度、高度与背景色 w = Canvas(root,width=200,height=100,background="black") w.pack()
#画一条黄色的线 w.create_line(0,50,200,50,fill="yellow") #画一条红色的竖线（虚线） w.create_line(100,0,100,100,fill="red") #中间画一个蓝色的矩形 w.create_rectangle(50,25,150,75,fill="blue")
mainloop()
实例2：
from tkinter import * root = Tk() #创建canvas对象框，设置其宽度、高度与背景色 w = Canvas(root,width=200,height=100,background="black") w.pack()
#画一条黄色的线（参数为其x、y轴坐标） line1 = w.create_line(0,50,200,50,fill="yellow") #画一条红色的竖线（虚线） line2 = w.create_line(100,0,100,100,fill="red") #中间画一个蓝色的矩形 rect1 = w.create_rectangle(50,25,150,75,fill="blue")
w.coords(line1,0,25,200,25)#将line1移动到新的坐标 w.itemconfig(rect1,fill="red")#重新设置矩形的填充色为红色 w.delete(line2)#删除线2
#创建一个按钮，按下时删除所有图形 Button(root,text="删除全部",command=(lambda x=ALL:w.delete(x))).pack()
mainloop()

实例3：在Canvas上显示文本
from tkinter import * root = Tk() #创建canvas对象框，设置其宽度、高度与背景色 w = Canvas(root,width=200,height=100,background="black") w.pack()
#画一条绿色的斜线（参数为其x、y轴坐标），宽度为三个像素点 line1 = w.create_line(0,0,200,100,fill="green",width=3) #画一条绿色的斜线 line2 = w.create_line(200,0,0,100,fill="green",width=3) #中间画两个矩形 rect1 = w.create_rectangle(40,20,160,80,fill="blue") rect2 = w.create_rectangle(60,30,140,70,fill="yellow") #在矩形正中（默认）显示文本，坐标为文本正中坐标 w.create_text(100,50,text="Hadley")
#创建一个按钮，按下时删除所有图形 Button(root,text="删除全部",command=(lambda x=ALL:w.delete(x))).pack()
mainloop()

实例4：绘制椭圆
from tkinter import * root = Tk() #创建canvas对象框，设置其宽度、高度与背景色 w = Canvas(root,width=200,height=100,background="white") w.pack()
#绘制一个虚线的矩形 w.create_rectangle(40,20,160,80,dash=(4,4)) #绘制椭圆，粉色填充 w.create_oval(40,20,160,80,fill="pink") #在矩形正中(默认)显示文本，坐标为文本正中坐标 w.create_text(100,50,text="Hadley")
mainloop() 实例5：绘制圆形
from tkinter import * root = Tk() #创建canvas对象框，设置其宽度、高度与背景色 w = Canvas(root,width=200,height=100,background="white") w.pack()
#绘制一个虚线的矩形 w.create_rectangle(40,20,160,80,dash=(4,4)) #绘制圆形，粉色填充 #w.create_oval(40,20,160,80,fill="pink") w.create_oval(70,20,130,80,fill="pink") #在矩形正中(默认)显示文本，坐标为文本正中坐标 w.create_text(100,50,text="Hadley")
mainloop()
实例6：绘制多边形
from tkinter import * import math as m
root = Tk() w=Canvas(root,width=200,height=150,background="red") w.pack() center_x = 100 center_y = 80 r = 70 points = [     #左上角A     center_x - int(r*m.sin(2*m.pi/5)),     center_y - int(r*m.cos(2*m.pi/5)),     #右上角C     center_x + int(r*m.sin(2*m.pi/5)),     center_y - int(r*m.cos(2*m.pi/5)),     #左下角E     center_x - int(r*m.sin(m.pi/5)),     center_y + int(r*m.cos(m.pi/5)),     #顶点D     center_x,     center_y - r,     #右下角B     center_x + int(r*m.sin(m.pi/5)),     center_y + int(r*m.cos(m.pi/5)),     ] #创建多边形方法，会自动按ACEDBA的形式连线，如果构成闭环，则会自动填充 w.create_polygon(points,outline="green",fill="yellow")
mainloop()
实例7：
from tkinter import *
root = Tk() w=Canvas(root,width=400,height=200,background="white") w.pack()
def paint(event):#画小圆     x1,y1 = (event.x - 1),(event.y -1)     x2,y2 = (event.x + 1),(event.y +1)     w.create_oval(x1,y1,x2,y2,fill="red")
w.bind("<B1 - Motion>",paint)#画布与鼠标进行绑定 Label(root,text="按住鼠标左键并移动，开始绘制你的理想蓝图吧。。。").pack(side=BOTTOM)
mainloop()
073 GUI的终极选择：Tkinter10
Munu组件
实例1：创建一个顶级菜单（或称窗口主菜单）
from tkinter import *
def callback():     print("被调用了")      root = Tk()
mainloop()
实例2：创建添加到主菜单上的下拉菜单
from tkinter import *
def callback():     print("被调用了")      root = Tk()
mainloop()
实例3：创建一个弹出菜单方法
from tkinter import *
def callback():     print("被调用了")      root = Tk()
#将鼠标右键与popup方法绑定 frame.bind("<Button-3>",popup)
mainloop()

实例4：菜单弹出
from tkinter import *
def callback():     print("被调用了")      root = Tk()
#将鼠标右键与popup方法绑定 frame.bind("<Button-3>",popup)
mainloop()
from tkinter import *
def callback():     print("被调用了")      root = Tk()
#创建radiobutton关联变量 editVar = IntVar() editVar.set(1)
mainloop()

实例1：
from tkinter import *
def callback():     print("被调用了")      root = Tk()
mb.pack(side=RIGHT)#设置为右中显示
mainloop()

选项菜单的发明弥补了Listbox组件无法实现下拉列表框的遗憾
实例1：
from tkinter import *
def callback():     print("被调用了")      root = Tk()
variable = StringVar()#创建字符串变量variable variable.set("one")#初始值设置为"one" w = OptionMenu(root,variable,"one","two","three") w.pack()
mainloop()

实例2：多个选项添加到选项菜单中
from tkinter import *
def callback():     print("被调用了")      root = Tk()
OPTIONS = [     "Hadley",     "小土豆",     "yiwofeiye",     "RAN"     ]
variable = StringVar()#创建字符串变量variable variable.set(OPTIONS[0])#初始值设置为"one" w = OptionMenu(root,variable,*OPTIONS) w.pack()
def callback():     print(variable.get())
Button(root,text="点我",command=callback).pack()
mainloop()

074  GUI的终极选择：Tkinter11
事件绑定
对于每个组件来说，可以通过bind()方法将函数或方法绑定到具体的事件上。当被触发的事件满足该组件绑定的事件时，Tkinter就会带着事件描述去调用handler()方法
实例1：捕获单击鼠标位置
from tkinter import*
root = Tk()
def callback(event):     print("点击位置：",event.x,event.y)
frame = Frame(root,width=200,height=200) #Button表示鼠标点击事件 #1代表左键 2代表中间滚轮点击 3代表右键 frame.bind("<Button-1>",callback)#按键按下时，调用callback方法 frame.pack()
mainloop()

实例2：捕获键盘事件
#捕获单击鼠标的位置 from tkinter import*
root = Tk()
def callback(event):     print("敲击位置：",repr(event.char))#打印当前按下按键的字符     print(event.char)
frame = Frame(root,width=200,height=200) #Key为键盘事件 frame.bind("<Key>",callback)#按键按下时，调用callback方法 frame.focus_set()#获得焦点 frame.pack()
mainloop()
实例3：捕获鼠标在组件上的运动轨迹
#当鼠标在组件内移动的整个过程均触发该事件
from tkinter import*
root = Tk()
def callback(event):     print("当前位置：",event.x,event.y)#打印当前按下按键的字符
frame = Frame(root,width=200,height=200) frame.bind("<Motion>",callback)#按键按下时，调用callback方法 frame.pack()
mainloop()

事件序列
Tkinter使用一种称为事件序列的机制来允许用户定义事件，用户需要使用bind()方法将具体的事件序列与自定义的方法绑定
Event对象（按键名keysym和按键码keycode）
实例1：打印当前按下按键的按键名
from tkinter import*
root = Tk()
def callback(event):     print(event.keysym)#打印当前按下按键的按键名     print(event.char)
frame = Frame(root,width=200,height=200) #Key为键盘事件 frame.bind("<Key>",callback)#按键按下时，调用callback方法 frame.focus_set()#获得焦点 frame.pack()
mainloop()
075 GUI的终极选择：Tkinter12
Message组件
Message(消息)组件是Label组件的变体，用于显示多行文本信息。Message组件能够自动换行，并调整文本的尺寸使其适应给定得尺寸。
实例1：
from tkinter import *
root = Tk() w1 = Message(root,text="这是一则消息",width=100) w1.pack() w2 = Message(root,text="这是一条骇人听闻的长消息！",width=100) w2.pack()
mainloop()

Spinbox组件
Entry组件的变体，用于从一些固定的值中选取一个。使用Spinbox组件，可以通过返回或者元组指定允许用户输入的内容。
实例1：
from tkinter import *
root = Tk()
#w = Spinbox(root,from_=0,to=10)#指定输入值为0-10 w = Spinbox(root,value=("Hadley","小土豆","雅馨"))#指定输入 w.pack()
mainloop()
PanedWindow组件
与Frame类似，都是为组件提供一个框架，但其还允许让用户调整应用程序的空间划分
实例1：两窗格
from tkinter import *
root = Tk()
m = PanedWindow(orient = VERTICAL)#设置为上下分布 m.pack(fill=BOTH,expand=1)#设置为框架覆盖全局
top = Label(m,text="top pane")#顶窗格 m.add(top)
bottom = Label(m,text="bottom pane")#底窗格 m.add(bottom)
mainloop()
实例2：三窗格
from tkinter import *
root = Tk()
m1 = PanedWindow()#默认为左右分布 m1.pack(fill=BOTH,expand=1) left = Label(m1,text="left pane")#左窗格 m1.add(left)
m2 = PanedWindow(orient=VERTICAL) m1.add(m2) top=Label(m2,text="top pane")#顶窗格 m2.add(top) bottom = Label(m2,text="bottom pane")#底窗格 m2.add(bottom)
mainloop()
实例3：显示“分割线”
from tkinter import *
root = Tk()
#showhandle=True表示显示“手柄” #sashrelief=SUNKEN表示分隔线的样式设置为向下凹 m1 = PanedWindow(showhandle=True,sashrelief=SUNKEN) m1.pack(fill=BOTH,expand=1) left = Label(m1,text="left pane") m1.add(left)
m2 = PanedWindow(orient=VERTICAL,showhandle=True,sashrelief=SUNKEN) m1.add(m2) top=Label(m2,text="top pane") m2.add(top) bottom = Label(m2,text="bottom pane") m2.add(bottom)
mainloop()
Toplevel组件
Topleve（顶级窗口）l组件类似于Frame组件，但其是一个独立的顶级窗口，通常拥有标题栏、边框等部件。通常用在显示额外的窗口、对话框和其他弹出窗口中。
实例1：按钮按下创建一个顶级窗口
from tkinter import *
def create():     top = Toplevel()#创建一个独立的顶级窗口     top.title("FishC Demo")     msg = Message(top,text="I love FishC.com")     msg.pack()      root = Tk() Button(root,text="创建顶级窗口",command=create).pack()
mainloop()
实例2：Toplevel的窗口设置为50%透明
from tkinter import *
def create():     top = Toplevel()     top.title("FishC Demo")     top.attributes("-alpha",0.5)#设置为50%透明度     msg = Message(top,text="I love FishC.com")     msg.pack()      root = Tk() Button(root,text="创建顶级窗口",command=create).pack()
mainloop()

076 GUI的终极选择：Tkinter13
布局管理器
布局管理器就是管理你的那些组件如何排列的家伙。Tkinter有三个布局管理器，分别是pack、grid和place
pack:按添加顺序排列组件
grid:按行/列形式排列组件
place:允许程序员指定组件的大小和位置pack
实例1：生成一个Listbox组件并将它填充到root窗口
from tkinter import *
root = Tk() listbox = Listbox(root) #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间 #BOTH表示同时横向和纵向扩展；X表示横向；Y表示纵向 #expand选项是告诉窗口管理器是否将父组件的额外空间也填满（任意拉伸窗口依旧会填满）
#默认情况下pack是将添加的组件依次纵向排列 listbox.pack(fill=BOTH,expand=True) for i in range(10):     listbox.insert(END,str(i))
mainloop()
实例2：纵向排列，横向填充
from tkinter import *
root = Tk() #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间 #BOTH表示同时横向和纵向扩展；X表示横向；Y表示纵向 #expand选项是告诉窗口管理器是否将父组件的额外空间也填满
#默认情况下pack的side属性是将添加的组件依次纵向排列 Label(root, text="red", bg="red", fg="white").pack(fill=X) Label(root, text="green", bg="green", fg="black").pack(fill=X) Label(root, text="blue", bg="blue", fg="white").pack(fill=X)
mainloop()

实例3：横向排列，纵向填充
from tkinter import *
root = Tk() #fill选项是告诉窗口管理器该组件将怎样填充整个分配给它的空间 #BOTH表示同时横向和纵向扩展；X表示横向；Y表示纵向 #expand选项是告诉窗口管理器是否将父组件的额外空间也填满
#将pack设置为横向排列 Label(root, text="red", bg="red", fg="white").pack(side=LEFT) Label(root, text="green", bg="green", fg="black").pack(side=LEFT) Label(root, text="blue", bg="blue", fg="white").pack(side=LEFT)
mainloop()
grid
使用一个grid就可以简单地实现你用很多个框架和pack搭建起来的效果。使用grid排列组件，只需告诉它你想要将组件放置的位置（行row/列column）。
实例1：
from tkinter import *
root = Tk()
#column默认值是0 #默认情况下组件会居中显示在对应的网格里 #Label(root,text="用户名").grid(row=0) #Label(root,text="密码").grid(row=1) #设置sticky=W使Label左对齐 Label(root,text="用户名").grid(row=0,sticky=W)#左对齐 Label(root,text="密码").grid(row=1,sticky=W)
Entry(root).grid(row=0,column=1) Entry(root,show="*").grid(row=1,column=1)
mainloop()

实例2：设置rowspan与columnspan实现跨行和跨列功能
from tkinter import *
root = Tk()
#column默认值是0 #默认情况下组件会居中显示在对应的网格里 #Label(root,text="用户名").grid(row=0) #Label(root,text="密码").grid(row=1) #设置sticky=W使Label左对齐 #创建Label文本 Label(root,text="用户名").grid(row=0,sticky=W) Label(root,text="密码").grid(row=1,sticky=W) #创建输入 Entry(root).grid(row=0,column=1) Entry(root,show="*").grid(row=1,column=1) #插入Label图像 photo = PhotoImage(file="logo.gif") #rowspan=2跨两行，边距5 Label(root,image=photo).grid(row=0,column=2,rowspan=2,padx=5,pady=5) #columnspan=3跨三列（默认为居中显示），边距5 Button(text="提交",width=10).grid(row=2,columnspan=3,pady=5)
mainloop()

place
通常情况下不建议使用place布局管理器
实例1：将子组件显示在父组件的正中间
from tkinter import *
def callback():     print("正中靶心") root = Tk() #relx和rely指定的是子组件相对于父组件的位置，范围是(001.0),0.5则表示一半，正中间 #anchor=CENTER表示正中显示 Button(root,text="点我",command=callback).place(relx=0.5,rely=0.5,anchor=CENTER)
mainloop()

实例2：Button组件覆盖Label组件
from tkinter import *
def callback():     print("正中靶心") root = Tk()
photo = PhotoImage(file="logo_big.gif") Label(root,image=photo).pack() #relx和rely指定的是子组件相对于父组件的位置，范围是(001.0),0.5则表示一半，正中间 Button(root,text="点我",command=callback).place(relx=0.5,rely=0.5,anchor=CENTER)
mainloop()

实例3：
from tkinter import *
root = Tk()
#relx和rely指定的是子组件相对于父组件的位置，范围是(001.0),0.5则表示一半，正中间 #relwidth和relheight选项指定相对父组件的尺寸 Label(root,bg="red").place(relx=0.5,rely=0.5,relheight=0.75,relwidth=0.75,anchor=CENTER) Label(root,bg="yellow").place(relx=0.5,rely=0.5,relheight=0.5,relwidth=0.5,anchor=CENTER) Label(root,bg="green").place(relx=0.5,rely=0.5,relheight=0.25,relwidth=0.25,anchor=CENTER)
mainloop()

077 GUI的终极选择：Tkinter14
Tkinter提供了三种标准对话框模块，分别是：messagebox、filedialog、colorchooser
messagebox(消息对话框)
from tkinter import *
mainloop()

实例3：asiretrycancel函数

实例5：showerror函数
from tkinter import *
mainloop()

实例6：showinfo函数
from tkinter import *
mainloop()

实例7：showwarning函数
from tkinter import *
mainloop()

filedialog(文本对话框)
当应用程序需要使用打开文件或保存文件的功能时
实例1：
from tkinter import *
root = Tk()
Button(root,text="打开文件夹",command=callback).pack()
mainloop()

实例2：限制打开文件类型
from tkinter import *
root = Tk()
Button(root,text="打开文件夹",command=callback).pack()
mainloop()
colorchooser(颜色选择对话框)
颜色对话框提供一个让用户选择颜色的界面
实例1：
from tkinter import *
root = Tk()
def callback():     #colorchooser函数用于打开颜色选择对话框     fileName = colorchooser.askcolor()     print(fileName)
Button(root,text="打开文件夹",command=callback).pack()
mainloop()

对应的RGB值及其对应的16进制值

078 Pygame：初次见面，请大家多多关照
`
展开全文
• ## MySQL 面试题

万次阅读 多人点赞 2019-09-02 16:03:33
MySQL 面试题 MySQL 涉及的内容非常非常非常多，所以面试题也容易写的杂乱。当年，我们记着几个一定要掌握的重心： 重点的题目添加了【重点】前缀。 索引。 ...因为 MySQL 还会有部分内容和运维相关度比较高，所以...
• ## matlab人脸识别论文

万次阅读 多人点赞 2019-10-11 17:41:51
人脸检测与定位，检测图像中是否由人脸，若有，将其从背景中分割出来，并确定其在图 像中的位置。在某些可以控制拍摄条件的场合，如警察拍罪犯照片时将人脸限定在标尺内，此时人脸的定位很简单。证件照背景简单，...
• ## XGBoost

千次阅读 多人点赞 2019-06-25 00:53:07
(3) 上面提到CART回归树中寻找最佳分割点的衡量标准是最小化均方差，XGBoost寻找分割点的标准是最大化，lamda，gama与正则化项相关     XGBoost算法的步骤和GB基本相同，都是首先初始化为一个常数，GB是...
• ## 数据库面试

千次阅读 多人点赞 2019-02-13 09:03:42
3、幻读：在一个事务里面的操作中发现了未被操作的数据，系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级，但是系统管理员B就在这个时候插入了一条新的学生成绩具体分数的记录，当系统管理员A改结束后...
• C#基础教程-c#实例教程，适合初学者。 第一章 C#语言基础 本章介绍C#语言的基础知识，希望具有C语言的读者能够基本掌握C#语言，并以此为基础，能够进一步学习用C#语言编写window应用程序和Web应用程序。...
• ## 机器学习算法 综述（入门）

万次阅读 多人点赞 2019-06-16 21:59:28
假设给定一些分属于两类的2维点，这些点可以通过直线分割， 我们要找到一条最优的分割线：  支持向量机将向量映射到一个更高维的空间里，在这个空间里建立有一个最大间隔超平面。  在分开数据的超平面的...
• 计算目标函数（输出课程权值，教学资源的充分利用，可见学生流动量最小，相邻授课间隔尽量均匀，同一课程尽量只用一个教室等）。 二、内容 编写软件，实现界面友好的系统设计，完成整个排课系统优化的过程。 要求：...
• ## EAST算法详解

万次阅读 多人点赞 2018-06-18 14:54:54
直接预测图像中任意方向和矩形形状的文本或文本行，通过单个神经网络消除不必要的中间步骤（例如候选聚合和单词分割） 。  通过ICDAR 2015，COCO-Text和MSRA-TD500的标准数据集实验表明，所提出的算法在准确性和...
• 点击蓝色“程序猿DD”关注我回复“资源”获取独家...那么多科普文都在说植物上的黄金分割，尤其比如向日葵的花盘什么的，但这究竟是啥意思呢？*本文所有Gif图片的原始素材由@pehiztu 计算生成那么多科普文都在说植...
• 点击上方“AI算法与图像处理”，选择加"星标"或“置顶”重磅干货，第一时间送达图像分割是计算机视觉研究中的一个经典难题，已经成为图像理解领域关注的一个热点，图像分割是图像...
• 计算机视觉-----图像分割综述计算机视觉-----图像分割综述传统分割方法1.基于阈值的分割方法2.基于区域的图像分割方法3.基于边缘检测的分割方法结合特定工具的图像分割算法基于小波分析和小波变换的图像分割方法基于...
• 方法：单阈值分割、多阈值分割 特点：只考虑灰度特征、不考虑空间特征，鲁棒性不好 改进方向：将智能遗传算法应用于阈值筛选，选取最优阈值。 2、基于区域的分割： 思想：直接寻找区域 ...
• SSS据说是迄今为止效果最好的图像分割算法，效果超过Mask-RCNN，我们来研究一下下。 （项目测试结果在最后） Github项目：https://github.com/yaksoy/SemanticSoftSegmentation 算法主要处理步骤： 特征降维，从128...
• 【新智元导读】来自纽约大学、滑铁卢大学、UCLA等学者深度学习图像分割最新综述论文，对现有的深度学习图像分割研究进行梳理使其系统化，并提出6方面挑战，帮助读者更好地了解当前的研究现状和思路。可作为相关领域...
• 所提出的算法在生成分割注释和使用生成的注释学习语义分割网络之间交替。在该框架中成功的关键决定因素是仅在给定图像级标签的情况下构建可靠的初始注释的能力。为此，我们提出了超像素合并网络（SPN），它利用输入...
• 在这项工作中，我们评估了超像素池化层在深层网络结构中用于语义分割的应用。超像素池化是一种灵活有效的方法，可以替代其他包含空z间先验信息的池策略。我们提出了一个简单而高效的gpu层实现，并探讨了几种将该层...
• （最开始接触医学图像分割时写的综述，写的比较幼稚，传上来的时候格式可能有些乱。需要原文的小伙伴可以加我qq：604395564联系，也欢迎做医学图像处理的小伙伴一起交流学习。自己写的，欢迎转载，但请注明出处哦^_^...
• 培训的目标是找到适当的价值 网络参数以最小化损耗功能。 2.2.1 通用损失函数 对于回归任务（例如心脏定位，钙评分，界标检测，图像重建），最简单的损失函数是均方误差（MSE）： 其中y ^ i是目标值的向量，yˆi是...
• 3D几何数据（例如点云）的核心问题包括语义分割，对象检测和实例分割。在这些问题中，实例分割仅在文献中才开始解决。主要的障碍是点云本质上是无序的，无结构的和不均匀的。广泛使用的卷积神经网络要求对3D点云进行...
• opencv学习笔记（1）-阈值分割3种方法 文章结构： 1.三种分割方法：直接分割、自适应分割（平均值、高斯均值） 2.函数使用 3.程序例程 （C++） 4.效果展示 5.参数设置心得 三种分割方法 1.直接分割 直接分割即...
• 410. 分割数组的最大值 解题思路 审题之后可以得出结论，结果必定在[max(nums), sum(bums)] 这个区间内，因为左端点对应每个单独的元素构成一个子数组，右端点对应所有元素构成一个子数组。 方法1：二分法 通过示例...
• ## 语义分割综述

万次阅读 多人点赞 2018-09-11 14:55:15
太优秀了，收藏用！... 综述论文翻译：A Review on Deep Learning Techniques Applied to Semantic Segmentation ...近期主要在学习语义分割相关方法，计划将arXiv上的这篇综述好好翻译下，目前已完成了...
• ## 语义分割技术综述

千次阅读 多人点赞 2020-03-30 14:16:35
近期主要在学习语义分割相关方法，计划将arXiv上的这篇综述好好翻译下，目前已完成了一部分，但仅仅是尊重原文的直译，后续将继续完成剩余的部分，并对文中提及的多个方法给出自己的理解。 论文地址：...

...