精华内容
下载资源
问答
  • Matplotlib

    万次阅读 多人点赞 2018-08-23 23:28:21
    该格式化字符串的字母和符号均来自于MATLAB,由一个代表颜色的字符串和一个代表线型的字符串组合而成。默认使用的格式化字符串是’b-‘(表示蓝色实线)。   如果想要使用红色的圆点来绘制上例中的图形,你可以...

    官网帮助文档地址:https://matplotlib.org/tutorials/introductory/pyplot.html 

    Pyplot简介

    matplotlib.pyplot 是一个命令风格的函数集合,这使得 matplotlib 工作起来和MATLAB很相似。每一个 pyplot 函数都会使图形发生一些变化,例如:创建一幅图形、在一幅图中创建一个绘图区域、在绘图区域中绘制一些线、使用标签对图形进行修饰,等等。在matplotlib.pyplot中,各种各样的状态通过调用不同的函数被保存下来,因此,matplotlib 能够持续地对例如:当前的图形和绘图区域、并将绘图函数作用到当前轴上(请注意,这里和文档中大多数地方的“轴”指的是图形的坐标系部分,而不是严格数学术语中的多个轴)。
               使用 pyplot 生成一个可视化效果非常快:

    import matplotlib.pyplot as plt
    plt.plot([1,2,3,4])
    plt.ylabel('some numbers')
    plt.show()

    你可能会觉得很奇怪:为什么X轴取值的范围是 0~3,而Y轴取值的范围是1~4 ?

    这是因为:如果你只提供 了一个列表或者数组作为 plot() 命令的参数,Matplotlib 就会把这个序列当作是Y轴上的取值,并自动地为你生成 X轴上的值。由于在python中范围是从0开始的,因此在默认情况下,X轴向量会与Y轴向量的长度相同但其取值会从0开始,正如我们所看到的X轴的数据是[0,1,2,3]。
               plot() 是一个多功能的命令,能够接收任意数量的参数,例如:你可以使用如下命令来绘制与X相对的Y的图形。

    plt.plot([1, 2, 3, 4], [1, 4, 9, 16])

    格式化图形样式

    plot() 还为每一对参数X和Y提供了一个可选的第三个格式化字符串参数用来指定图形的颜色和线型。该格式化字符串的字母和符号均来自于MATLAB,由一个代表颜色的字符串和一个代表线型的字符串组合而成。默认使用的格式化字符串是’b-‘(表示蓝色实线)。 
               如果想要使用红色的圆点来绘制上例中的图形,你可以这么写:

    plt.plot([1, 2, 3, 4], [1, 4, 9, 16], 'ro')
    plt.axis([0, 6, 0, 20])
    plt.show()

     你可以查看 plot() 的文档来获取关于线的样式和格式化字符串的完整列表。
                上例中的 axis() 命令接收了 [xmin, xmax, ymin, ymax] 列表作为参数来指定了各个轴的视口大小。但如果 Matplotlib 被限制成只能和 list 一起使用 ,那么它在数值处理上将显得几乎毫无用处 。通常,我们都会使用 numpy 数组,事实上,所有的序列都会在 Matplotlib 内部被转化成 numpy 数组。下面的例子展示了如何在一条命令里使用  numpy 数组绘制出几条样式不同的线:

    import numpy as np
    
    # evenly sampled time at 200ms intervals
    t = np.arange(0., 5., 0.2)
    
    # red dashes, blue squares and green triangles
    plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
    plt.show()

     

     补充:plot()  中,格式化字符串由颜色、标识符和线型三部分组成,定义格式如下:

    fmt = '[color][marker][line]'

    其中,支持的颜色缩写有如下几个:

    charactercolor
    'b'blue
    'g'green
    'r'red
    'c'cyan
    'm'magenta
    'y'yellow
    'k'black
    'w'white

    标识符定义如下表所示:

    characterdescription
    '.'point marker
    ','pixel marker
    'o'circle marker
    'v'triangle_down marker
    '^'triangle_up marker
    '<'triangle_left marker
    '>'triangle_right marker
    '1'tri_down marker
    '2'tri_up marker
    '3'tri_left marker
    '4'tri_right marker
    's'square marker
    'p'pentagon marker
    '*'star marker
    'h'hexagon1 marker
    'H'hexagon2 marker
    '+'plus marker
    'x'x marker
    'D'diamond marker
    'd'thin_diamond marker
    '|'vline marker
    '_'hline marker

    可以选用的线型有以下四个:

    characterdescription
    '-'solid line style
    '--'dashed line style
    '-.'dash-dot line style
    ':'dotted line style

     以下是几个格式化字符串的示例:

    'b'    # blue markers with default shape
    'ro'   # red circles
    'g-'   # green solid line
    '--'   # dashed line with default color
    'k^:'  # black triangle_up markers connected by a dotted line

    使用关键字绘图

    Matplotlib 允许用户提供带有关键字的数据对象作为参数,一旦提供了带有关键字的数据,就能够直接使用关键字来代替这些变量去生成图形。
               我们可以使用某些命令来获取到这种格式的数据,例如:numpy.recarraypandas.DataFrame  。

    data = {'a': np.arange(50),
            'c': np.random.randint(0, 50, 50),
            'd': np.random.randn(50)}
    data['b'] = data['a'] + 10 * np.random.randn(50)
    data['d'] = np.abs(data['d']) * 100
    
    plt.scatter('a', 'b', c='c', s='d', data=data)
    plt.xlabel('entry a')
    plt.ylabel('entry b')
    plt.show()

    使用分类变量绘图

    使用分类变量来创建图形也是可以的,Matplotlib 允许用户直接把分类变量传递给多个绘图函数,例如:

    names = ['group_a', 'group_b', 'group_c']
    values = [1, 10, 100]
    
    plt.figure(1, figsize=(9, 3))
    
    plt.subplot(131)
    plt.bar(names, values)
    plt.subplot(132)
    plt.scatter(names, values)
    plt.subplot(133)
    plt.plot(names, values)
    plt.suptitle('Categorical Plotting')
    plt.show()

     控制线的属性 

    线的属性有很多,详情参见  matplotlib.lines.Line2D ,例如你可以设置:线宽、虚线和抗锯齿效果,等等。
               我们可以通过以下几种途径对线的属性进行设置:
               使用关键字参数

    plt.plot(x, y, linewidth=2.0)

    使用2D 线实例的set方法
               plot() 命令会返回一个线对象的列表,例如:line1, line2 = plot(x1, y1, x2, y2)。在下面的代码中,我们将假设我们只有一条线即plot() 命令返回的线列表的长度为 1 。我们使用 line, 来获取列表中的第一个元素。

    line, = plt.plot(x, y, '-')
    line.set_antialiased(False) # turn off antialising

               使用setp()命令
               下面示例一下如何使用MATLAB风格的命令对一个线列表的多个属性进行设置。很明显地,setp() 能够作用于一个对象列表或者是一个单独的对象。你既可以使用Python 的关键字作为参数也可以使用MATLAB风格的string/value 对作为参数:

    lines = plt.plot(x1, y1, x2, y2)
    # use keyword args
    plt.setp(lines, color='r', linewidth=2.0)
    # or MATLAB style string value pairs
    plt.setp(lines, 'color', 'r', 'linewidth', 2.0)

    2D 线实例的可用属性见下表:

    PropertyValue Type
    alphafloat
    animated[True | False]
    antialiased or aa[True | False]
    clip_boxa matplotlib.transform.Bbox instance
    clip_on[True | False]
    clip_patha Path instance and a Transform instance, a Patch
    color or cany matplotlib color
    containsthe hit testing function
    dash_capstyle['butt' | 'round' | 'projecting']
    dash_joinstyle['miter' | 'round' | 'bevel']
    dashessequence of on/off ink in points
    data(np.array xdata, np.array ydata)
    figurea matplotlib.figure.Figure instance
    labelany string
    linestyle or ls[ '-' | '--' | '-.' | ':' | 'steps' | ...]
    linewidth or lwfloat value in points
    lod[True | False]
    marker[ '+' | ',' | '.' | '1' | '2' | '3' | '4' ]
    markeredgecolor or mecany matplotlib color
    markeredgewidth or mewfloat value in points
    markerfacecolor or mfcany matplotlib color
    markersize or msfloat
    markevery[ None | integer | (startind, stride) ]
    pickerused in interactive line selection
    pickradiusthe line pick selection radius
    solid_capstyle['butt' | 'round' | 'projecting']
    solid_joinstyle['miter' | 'round' | 'bevel']
    transforma matplotlib.transforms.Transform instance
    visible[True | False]
    xdatanp.array
    ydatanp.array
    zorderany number

     如果你想要获取可供设置的线属性的列表,你可以直接调用 setp() 命令并传入一条线或者传入一个线列表作为唯一参数。

    lines = plt.plot([1, 2, 3])
    
    plt.setp(lines)
      alpha: float
      animated: [True | False]
      antialiased or aa: [True | False]
      ...snip

     使用多图形和多轴

    MATLAB和 pyplot 都有当前图形和当前轴的概念,所有的绘图命令都会被应用到当前轴上。在 Matplotlib 中,gca() 命令用来获取当前轴(一个 matplotlib.axes.Axes 实例),gcf() 命令用来获取当前图形(一个 matplotlib.figure.Figure 实例)。通常情况下,你并不需要考虑这些,因为 Matplotlib 会在后台对它们进行自动处理。
               下面是一个创建了两个子图形的示例脚本:

    def f(t):
        return np.exp(-t) * np.cos(2*np.pi*t)
    
    t1 = np.arange(0.0, 5.0, 0.1)
    t2 = np.arange(0.0, 5.0, 0.02)
    
    plt.figure(1)
    plt.subplot(211)
    plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')
    
    plt.subplot(212)
    plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
    plt.show()

    figure()命令在这儿是一个可选项,因为默认创建的就是 figure(1) ,同样地,在没有手动地指定任何轴的情况下,将默认创建 subplot(111) 。subplot() 命令需要指定行数、列数、图形编号,其中图形编号的取值范围为(1 ~ 行数*列数)。如果行数*列数<10,参数之间的逗号可以省略。因此,subplot(211) 和 subplot(2, 1, 1) 的作用是一样的。

    你可以创建任意数量的子图形或者坐标系。如果你想要手动去放置一个坐标系可以使用 axes() 命令,并通过  axes([left, bottom, width, height]) 的四个参数( 0 ~ 1 的小数)来指定坐标系的位置和大小。

               手动设置坐标系示例:https://matplotlib.org/gallery/subplots_axes_and_figures/axes_demo.html
               绘制多个子图形示例:
               https://matplotlib.org/gallery/subplots_axes_and_figures/subplot_demo.html
               https://matplotlib.org/gallery/subplots_axes_and_figures/subplots_demo.html
               你可以通过多次调用figure() 方法并传入一个递增的图形编号来创建多个图形。当然,只要你喜欢每一个图形也可以包含多个子图形或者坐标系:

    import matplotlib.pyplot as plt
    plt.figure(1)                # the first figure
    plt.subplot(211)             # the first subplot in the first figure
    plt.plot([1, 2, 3])
    plt.subplot(212)             # the second subplot in the first figure
    plt.plot([4, 5, 6])
    
    
    plt.figure(2)                # a second figure
    plt.plot([4, 5, 6])          # creates a subplot(111) by default
    
    plt.figure(1)                # figure 1 current; subplot(212) still current
    plt.subplot(211)             # make subplot(211) in figure1 current
    plt.title('Easy as 1, 2, 3') # subplot 211 title
    plt.show()

     

     你可以使用 clf() 命令来清除当前图形或者使用 cla() 命令来清除当前坐标系。 如果你觉得由后台帮你维护图形状态(指当前的图像,图形和坐标系)这种方式比较烦人,无须绝望:这只是对面向对象API的一个瘦状态封装,你可以把它替换掉(参见 Artist tutorial )。
                如果你是在创建多个图形,你还需要注意另外一件事:图形所占用的内存直到你调用 close() 显式地将其关闭才会全部释放,如果只删除掉图形的引用或者通过窗口管理器销毁掉屏幕上的图像展示窗口,图形所占用的内存是不能完全被释放的,因为pyplot 在 close() 命令被调用之前仍会对其内部引用进行维护。

     使用文本

    使用 text() 命令可以在图形的任意位置添加文本,xlabel()、ylabel()、title() 用来在指定位置添加文本(更多详细案例参见 Text in Matplotlib Plots )。

    mu, sigma = 100, 15
    x = mu + sigma * np.random.randn(10000)
    
    # the histogram of the data
    n, bins, patches = plt.hist(x, 50, density=1, facecolor='g', alpha=0.75)
    
    
    plt.xlabel('Smarts')
    plt.ylabel('Probability')
    plt.title('Histogram of IQ')
    plt.text(60, .025, r'$\mu=100,\ \sigma=15$')
    plt.axis([40, 160, 0, 0.03])
    plt.grid(True)
    plt.show()

     

    所有的文本命令都会返回一个  matplotlib.text.Text  实例,跟前面线的属性设置一样,你可以往文本命令中传入关键字参数或者使用 setp() 命令来自定义文本的属性。

    t = plt.xlabel('my data', fontsize=14, color='red')

                关于文本的属性的更多细节请参见:https://matplotlib.org/tutorials/text/text_props.html

     在文本中使用数学表达式 

    Matplotlib 接收的文本表达式中可以包含 TeX 方程表达式,比如,书写时,需要使用 "$" 符号把TeX表达式包起来:

    plt.title(r'$\sigma_i=15$')

    标题字符串前面的”r”字符非常重要:它表示这个字符串是一个原生字符串,不用把里面的反斜杠当作 Python 的转义字符来看待。
               Matplotlib 有一个內建的 TeX 方程式的语法分析器和布局引擎,并带有自己的数学字体(详情参见  Writing mathematical expressions )。因此你可以不用安装TeX就能够跨平台使用数学表达式,对于那些已经安装了LaTeX和dvipng的用户,你也可以使用LaTex 来格式化你的文字并将输出的文本并入到你要展示的图形或者要保存的脚本中(详情参见 Text rendering With LaTeX)。

     注释文本 

    上面讲到过 text()命令的基本作用是在坐标系的任意位置添加一个文本,而文本通常是用来对图形的某些特征进行注释,annotate() 方法提供了一些非常有用的功能使得注释变得更加容易。对于注释有两个需要考虑的点:使用 xy 参数来代表需要被注释的位置;使用 xytext 参数来指定注释文本的放置位置;这两个参数的取值均为(x,y) 的组合。

    ax = plt.subplot(111)
    
    t = np.arange(0.0, 5.0, 0.01)
    s = np.cos(2*np.pi*t)
    line, = plt.plot(t, s, lw=2)
    
    plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
                 arrowprops=dict(facecolor='black', shrink=0.05),
                 )
    
    plt.ylim(-2, 2)
    plt.show()

     

    在这个基础示例中,xy (箭头提示) 和 xytext(文本位置)都是在数据的坐标系中,这里还有许多其他种类的坐标系可以选择(详情参见  Basic annotationAdvanced Annotation),更多示例参见: Annotating Plots

     对数及其他非线性坐标系

    matplotlib.pylot 除了支持线性坐标系外,也支持对数和分对数等非线性坐标系,当数据的量级跨度比较大时会经常用到这些坐标系,改变一个坐标轴的刻度非常容易:

    plt.xscale('log')

    下例中展示了对于同样的数据,当Y轴使用不同刻度时的四个图形:

    from matplotlib.ticker import NullFormatter  # useful for `logit` scale
    
    # Fixing random state for reproducibility
    np.random.seed(19680801)
    
    # make up some data in the interval ]0, 1[
    y = np.random.normal(loc=0.5, scale=0.4, size=1000)
    y = y[(y > 0) & (y < 1)]
    y.sort()
    x = np.arange(len(y))
    
    # plot with various axes scales
    plt.figure(1)
    
    # linear
    plt.subplot(221)
    plt.plot(x, y)
    plt.yscale('linear')
    plt.title('linear')
    plt.grid(True)
    
    
    # log
    plt.subplot(222)
    plt.plot(x, y)
    plt.yscale('log')
    plt.title('log')
    plt.grid(True)
    
    
    # symmetric log
    plt.subplot(223)
    plt.plot(x, y - y.mean())
    plt.yscale('symlog', linthreshy=0.01)
    plt.title('symlog')
    plt.grid(True)
    
    # logit
    plt.subplot(224)
    plt.plot(x, y)
    plt.yscale('logit')
    plt.title('logit')
    plt.grid(True)
    # Format the minor tick labels of the y-axis into empty strings with
    # `NullFormatter`, to avoid cumbering the axis with too many labels.
    plt.gca().yaxis.set_minor_formatter(NullFormatter())
    # Adjust the subplot layout, because the logit one may take more space
    # than usual, due to y-tick labels like "1 - 10^{-3}"
    plt.subplots_adjust(top=0.92, bottom=0.08, left=0.10, right=0.95, hspace=0.25,
                        wspace=0.35)
    
    plt.show()

    你也可以添加你自定义的刻度,详情参见:Developer's guide for creating scales and transformations

    展开全文
  • 小甲鱼零基础入门学习python笔记

    万次阅读 多人点赞 2019-08-14 11:06:30
    一个 Shell 脚本主要由原本需要在命令行输入的命令组成,或在一个文本编辑器中,用户可以使用脚本来把一些常用的操作组合成一组串行。主要用来书写这种脚本的语言叫做脚本语言。很多脚本 语言实际上已经超过简单的...

    小甲鱼老师零基础入门学习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 Shellshell的意思就是“外壳”,基本上来说,就是一个通过键入文本与程序交互的途径

    我们看到>>>这个提示符,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”没有“变量”,只有“名字”。

    需要注意的地方

    在使用变量之前,需要对其先赋值。

    变量名可以包括字母、数字、下划线,但变量名不能以数字开头

    字母可以是大写或小写,但大小写是不同的。也就是说fishcFishC对于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分以上成绩为A8090B6080C60以下为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语言编写而来的,但是Tafor循环跟C语言的for循环不太一样,Pythonfor循环显得更为智能和强大!

    语法:

    for 目标 in 表达式:

         循环体

    每次取FishC中一个字符及空格输出

    range()函数

    语法:range( [strat],[stop],[step] )

    这个BIF有三个参数,其中用中括号括起来的两个表示这两个参数是可选的。

    step=1表示第三个参数的值默认值是1setp为每步距离

    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 

    •也可以通过innot in判断一个元素是否在集合中已经存在

    >>> 0 in set1
    True
    >>> 8 in set1
    False

    使用add()方法可以为集合添加元素,使用remove()方法可以删除集合中已知的元素:

    >>> 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.txtboy_2.txt, girl_2.txt, boy_3.txt, gril_3.txt6个文件(提示:文件中不同的对话间已经使用“==========分割

    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.__name
    Traceback (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.__name
    Traceback (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.size
    Traceback (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

    反运算:

    反运算与算术运算符的不同之处是,反运算多了一个'r',例如 __add__()的反运算对应为 __radd__()

    >>> a + b

    这里a是加数,b是被加数,如果a对象的__add__()方法没有实现或者不支持相应的操作,那么python就会自动调用b的__radd__()方法

    实例:

    >>> 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 魔法方法:简单定制

    简单定制

    基本要求:

    定制一个计时器的类

    startstop方法代表启动计时和停止计时

    假设计时器对象t1print(t1)和直接调用t1均显示结果

    当计时器未启动或已经停止计时,调用stop方法会给予温馨的提示

    两个计时器对象可以进行相加:t1 + t2

    只能使用提供的有限资源完成

    你需要这些资源

    使用time模块的localtime方法获取时间

    扩展阅读:time 模块详解(时间获取和转换)

    有关time模块的localtime方法获取时间(参考:

    https://fishc.com.cn/forum.php?mod=viewthread&tid=51326&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

    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秒'        

    进阶定制

    如果开始计时的时间是202222216:30:30,停止时间是202512315:30:30,那按照我们用停止时间减开始时间的计算方式就会出现负数3-11-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._x
    Traceback (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 TemperatureConversion
    Traceback (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由三部分组成:

    第一部分是协议httphttpsftpfileed2k…

    第二部分是存放资源的服务器的域名系统或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:隐藏

    修改 headers

    通过Requestheaders参数修改

    通过Request.add_header()方法修改

    代理

    步骤:

    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()

    实例3:Radiobutton 组件

    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列


    e1 = Entry(root)#在主窗口中插入输入框,文本框的内容通过e1调用
    e2 = Entry(root)#在主窗口中插入输入框
    e1.grid(row=0,column=1,padx=10)#x方向偏移是相对于"作品"的x方向偏移的;y方向偏移表示此输入框与y方向相邻物体或边框之间偏移的距离(y方向偏移)
    e2.grid(row=1,column=1,padx=10,pady=20)#x方向偏移是相对于"小甲鱼"的x方向偏移的;y方向偏移表示此输入框与y方向相邻上下物体或边框偏移的距离(y方向偏移)


    #加两个按钮
    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()

    e1 = Entry(root,textvariable = v1)#在主窗口中插入输入框,文本框的内容通过e1调用
    e2 = Entry(root,textvariable = v2,show="*")#在主窗口中插入输入框
    e1.grid(row=0,column=1,padx=10,pady=5)#x方向偏移是相对于"作品"的x方向偏移的;y方向偏移表示此输入框与y方向相邻物体或边框之间偏移的距离(y方向偏移)
    e2.grid(row=1,column=1,padx=10,pady=5)#x方向偏移是相对于"小甲鱼"的x方向偏移的;y方向偏移表示此输入框与y方向相邻上下物体或边框偏移的距离(y方向偏移)


    #可以使用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函数

    e1 = Entry(master, textvariable=v, validate="focusout", validatecommand=test)
    e2 = Entry(master)
    e1.pack(padx=10, pady=10)
    e2.pack(padx=10, pady=10)

    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)

    #创建一个按钮,宽度为10
    button=Button(frame,text="计算结果",width=10,command=calc)
    button.grid(row=1,column=2,pady=10)

    mainloop()

    068 GUI的终极选择:Tkinter5

    Listbox组件

    如果需要提供选项给用户选择,单选可以用Radiobutton组件,多选可以用Checkbutton,如果提供的选项非常多,可以考虑使用Listbox组件。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()

    Mask用法

    mask(标记)通常是嵌入到Text组件文本中的不可见对象。事实上,Marks是指定字符间的位置,并跟随相应的字符一起移动。

    实例: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()

    #INSERT索引表示插入光标当前的位置
    text.insert(INSERT,"I love FishC.com!")#光标当前的位置插入
    #注意,行号从1开始,列号则从0开始
    text.tag_add("link","1.7","1.16")#1.7(第一行第八列)到1.16
    #设置蓝色前景色并底部划线
    text.tag_config("link",foreground="blue",underline=True)

    #当进入绑定文本段时,鼠标样式切换为“arrow"形态
    text.tag_bind("link","<Enter>",show_hand_cursor)
    #当离开绑定文本段时,鼠标样式切换为“xterm"形态
    text.tag_bind("link","<Leave>",show_arrow_cursor)
    #当触发鼠标“左键单击”时,使用默认浏览器打开鱼C网址
    text.tag_bind("link","<Button-1>",click)

    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")

    w.create_text(100,80,text="Hadley")

    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组件

    Tkinter提供了一个Menu组件,用于实现顶级菜单、下拉菜单和弹出菜单。

    实例1:创建一个顶级菜单(或称窗口主菜单

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()


    menubar = Menu(root)#创建一个顶级菜单
    menubar.add_command(label="Hello",command=callback)#创建一个顶级菜单对象
    menubar.add_command(label="Quit",command=root.quit)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    实例2:创建添加到主菜单上的下拉菜单

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单
    menubar = Menu(root)

    #创建下拉菜单filemenu包含内容
    filemenu=Menu(menubar,tearoff=False)#创建一个从属于menubar的子菜单(下拉菜单)filemenu
    filemenu.add_command(label="打开",command=callback)#创建一个下拉菜单对象
    filemenu.add_command(label="保存",command=callback)
    filemenu.add_separator()#插入分隔线
    filemenu.add_command(label="退出",command=root.quit)
    #创建一个顶级菜单对象“文件”,filemenu从属于这个对象(或称将filemenu添加到顶级菜单“文件”中)
    menubar.add_cascade(label="文件",menu=filemenu)

    #创建另一个下拉菜单editmenu包含内容
    editmenu=Menu(menubar,tearoff=False)#创建一个从属于menubar的子菜单(下拉菜单)editmenu
    editmenu.add_command(label="剪切",command=callback)
    editmenu.add_command(label="拷贝",command=callback)
    editmenu.add_separator()#插入分隔线
    editmenu.add_command(label="粘贴",command=callback)
    #创建一个顶级菜单对象“编辑”,editmenu从属于这个对象(或称将editmenu添加到顶级菜单“编辑”中)
    menubar.add_cascade(label="编辑",menu=editmenu)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    实例3:创建一个弹出菜单方法

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    def popup(event):
        menu.post(event.x_root,event.y_root)#在此时鼠标位置弹出显示窗口
        
    #创建一个顶级菜单menu
    menu = Menu(root,tearoff=False)

    #创建顶级菜单menu包含内容
    menu.add_command(label="撤销",command=callback)#创建一个顶级菜单对象
    menu.add_command(label="重做",command=callback)
    #创建一个框架
    frame = Frame(root,width=100,height=100)
    frame.pack()

    #将鼠标右键与popup方法绑定
    frame.bind("<Button-3>",popup)

    #显示菜单
    #root.config(menu=menu)

    mainloop()

    实例4:菜单弹出

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    def popup(event):
        menu.post(event.x_root,event.y_root)#在此时鼠标位置弹出显示窗口
        
    #创建一个顶级菜单menu
    menu = Menu(root,tearoff=True)

    #创建顶级菜单menu包含内容
    menu.add_command(label="撤销",command=callback)#创建一个顶级菜单对象
    menu.add_command(label="重做",command=callback)
    #创建一个框架
    frame = Frame(root,width=500,height=500)
    frame.pack()

    #将鼠标右键与popup方法绑定
    frame.bind("<Button-3>",popup)

    #显示菜单
    #root.config(menu=menu)

    mainloop()

    实例5:添加单选组件radiobutton和多选按钮checkbutton

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单
    menubar = Menu(root)
    #创建checkbutton关联变量
    openVar = IntVar()
    saveVar = IntVar()
    exitVar = IntVar()
    #创建下拉菜单filemenu包含内容
    filemenu=Menu(menubar,tearoff=True)#创建一个从属于menubar的子菜单(下拉菜单)filemenu
    filemenu.add_checkbutton(label="打开",command=callback,variable=openVar)#创建一个下拉菜单对象
    filemenu.add_checkbutton(label="保存",command=callback,variable=saveVar)
    filemenu.add_separator()#插入分隔线
    filemenu.add_checkbutton(label="退出",command=root.quit,variable=exitVar)
    #创建一个顶级菜单对象“文件”,filemenu从属于这个对象(或称将filemenu添加到顶级菜单“文件”中)
    menubar.add_cascade(label="文件",menu=filemenu)

    #创建radiobutton关联变量
    editVar = IntVar()
    editVar.set(1)

    #创建另一个下拉菜单editmenu包含内容
    editmenu=Menu(menubar,tearoff=True)#创建一个从属于menubar的子菜单(下拉菜单)editmenu
    editmenu.add_radiobutton(label="剪切",command=callback,variable=editVar,value=1)
    editmenu.add_radiobutton(label="拷贝",command=callback,variable=editVar,value=2)
    editmenu.add_separator()#插入分隔线
    editmenu.add_radiobutton(label="粘贴",command=callback,variable=editVar,value=3)
    #创建一个顶级菜单对象“编辑”,editmenu从属于这个对象(或称将editmenu添加到顶级菜单“编辑”中)
    menubar.add_cascade(label="编辑",menu=editmenu)

    #显示菜单
    root.config(menu=menubar)

    mainloop()

    Menubutton组件(希望菜单按钮出现在其它位置时)

    Menubutton组件是一个与Menu组件相关联的按钮,它可以放在窗口中的任意位置,并且在被按下时弹出下拉菜单

    实例1:

    from tkinter import *

    def callback():
        print("被调用了")
        
    root = Tk()

    #创建一个顶级菜单Menubutton按钮,设置为浮起显示(RAISED)
    mb = Menubutton(root,text="点我",relief=RAISED)

    mb.pack(side=RIGHT)#设置为右中显示

    #创建下拉菜单filemenu包含内容
    filemenu = Menu(mb,tearoff=False)#创建一个从属于mb的下拉菜单filemenu
    filemenu.add_checkbutton(label="打开",command=callback,selectcolor="yellow")
    filemenu.add_command(label="保存",command=callback)#创建一个下拉菜单对象"保存“
    filemenu.add_separator()
    filemenu.add_command(label="退出",command=root.quit)
    #显示菜单
    mb.config(menu=filemenu)

    mainloop()

    OptionMenu(选项菜单)组件

    选项菜单的发明弥补了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指定的是子组件相对于父组件的位置,范围是(00`1.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指定的是子组件相对于父组件的位置,范围是(00`1.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指定的是子组件相对于父组件的位置,范围是(00`1.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(消息对话框)

    实例1:askokcancel函数

    from tkinter import *

    print(messagebox.askokcancel("FishC Demo","发射核弹?"))

    mainloop()

    实例2:askquestion函数

    实例3:asiretrycancel函数

    实例4:askyesno函数

    实例5:showerror函数

    from tkinter import *

    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    print(messagebox.showerror("FishC Demo","Error!!!"))

    mainloop()

    实例6:showinfo函数

    from tkinter import *

    #options参数可设置为default、icon与parent
    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    #print(messagebox.showerror("FishC Demo","Error!!!"))
    messagebox.showinfo("Hadley","Great!!!",icon="info")

    mainloop()

    实例7:showwarning函数

    from tkinter import *

    #options参数可设置为default、icon与parent
    #print(messagebox.askokcancel("FishC Demo","发射核弹?"))
    #print(messagebox.askquestion("FishC Demo","买个U盘?"))
    #print(messagebox.askretrycancel("FishC Demo","启动失败,重启?"))
    #print(messagebox.askyesno("FishC Demo","你确定要格式化硬盘吗?"))
    #print(messagebox.showerror("FishC Demo","Error!!!"))
    #messagebox.showinfo("Hadley","Great!!!",icon="info")
    messagebox.showwarning("Hadley","Warning!!!",icon="warning")

    mainloop()

    filedialog(文本对话框)

    当应用程序需要使用打开文件或保存文件的功能时

    实例1:

    from tkinter import *

    root = Tk()

    def callback():
        #askopenfilename函数用来打开文件
        #asksaveasfilename函数用来保存文件
        fileName = filedialog.askopenfilename()
        print(fileName)

    Button(root,text="打开文件夹",command=callback).pack()

    mainloop()

    实例2:限制打开文件类型

    from tkinter import *

    root = Tk()

    def callback():
        #askopenfilename函数用来打开文件
        #asksaveasfilename函数用来保存文件
        #fileName = filedialog.askopenfilename()
        #限制打开文件类型
        fileName = filedialog.askopenfilename(filetypes=[("PNG",".png"),("GIF",".gif")])
        print(fileName)

    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:初次见面,请大家多多关照

     

    展开全文
  • 入门学习Linux常用必会60个命令实例详解doc/txt

    千次下载 热门讨论 2011-06-09 00:08:45
    ◆ 系统管理相关命令:df、top、free、quota、at、lp、adduser、groupadd、kill、crontab; ◆ 网络操作命令:ifconfig、ip、ping、netstat、telnet、ftp、route、rlogin、rcp、finger、mail、 nslookup; ◆ 系统...
  • MySQL 面试题

    万次阅读 多人点赞 2019-09-02 16:03:33
    4、复合索引:将多个列组合在一起创建索引,可以覆盖多个列。 5、外键索引:只有InnoDB类型的表才可以使用外键索引,保证数据的一致性、完整性和实现级联操作。 6、全文索引:MySQL 自带的全文索引只能...

    MySQL 面试题

    MySQL 涉及的内容非常非常非常多,所以面试题也容易写的杂乱。当年,我们记着几个一定要掌握的重心:

    重点的题目添加了【重点】前缀。

    1. 索引。
    2. 锁。
    3. 事务和隔离级别。

    因为 MySQL 还会有部分内容和运维相关度比较高,所以本文我们分成两部分【开发】【运维】两部分。

    • 对于【开发】部分,我们需要掌握。
    • 对于【运维】部分,更多考验开发的知识储备情况,当然能回答出来是比较好的,特别是对于高级开发工程师、架构师等。

    开发

    为什么互联网公司一般选择 MySQL 而不是 Oracle?

    免费、流行、够用。

    ? 当然,这个回答要稍微润色下。不过一般,很少问这个问题了。

    数据库的三范式是什么?什么是反模式?

    艿艿:重点在于反模式的回答。实际开发中,不会严格遵守三范式。

    胖友直接看 《服务端指南 数据存储篇 | MySQL(07) 范式与反模式》

    MySQL 有哪些数据类型?

    MySQL 支持多种类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。具体可以看看 《MySQL 数据类型》 文档。

    • 正确的使用数据类型,对数据库的优化是非常重要的。

    ? MySQL 中 varchar 与 char 的区别?varchar(50) 中的 50 代表的涵义?

    • 1、varchar 与 char 的区别,char 是一种固定长度的类型,varchar 则是一种可变长度的类型。
    • 2、varchar(50) 中 50 的涵义最多存放 50 个字符。varchar(50) 和 (200) 存储 hello 所占空间一样,但后者在排序时会消耗更多内存,因为 ORDER BY col 采用 fixed_length 计算 col 长度(memory引擎也一样)。所以,实际场景下,选择合适的 varchar 长度还是有必要的。

    ? int(11) 中的 11 代表什么涵义?

    int(11) 中的 11 ,不影响字段存储的范围,只影响展示效果。具体可以看看 《MySQL 中 int 长度的意义》 文章。

    ? 金额(金钱)相关的数据,选择什么数据类型?

    • 方式一,使用 int 或者 bigint 类型。如果需要存储到分的维度,需要 *100 进行放大。
    • 方式二,使用 decimal 类型,避免精度丢失。如果使用 Java 语言时,需要使用 BigDecimal 进行对应。

    ? 一张表,里面有 ID 自增主键,当 insert 了 17 条记录之后,删除了第 15,16,17 条记录,再把 MySQL 重启,再 insert 一条记录,这条记录的 ID 是 18 还是 15?

    • 一般情况下,我们创建的表的类型是 InnoDB ,如果新增一条记录(不重启 MySQL 的情况下),这条记录的 ID 是18 ;但是如果重启 MySQL 的话,这条记录的 ID 是 15 。因为 InnoDB 表只把自增主键的最大 ID 记录到内存中,所以重启数据库或者对表 OPTIMIZE 操作,都会使最大 ID 丢失。
    • 但是,如果我们使用表的类型是 MyISAM ,那么这条记录的 ID 就是 18 。因为 MyISAM 表会把自增主键的最大 ID 记录到数据文件里面,重启 MYSQL 后,自增主键的最大 ID 也不会丢失。

    最后,还可以跟面试官装个 x ,生产数据,不建议进行物理删除记录。

    ? 表中有大字段 X(例如:text 类型),且字段 X 不会经常更新,以读为为主,请问您是选择拆成子表,还是继续放一起?写出您这样选择的理由

    • 拆带来的问题:连接消耗 + 存储拆分空间。

      如果能容忍拆分带来的空间问题,拆的话最好和经常要查询的表的主键在物理结构上放置在一起(分区) 顺序 IO ,减少连接消耗,最后这是一个文本列再加上一个全文索引来尽量抵消连接消耗。

    • 不拆可能带来的问题:查询性能。

      如果能容忍不拆分带来的查询性能损失的话,上面的方案在某个极致条件下肯定会出现问题,那么不拆就是最好的选择。

    实际场景下,例如说商品表数据量比较大的情况下,会将商品描述单独存储到一个表中。即,使用拆的方案。

    MySQL 有哪些存储引擎?

    MySQL 提供了多种的存储引擎:

    • InnoDB
    • MyISAM
    • MRG_MYISAM
    • MEMORY
    • CSV
    • ARCHIVE
    • BLACKHOLE
    • PERFORMANCE_SCHEMA
    • FEDERATED

    具体每种存储引擎的介绍,可以看看 《数据库存储引擎》

    ? 如何选择合适的存储引擎?

    提供几个选择标准,然后按照标准,选择对应的存储引擎即可,也可以根据 常用引擎对比 来选择你使用的存储引擎。使用哪种引擎需要根据需求灵活选择,一个数据库中多个表可以使用不同的引擎以满足各种性能和实际需求。使用合适的存储引擎,将会提高整个数据库的性能。

    1. 是否需要支持事务。

    2. 对索引和缓存的支持。

    3. 是否需要使用热备。

    4. 崩溃恢复,能否接受崩溃。

    5. 存储的限制。

    6. 是否需要外键支持。

      艿艿:目前开发已经不考虑外键,主要原因是性能。具体可以看看 《从 MySQL 物理外键开始的思考》 文章。

    目前,MySQL 默认的存储引擎是 InnoDB ,并且也是最主流的选择。主要原因如下:

    • 【最重要】支持事务。
    • 支持行级锁和表级锁,能支持更多的并发量。
    • 查询不加锁,完全不影响查询。
    • 支持崩溃后恢复。

    在 MySQL5.1 以及之前的版本,默认的存储引擎是 MyISAM ,但是目前已经不再更新,且它有几个比较关键的缺点:

    • 不支持事务。
    • 使用表级锁,如果数据量大,一个插入操作锁定表后,其他请求都将阻塞。

    艿艿:也就是说,我们不需要花太多力气在 MyISAM 的学习上。

    ? 请说明 InnoDB 和 MyISAM 的区别

    InnoDBMyISAM
    事务支持不支持
    存储限制64TB
    锁粒度行锁表锁
    崩溃后的恢复支持不支持
    外键支持不支持
    全文检索5.7 版本后支持支持

    更完整的对比,可以看看 《数据库存储引擎》「常用引擎对比」 小节。

    ? 请说说 InnoDB 的 4 大特性?

    艿艿:貌似我面试没被问过…反正,我是没弄懂过~~

    • 插入缓冲(insert buffer)
    • 二次写(double write)
    • 自适应哈希索引(ahi)
    • 预读(read ahead)

    ? 为什么 SELECT COUNT(*) FROM table 在 InnoDB 比 MyISAM 慢?

    对于 SELECT COUNT(*) FROM table 语句,在没有 WHERE 条件的情况下,InnoDB 比 MyISAM 可能会慢很多,尤其在大表的情况下。因为,InnoDB 是去实时统计结果,会全表扫描;而 MyISAM 内部维持了一个计数器,预存了结果,所以直接返回即可。

    详细的原因,胖友可以看看 《高性能 MySQL 之 Count 统计查询》 博客。

    ? 各种不同 MySQL 版本的 Innodb 的改进?

    艿艿:这是一个选择了解的问题。

    MySQL5.6 下 Innodb 引擎的主要改进:

    1. online DDL
    2. memcached NoSQL 接口
    3. transportable tablespace( alter table discard/import tablespace)
    4. MySQL 正常关闭时,可以 dump 出 buffer pool 的( space, page_no),重启时 reload,加快预热速度
    5. 索引和表的统计信息持久化到 mysql.innodb_table_stats 和 mysql.innodb_index_stats,可提供稳定的执行计划
    6. Compressed row format 支持压缩表

    MySQL5.7 下 Innodb 引擎的主要改进:

    • 1、修改 varchar 字段长度有时可以使用

      这里的“有时”,指的是也有些限制。可见 《MySQL 5.7 online ddl 的一些改进》

    • 2、Buffer pool 支持在线改变大小

    • 3、Buffer pool 支持导出部分比例

    • 4、支持新建 innodb tablespace,并可以在其中创建多张表

    • 5、磁盘临时表采用 innodb 存储,并且存储在 innodb temp tablespace 里面,以前是 MyISAM 存储

    • 6、透明表空间压缩功能

    重点】什么是索引?

    索引,类似于书籍的目录,想找到一本书的某个特定的主题,需要先找到书的目录,定位对应的页码。

    MySQL 中存储引擎使用类似的方式进行查询,先去索引中查找对应的值,然后根据匹配的索引找到对应的数据行。

    ? 索引有什么好处?

    1. 提高数据的检索速度,降低数据库IO成本:使用索引的意义就是通过缩小表中需要查询的记录的数目从而加快搜索的速度。
    2. 降低数据排序的成本,降低CPU消耗:索引之所以查的快,是因为先将数据排好序,若该字段正好需要排序,则正好降低了排序的成本。

    ? 索引有什么坏处?

    1. 占用存储空间:索引实际上也是一张表,记录了主键与索引字段,一般以索引文件的形式存储在磁盘上。
    2. 降低更新表的速度:表的数据发生了变化,对应的索引也需要一起变更,从而减低的更新速度。否则索引指向的物理数据可能不对,这也是索引失效的原因之一。

    ? 索引的使用场景?

    • 1、对非常小的表,大部分情况下全表扫描效率更高。

    • 2、对中大型表,索引非常有效。

    • 3、特大型的表,建立和使用索引的代价随着增长,可以使用分区技术来解决。

      实际场景下,MySQL 分区表很少使用,原因可以看看 《互联网公司为啥不使用 MySQL 分区表?》 文章。

      对于特大型的表,更常用的是“分库分表”,目前解决方案有 Sharding Sphere、MyCAT 等等。

    ? 索引的类型?

    索引,都是实现在存储引擎层的。主要有六种类型:

    • 1、普通索引:最基本的索引,没有任何约束。

    • 2、唯一索引:与普通索引类似,但具有唯一性约束。

    • 3、主键索引:特殊的唯一索引,不允许有空值。

    • 4、复合索引:将多个列组合在一起创建索引,可以覆盖多个列。

    • 5、外键索引:只有InnoDB类型的表才可以使用外键索引,保证数据的一致性、完整性和实现级联操作。

    • 6、全文索引:MySQL 自带的全文索引只能用于 InnoDB、MyISAM ,并且只能对英文进行全文检索,一般使用全文索引引擎。

      常用的全文索引引擎的解决方案有 Elasticsearch、Solr 等等。最为常用的是 Elasticsearch 。

    具体的使用,可以看看 《服务端指南 数据存储篇 | MySQL(03) 如何设计索引》

    ? MySQL 索引的“创建”原则?

    注意,是“创建”噢。

    • 1、最适合索引的列是出现在 WHERE 子句中的列,或连接子句中的列,而不是出现在 SELECT 关键字后的列。

    • 2、索引列的基数越大,索引效果越好。

      具体为什么,可以看看如下两篇文章:

    • 3、根据情况创建复合索引,复合索引可以提高查询效率。

      因为复合索引的基数会更大。

    • 4、避免创建过多的索引,索引会额外占用磁盘空间,降低写操作效率。

    • 5、主键尽可能选择较短的数据类型,可以有效减少索引的磁盘占用提高查询效率。

    • 6、对字符串进行索引,应该定制一个前缀长度,可以节省大量的索引空间。

    ? MySQL 索引的“使用”注意事项?

    注意,是“使用”噢。

    • 1、应尽量避免在 WHERE 子句中使用 !=<> 操作符,否则将引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。

      注意,column IS NULL 也是不可以使用索引的。

    • 2、应尽量避免在 WHERE 子句中使用 OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:SELECT id FROM t WHERE num = 10 OR num = 20

    • 3、应尽量避免在 WHERE 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。

    • 4、应尽量避免在 WHERE 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。

    • 5、不要在 WHERE 子句中的 = 左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

    • 6、复合索引遵循前缀原则。

    • 7、如果 MySQL 评估使用索引比全表扫描更慢,会放弃使用索引。如果此时想要索引,可以在语句中添加强制索引。

    • 8、列类型是字符串类型,查询时一定要给值加引号,否则索引失效。

    • 9、LIKE 查询,% 不能在前,因为无法使用索引。如果需要模糊匹配,可以使用全文索引。

    关于这块,可以看看 《服务端指南 数据存储篇 | MySQL(04) 索引使用的注意事项》 文章,写的更加细致。

    ? 以下三条 SQL 如何建索引,只建一条怎么建?

    WHERE a = 1 AND b = 1
    WHERE b = 1
    WHERE b = 1 ORDER BY time DESC
    
    
    • 以顺序 b , a, time 建立复合索引,CREATE INDEX table1_b_a_time ON index_test01(b, a, time)
    • 对于第一条 SQL ,因为最新 MySQL 版本会优化 WHERE 子句后面的列顺序,以匹配复合索引顺序。

    ? 想知道一个查询用到了哪个索引,如何查看?

    EXPLAIN 显示了 MYSQL 如何使用索引来处理 SELECT 语句以及连接表,可以帮助选择更好的索引和写出更优化的查询语句。

    使用方法,在 SELECT 语句前加上 EXPLAIN 就可以了。 《MySQL explain 执行计划详细解释》

    【重点】MySQL 索引的原理?

    解释 MySQL 索引的原理,篇幅会比较长,并且网络上已经有靠谱的资料可以看,所以艿艿这里整理了几篇,胖友可以对照着看。

    下面,艿艿对关键知识做下整理,方便胖友回顾。

    几篇好一点的文章:

    《MySQL索引背后的数据结构及算法原理》

    《MySQL 索引原理》

    《深入理解 MySQL 索引原理和实现 —— 为什么索引可以加速查询?》

    MySQL 有哪些索引方法?

    在 MySQL 中,我们可以看到两种索引方式:

    什么是 B-Tree 索引?

    B-Tree 是为磁盘等外存储设备设计的一种平衡查找树。因此在讲 B-Tree 之前先了解下磁盘的相关知识。

    • 系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。
    • InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB 存储引擎中默认每个页的大小为 16 KB,可通过参数 innodb_page_size 将页的大小设置为 4K、8K、16K ,在 MySQL 中可通过如下命令查看页的大小:
    mysql> show variables like 'innodb_page_size';
    
    • 而系统一个磁盘块的存储空间往往没有这么大,因此 InnoDB 每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小 16KB 。InnoDB 在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘 I/O 次数,提高查询效率。

    B-Tree 结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录为一个二元组 [key, data] ,key 为记录的键值,对应表中的主键值,data 为一行记录中除主键外的数据。对于不同的记录,key值互不相同。

    一棵 m 阶的 B-Tree 有如下特性:

    1. 每个节点最多有 m 个孩子。
      • 除了根节点和叶子节点外,其它每个节点至少有 Ceil(m/2) 个孩子。
      • 若根节点不是叶子节点,则至少有 2 个孩子。
    2. 所有叶子节点都在同一层,且不包含其它关键字信息。
    3. 每个非叶子节点包含 n 个关键字信息(P0,P1,…Pn, k1,…kn)
      • 关键字的个数 n 满足:ceil(m/2)-1 <= n <= m-1
      • ki(i=1,…n) 为关键字,且关键字升序排序。
      • Pi(i=0,…n) 为指向子树根节点的指针。P(i-1) 指向的子树的所有节点关键字均小于 ki ,但都大于 k(i-1) 。

    B-Tree 中的每个节点根据实际情况可以包含大量的关键字信息和分支,如下图所示为一个 3 阶的 B-Tree:

    B-Tree 的结构

    • 每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的 key 和三个指向子树根节点的 point ,point 存储的是子节点所在磁盘块的地址。两个 key 划分成的三个范围域,对应三个 point 指向的子树的数据的范围域。
    • 以根节点为例,key 为 17 和 35 ,P1 指针指向的子树的数据范围为小于 17 ,P2 指针指向的子树的数据范围为 [17~35] ,P3 指针指向的子树的数据范围为大于 35 。

    模拟查找 key 为 29 的过程:

    • 1、根据根节点找到磁盘块 1 ,读入内存。【磁盘I/O操作第1次】
    • 2、比较 key 29 在区间(17,35),找到磁盘块 1 的指针 P2 。
    • 3、根据 P2 指针找到磁盘块 3 ,读入内存。【磁盘I/O操作第2次】
    • 4、比较 key 29 在区间(26,30),找到磁盘块3的指针P2。
    • 5、根据 P2 指针找到磁盘块 8 ,读入内存。【磁盘I/O操作第3次】
    • 6、在磁盘块 8 中的 key 列表中找到 eky 29 。

    分析上面过程,发现需要 3 次磁盘 I/O 操作,和 3 次内存查找操作。由于内存中的 key 是一个有序表结构,可以利用二分法查找提高效率。而 3 次磁盘 I/O 操作是影响整个 B-Tree 查找效率的决定因素。B-Tree 相对于 AVLTree 缩减了节点个数,使每次磁盘 I/O 取到内存的数据都发挥了作用,从而提高了查询效率。

    什么是 B+Tree 索引?

    B+Tree 是在 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用 B+Tree 实现其索引结构。

    从上一节中的 B-Tree 结构图中可以看到,每个节点中不仅包含数据的 key 值,还有 data 值。而每一个页的存储空间是有限的,如果 data 数据较大时将会导致每个节点(即一个页)能存储的 key 的数量很小,当存储的数据量很大时同样会导致 B-Tree 的深度较大,增大查询时的磁盘 I/O 次数,进而影响查询效率。在 B+Tree 中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储 key 值信息,这样可以大大加大每个节点存储的 key 值数量,降低 B+Tree 的高度。

    B+Tree 相对于 B-Tree 有几点不同:

    • 非叶子节点只存储键值信息。
    • 所有叶子节点之间都有一个链指针。
    • 数据记录都存放在叶子节点中。

    将上一节中的 B-Tree 优化,由于 B+Tree 的非叶子节点只存储键值信息,假设每个磁盘块能存储 4 个键值及指针信息,则变成 B+Tree 后其结构如下图所示:

    B+Tree 的结构

    • 通常在 B+Tree 上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对 B+Tree 进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

    可能上面例子中只有 22 条数据记录,看不出 B+Tree 的优点,下面做一个推算:

    • InnoDB 存储引擎中页的大小为 16KB,一般表的主键类型为 INT(占用4个字节) 或 BIGINT(占用8个字节),指针类型也一般为 4 或 8 个字节,也就是说一个页(B+Tree 中的一个节点)中大概存储 16KB/(8B+8B)=1K 个键值(因为是估值,为方便计算,这里的 K 取值为〖10〗^3)。也就是说一个深度为 3 的 B+Tree 索引可以维护10^3 *10^3 *10^3 = 10亿 条记录。
    • 实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree 的高度一般都在 2~4 层。MySQL 的 InnoDB 存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要 1~3 次磁盘 I/O 操作。

    B+Tree 有哪些索引类型?

    在 B+Tree 中,根据叶子节点的内容,索引类型分为主键索引非主键索引

    • 主键索引的叶子节点存的数据是整行数据( 即具体数据 )。在 InnoDB 里,主键索引也被称为聚集索引(clustered index)。
    • 非主键索引的叶子节点存的数据是整行数据的主键,键值是索引。在 InnoDB 里,非主键索引也被称为辅助索引(secondary index)。

    辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,需要进过两步:

    • 首先,InnoDB 存储引擎会遍历辅助索引找到主键。
    • 然后,再通过主键在聚集索引中找到完整的行记录数据。

    另外,InnoDB 通过主键聚簇数据,如果没有定义主键,会选择一个唯一的非空索引代替,如果没有这样的索引,会隐式定义个主键作为聚簇索引。

    再另外,可能有胖友有和艿艿的一样疑惑,在辅助索引如果相同的索引怎么存储?最终存储到 B+Tree 非子节点中时,它们对应的主键 ID 是不同的,所以妥妥的。如下图所示:

    相同的索引怎么存储

    聚簇索引的注意点有哪些?

    聚簇索引表最大限度地提高了 I/O 密集型应用的性能,但它也有以下几个限制:

    • 1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。

      关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。

    • 2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。

      MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。

    • 3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。

      当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。

    • 4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。

    什么是索引的最左匹配特性?

    当 B+Tree 的数据项是复合的数据结构,比如索引 (name, age, sex) 的时候,B+Tree 是按照从左到右的顺序来建立搜索树的。

    • 比如当 (张三, 20, F) 这样的数据来检索的时候,B+Tree 会优先比较 name 来确定下一步的所搜方向,如果 name 相同再依次比较 age 和 sex ,最后得到检索的数据。
    • 但当 (20, F) 这样的没有 name 的数据来的时候,B+Tree 就不知道下一步该查哪个节点,因为建立搜索树的时候 name 就是第一个比较因子,必须要先根据 name 来搜索才能知道下一步去哪里查询。
    • 比如当 (张三, F) 这样的数据来检索时,B+Tree 可以用 name 来指定搜索方向,但下一个字段 age 的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是 F 的数据了。

    这个是非常重要的性质,即索引的最左匹配特性。

    MyISAM 索引实现?

    MyISAM 索引的实现,和 InnoDB 索引的实现是一样使用 B+Tree ,差别在于 MyISAM 索引文件和数据文件是分离的,索引文件仅保存数据记录的地址

    MyISAM 索引与 InnoDB 索引的区别?

    • InnoDB 索引是聚簇索引,MyISAM 索引是非聚簇索引。
    • InnoDB 的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
    • MyISAM 索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
    • InnoDB 非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。

    【重点】请说说 MySQL 的四种事务隔离级别?

    • 1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。

      关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。

    • 2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。

      MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。

    • 3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。

      当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。

    • 4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。

    • 1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。

      关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。

    • 2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。

      MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。

    • 3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。

      当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。

    • 4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。

    • 1、插入速度严重依赖于插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于 InnoDB 表,我们一般都会定义一个自增的 ID 列为主键。

      关于这一点,可能面试官会换一个问法。例如,为什么主键需要是自增 ID ,又或者为什么主键需要带有时间性关联。

    • 2、更新主键的代价很高,因为将会导致被更新的行移动。因此,对于InnoDB 表,我们一般定义主键为不可更新。

      MySQL 默认情况下,主键是允许更新的。对于 MongoDB ,其 主键是不允许更新的。

    • 3、二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。

      当然,有一种情况可以无需二次查找,基于非主键索引查询,但是查询字段只有主键 ID ,那么在二级索引中就可以查找到。

    • 4、主键 ID 建议使用整型。因为,每个主键索引的 B+Tree 节点的键值可以存储更多主键 ID ,每个非主键索引的 B+Tree 节点的数据可以存储更多主键 ID 。

    事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。

    这样可以防止出现脏数据,防止数据库数据出现问题。

    事务的特性指的是?

    指的是 ACID ,如下图所示:

    事务的特性

    1. 原子性 Atomicity :一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
    2. 一致性 Consistency :在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束触发器级联回滚等。
    3. 隔离性 Isolation :数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
    4. 持久性 Durability :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

    事务的并发问题?

    实际场景下,事务并不是串行的,所以会带来如下三个问题:

    • 1、脏读:事务 A 读取了事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据。
    • 2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果不一致。
    • 3、幻读:系统管理员 A 将数据库中所有学生的成绩从具体分数改为 ABCDE 等级,但是系统管理员 B 就在这个时候插入了一条具体分数的记录,当系统管理员 A 改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

    小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

    MySQL 事务隔离级别会产生的并发问题?

    • READ UNCOMMITTED(未提交读):事务中的修改,即使没有提交,对其他事务也都是可见的。

      会导致脏读。

    • READ COMMITTED(提交读):事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。

      会导致不可重复读。

      这个隔离级别,也可以叫做“不可重复读”。

    • REPEATABLE READ(可重复读):一个事务按相同的查询条件读取以前检索过的数据,其他事务插入了满足其查询条件的新数据。产生幻行。

      会导致幻读。

    • SERIALIZABLE(可串行化):强制事务串行执行。

    事务隔离级别脏读不可重复读幻读
    读未提交(read-uncommitted)
    读已提交(read-committed)
    可重复读(repeatable-read)是(x)
    串行化(serializable)
    • MySQL 默认的事务隔离级别为可重复读(repeatable-read) 。
    • 上图的 <X> 处,MySQL 因为其间隙锁的特性,导致其在可重复读(repeatable-read)的隔离级别下,不存在幻读问题。也就是说,上图 <X> 处,需要改成“否”!!!!
    • ? 记住这个表的方式,我们会发现它是自左上向右下是一个对角线。当然,最好是去理解。
    • 具体的实验,胖友可以看看 《MySQL 的四种事务隔离级别》

    【重点】请说说 MySQL 的锁机制?

    表锁是日常开发中的常见问题,因此也是面试当中最常见的考察点,当多个查询同一时刻进行数据修改时,就会产生并发控制的问题。MySQL 的共享锁和排他锁,就是读锁和写锁。

    • 共享锁:不堵塞,多个用户可以同时读一个资源,互不干扰。
    • 排他锁:一个写锁会阻塞其他的读锁和写锁,这样可以只允许一个用户进行写入,防止其他用户读取正在写入的资源。

    ? 锁的粒度?

    • 表锁:系统开销最小,会锁定整张表,MyIsam 使用表锁。
    • 行锁:最大程度的支持并发处理,但是也带来了最大的锁开销,InnoDB 使用行锁。

    ? 什么是悲观锁?什么是乐观锁?

    1)悲观锁

    它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

    在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。

    2)乐观锁

    相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

    而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。

    什么是死锁?

    多数情况下,可以认为如果一个资源被锁定,它总会在以后某个时间被释放。而死锁发生在当多个进程访问同一数据库时,其中每个进程拥有的锁都是其他进程所需的,由此造成每个进程都无法继续下去。简单的说,进程 A 等待进程 B 释放他的资源,B 又等待 A 释放他的资源,这样就互相等待就形成死锁。

    虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件:

    • 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    • 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
    • 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
    • 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合 {P0,P1,P2,•••,Pn} 中的 P0 正在等待一个 P1 占用的资源;P1 正在等待 P2 占用的资源,……,Pn 正在等待已被 P0 占用的资源。

    下列方法有助于最大限度地降低死锁:

    • 设置获得锁的超时时间。

      通过超时,至少保证最差最差最差情况下,可以有退出的口子。

    • 按同一顺序访问对象。

      这个是最重要的方式。

    • 避免事务中的用户交互。

    • 保持事务简短并在一个批处理中。

    • 使用低隔离级别。

    • 使用绑定连接。

    ? MySQL 中 InnoDB 引擎的行锁是通过加在什么上完成(或称实现)的?为什么是这样子的??

    InnoDB 是基于索引来完成行锁。例如:SELECT * FROM tab_with_index WHERE id = 1 FOR UPDATE

    • FOR UPDATE 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么 InnoDB 将完成表锁,并发将无从谈起。

    【重要】MySQL 查询执行顺序?

    MySQL 查询执行的顺序是:

    (1)     SELECT
    (2)     DISTINCT <select_list>
    (3)     FROM <left_table>
    (4)     <join_type> JOIN <right_table>
    (5)     ON <join_condition>
    (6)     WHERE <where_condition>
    (7)     GROUP BY <group_by_list>
    (8)     HAVING <having_condition>
    (9)     ORDER BY <order_by_condition>
    (10)    LIMIT <limit_number>
    

    具体的,可以看看 《SQL 查询之执行顺序解析》 文章。

    【重要】聊聊 MySQL SQL 优化?

    可以看看如下几篇文章:

    另外,除了从 SQL 层面进行优化,也可以从服务器硬件层面,进一步优化 MySQL 。具体可以看看 《MySQL 数据库性能优化之硬件优化》

    编写 SQL 查询语句的考题合集

    MySQL 数据库 CPU 飙升到 500% 的话,怎么处理?

    当 CPU 飙升到 500% 时,先用操作系统命令 top 命令观察是不是 mysqld 占用导致的,如果不是,找出占用高的进程,并进行相关处理。

    如果此时是 IO 压力比较大,可以使用 iostat 命令,定位是哪个进程占用了磁盘 IO 。

    如果是 mysqld 造成的,使用 show processlist 命令,看看里面跑的 Session 情况,是不是有消耗资源的 SQL 在运行。找出消耗高的 SQL ,看看执行计划是否准确, index 是否缺失,或者实在是数据量太大造成。一般来说,肯定要 kill 掉这些线程(同时观察 CPU 使用率是否下降),等进行相应的调整(比如说加索引、改 SQL 、改内存参数)之后,再重新跑这些 SQL。

    也可以查看 MySQL 慢查询日志,看是否有慢 SQL 。

    也有可能是每个 SQL 消耗资源并不多,但是突然之间,有大量的 Session 连进来导致 CPU 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等。

    ? 在 MySQL 服务器运行缓慢的情况下输入什么命令能缓解服务器压力?

    1)检查系统的状态

    通过操作系统的一些工具检查系统的状态,比如 CPU、内存、交换、磁盘的利用率,根据经验或与系统正常时的状态相比对,有时系统表面上看起来看空闲,这也可能不是一个正常的状态,因为 CPU 可能正等待IO的完成。除此之外,还应观注那些占用系统资源(CPU、内存)的进程。

    • 使用 sar 来检查操作系统是否存在 IO 问题。
    • 使用 vmstat 监控内存 CPU 资源。
    • 磁盘 IO 问题,处理方式:做 raid10 提高性能 。
    • 网络问题,telnet 一下 MySQL 对外开放的端口。如果不通的话,看看防火墙是否正确设置了。另外,看看 MySQ L是不是开启了 skip-networking 的选项,如果开启请关闭。

    2)检查 MySQL 参数

    • max_connect_errors
    • connect_timeout
    • skip-name-resolve
    • slave-net-timeout=seconds
    • master-connect-retry

    3)检查 MySQL 相关状态值

    • 关注连接数
    • 关注下系统锁情况
    • 关注慢查询(slow query)日志

    Innodb 的事务与日志的实现方式

    ? 有多少种日志?

    • redo 日志
    • undo 日志

    ? 日志的存放形式?

    • redo:在页修改的时候,先写到 redo log buffer 里面, 然后写到 redo log 的文件系统缓存里面(fwrite),然后再同步到磁盘文件(fsync)。
    • undo:在 MySQL5.5 之前,undo 只能存放在 ibdata* 文件里面, 5.6 之后,可以通过设置 innodb_undo_tablespaces 参数把 undo log 存放在 ibdata* 之外。

    ? 事务是如何通过日志来实现的,说得越深入越好

    艿艿:这个流程的理解还是比较简单的,实际思考实现感觉还是蛮复杂的。

    基本流程如下:

    • 因为事务在修改页时,要先记 undo ,在记 undo 之前要记 undo 的 redo, 然后修改数据页,再记数据页修改的 redo。 redo(里面包括 undo 的修改)一定要比数据页先持久化到磁盘。
    • 当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的状态。
    • 崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo 把该事务的修改回滚到事务开始之前。如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。

    MySQL binlog 的几种日志录入格式以及区别

    ? 各种日志格式的涵义

    binlog 有三种格式类型,分别如下:

    1)Statement

    每一条会修改数据的 SQL 都会记录在 binlog 中。

    • 优点:不需要记录每一行的变化,减少了 binlog 日志量,节约了 IO,提高性能。(相比 row 能节约多少性能与日志量,这个取决于应用的 SQL 情况,正常同一条记录修改或者插入 row 格式所产生的日志量还小于 Statement 产生的日志量,但是考虑到如果带条件的 update 操作,以及整表删除,alter 表等操作,ROW 格式会产生大量日志,因此在考虑是否使用 ROW 格式日志时应该跟据应用的实际情况,其所产生的日志量会增加多少,以及带来的 IO 性能问题。)

    • 缺点:由于记录的只是执行语句,为了这些语句能在 slave 上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在 slave 得到和在 master 端执行时候相同 的结果。另外 MySQL 的复制,像一些特定函数功能,slave 可与 master 上要保持一致会有很多相关问题(如 sleep() 函数,last_insert_id(),以及 user-defined functions(udf) 会出现问题)。

    • 使用以下函数的语句也无法被复制:

      • LOAD_FILE()

      • UUID()

      • USER()

      • FOUND_ROWS()

      • SYSDATE() (除非启动时启用了 --sysdate-is-now 选项)

        同时在 INSERT …SELECT 会产生比 RBR 更多的行级锁 。

    2)Row

    不记录 SQL 语句上下文相关信息,仅保存哪条记录被修改。

    • 优点:binlog 中可以不记录执行的 SQL 语句的上下文相关的信息,仅需要记录那一条记录被修改成什么了。所以 rowlevel 的日志内容会非常清楚的记录下每一行数据修改的细节。而且不会出现某些特定情况下的存储过程,或 function ,以及 trigger 的调用和触发无法被正确复制的问题。
    • 缺点:所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容,比如一条 Update 语句,修改多条记录,则 binlog 中每一条修改都会有记录,这样造成 binlog 日志量会很大,特别是当执行 alter table 之类的语句的时候,由于表结构修改,每条记录都发生改变,那么该表每一条记录都会记录到日志中。

    3)Mixedlevel

    是以上两种 level 的混合使用。

    • 一般的语句修改使用 Statement 格式保存 binlog 。
    • 如一些函数,statement 无法完成主从复制的操作,则采用 Row 格式保存 binlog 。

    MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 Statement 和 Row 之间选择 一种。

    新版本的 MySQL 中对 row level 模式也被做了优化,并不是所有的修改都会以 row level 来记录。

    • 像遇到表结构变更的时候就会以 Statement 模式来记录。
    • 至于 Update 或者 Delete 等修改数据的语句,还是会记录所有行的变更,即使用 Row 模式。

    ? 适用场景?

    在一条 SQL 操作了多行数据时, Statement 更节省空间,Row 更占用空间。但是, Row 模式更可靠。

    因为,互联网公司,使用 MySQL 的功能相对少,基本不使用存储过程、触发器、函数的功能,选择默认的语句模式,Statement Level(默认)即可。

    ? 结合第一个问题,每一种日志格式在复制中的优劣?

    • Statement 可能占用空间会相对小一些,传送到 slave 的时间可能也短,但是没有 Row 模式的可靠。
    • Row 模式在操作多行数据时更占用空间,但是可靠。

    所以,这是在占用空间和可靠之间的选择。

    如何在线正确清理 MySQL binlog?

    MySQL 中的 binlog 日志记录了数据中的数据变动,便于对数据的基于时间点和基于位置的恢复。但日志文件的大小会越来越大,占用大量的磁盘空间,因此需要定时清理一部分日志信息。

    # 首先查看主从库正在使用的binlog文件名称
    show master(slave) status
    
    # 删除之前一定要备份
    purge master logs before'2017-09-01 00:00:00'; # 删除指定时间前的日志
    purge master logs to'mysql-bin.000001'; # 删除指定的日志文件
    
    # 自动删除:通过设置binlog的过期时间让系统自动删除日志
    show variables like 'expire_logs_days'; # 查看过期时间
    set global expire_logs_days = 30; # 设置过期时间
    

    MySQL 主从复制的流程是怎么样的?

    MySQL 的主从复制是基于如下 3 个线程的交互(多线程复制里面应该是 4 类线程):

    • 1、Master 上面的 binlog dump 线程,该线程负责将 master 的 binlog event 传到 slave 。
    • 2、Slave 上面的 IO 线程,该线程负责接收 Master 传过来的 binlog,并写入 relay log 。
    • 3、Slave 上面的 SQL 线程,该线程负责读取 relay log 并执行。
    • 4、如果是多线程复制,无论是 5.6 库级别的假多线程还是 MariaDB 或者 5.7 的真正的多线程复制, SQL 线程只做 coordinator ,只负责把 relay log 中的 binlog 读出来然后交给 worker 线程, woker 线程负责具体 binlog event 的执行。

    ? MySQL 如何保证复制过程中数据一致性?

    • 1、在 MySQL5.5 以及之前, slave 的 SQL 线程执行的 relay log 的位置只能保存在文件( relay-log.info)里面,并且该文件默认每执行 10000 次事务做一次同步到磁盘, 这意味着 slave 意外 crash 重启时, SQL 线程执行到的位置和数据库的数据是不一致的,将导致复制报错,如果不重搭复制,则有可能会导致数据不一致。
      • MySQL 5.6 引入参数 relay_log_info_repository,将该参数设置为 TABLE 时, MySQL 将 SQL 线程执行到的位置存到 mysql.slave_relay_log_info 表,这样更新该表的位置和 SQL 线程执行的用户事务绑定成一个事务,这样 slave 意外宕机后,slave 通过 innodb 的崩溃恢复可以把 SQL 线程执行到的位置和用户事务恢复到一致性的状态。
    • 2、MySQL 5.6 引入 GTID 复制,每个 GTID 对应的事务在每个实例上面最多执行一次, 这极大地提高了复制的数据一致性。
    • 3、MySQL 5.5 引入半同步复制, 用户安装半同步复制插件并且开启参数后,设置超时时间,可保证在超时时间内如果 binlog 不传到 slave 上面,那么用户提交事务时不会返回,直到超时后切成异步复制,但是如果切成异步之前用户线程提交时在 master 上面等待的时候,事务已经提交,该事务对 master 上面的其他 session 是可见的,如果这时 master 宕机,那么到 slave 上面该事务又不可见了,该问题直到 5.7 才解决。
    • 4、MySQL 5.7 引入无损半同步复制,引入参 rpl_semi_sync_master_wait_point,该参数默认为 after_sync,指的是在切成半同步之前,事务不提交,而是接收到 slave 的 ACK 确认之后才提交该事务,从此,复制真正可以做到无损的了。
    • 5、可以再说一下 5.7 的无损复制情况下, master 意外宕机,重启后发现有 binlog 没传到 slave 上面,这部分 binlog 怎么办???分 2 种情况讨论, 1 宕机时已经切成异步了, 2 是宕机时还没切成异步??? 这个怎么判断宕机时有没有切成异步呢??? 分别怎么处理???

    ? MySQL 如何解决主从复制的延时性?

    5.5 是单线程复制,5.6 是多库复制(对于单库或者单表的并发操作是没用的),5.7 是真正意义的多线程复制,它的原理是基于 group commit, 只要 master 上面的事务是 group commit 的,那 slave 上面也可以通过多个 worker线程去并发执行。 和 MairaDB10.0.0.5 引入多线程复制的原理基本一样。

    ? 工作遇到的复制 bug 的解决方法?

    5.6 的多库复制有时候自己会停止,我们写了一个脚本重新 start slave 。

    ? 你是否做过主从一致性校验,如果有,怎么做的,如果没有,你打算怎么做?

    主从一致性校验有多种工具 例如 checksum、mysqldiff、pt-table-checksum 等。

    聊聊 MySQL 备份方式?备份策略是怎么样的?

    具体的,胖友可以看看 《MySQL 高级备份策略》 。主要有几个知识点:

    • 数据的备份类型

      • 【常用】完全备份

        这是大多数人常用的方式,它可以备份整个数据库,包含用户表、系统表、索引、视图和存储过程等所有数据库对象。但它需要花费更多的时间和空间,所以,一般推荐一周做一次完全备份。

      • 增量备份

        它是只备份数据库一部分的另一种方法,它不使用事务日志,相反,它使用整个数据库的一种新映象。它比最初的完全备份小,因为它只包含自上次完全备份以来所改变的数据库。它的优点是存储和恢复速度快。推荐每天做一次差异备份。

      • 【常用】事务日志备份

        事务日志是一个单独的文件,它记录数据库的改变,备份的时候只需要复制自上次备份以来对数据库所做的改变,所以只需要很少的时间。为了使数据库具有鲁棒性,推荐每小时甚至更频繁的备份事务日志。

      • 文件备份

        数据库可以由硬盘上的许多文件构成。如果这个数据库非常大,并且一个晚上也不能将它备份完,那么可以使用文件备份每晚备份数据库的一部分。由于一般情况下数据库不会大到必须使用多个文件存储,所以这种备份不是很常用。

    • 备份数据的类型

      • 热备份
      • 温备份
      • 冷备份
    • 备份工具

      • cp
      • mysqldump
      • xtrabackup
      • lvm2 快照

    MySQL 几种备份方式?

    MySQL 一般有 3 种备份方式。

    1)逻辑备份

    使用 MySQL 自带的 mysqldump 工具进行备份。备份成sql文件形式。

    • 优点:最大好处是能够与正在运行的 MySQL 自动协同工作,在运行期间可以确保备份是当时的点,它会自动将对应操作的表锁定,不允许其他用户修改(只能访问)。可能会阻止修改操作。SQL 文件通用方便移植。
    • 缺点:备份的速度比较慢。如果是数据量很多的时候,就很耗时间。如果数据库服务器处在提供给用户服务状态,在这段长时间操作过程中,意味着要锁定表(一般是读锁定,只能读不能写入数据),那么服务就会影响的。

    2)物理备份

    艿艿:因为现在主流是 InnoDB ,所以基本不再考虑这种方式。

    直接拷贝只适用于 MyISAM 类型的表。这种类型的表是与机器独立的。但实际情况是,你设计数据库的时候不可能全部使用 MyISAM 类型表。你也不可能因为 MyISAM 类型表与机器独立,方便移植,于是就选择这种表,这并不是选择它的理由。

    • 缺点:你不能去操作正在运行的 MySQL 服务器(在拷贝的过程中有用户通过应用程序访问更新数据,这样就无法备份当时的数据),可能无法移植到其他机器上去。

    3)双机热备份。

    当数据量太大的时候备份是一个很大的问题,MySQL 数据库提供了一种主从备份的机制,也就是双机热备。

    • 优点:适合数据量大的时候。现在明白了,大的互联网公司对于 MySQL 数据备份,都是采用热机备份。搭建多台数据库服务器,进行主从复制。

    数据库不能停机,请问如何备份? 如何进行全备份和增量备份?

    可以使用逻辑备份和双机热备份。

    • 完全备份:完整备份一般一段时间进行一次,且在网站访问量最小的时候,这样常借助批处理文件定时备份。主要是写一个批处理文件在里面写上处理程序的绝对路径然后把要处理的东西写在后面,即完全备份数据库。
    • 增量备份:对 ddl 和 dml 语句进行二进制备份。且 5.0 无法增量备份,5.1 后可以。如果要实现增量备份需要在 my.ini 文件中配置备份路径即可,重启 MySQL 服务器,增量备份就启动了。

    ? 你的备份工具的选择?备份计划是怎么样的?

    视库的大小来定,一般来说 100G 内的库,可以考虑使用 mysqldump 来做,因为 mysqldump 更加轻巧灵活,备份时间选在业务低峰期,可以每天进行都进行全量备份(mysqldump 备份出来的文件比较小,压缩之后更小)。

    100G 以上的库,可以考虑用 xtrabackup 来做,备份速度明显要比 mysqldump 要快。一般是选择一周一个全备,其余每天进行增量备份,备份时间为业务低峰期。

    备份恢复时间是多长?

    物理备份恢复快,逻辑备份恢复慢。

    这里跟机器,尤其是硬盘的速率有关系,以下列举几个仅供参考:

    • 20G 的 2 分钟(mysqldump)
    • 80G 的 30分钟(mysqldump)
    • 111G 的 30分钟(mysqldump)
    • 288G 的 3 小时(xtrabackup)
    • 3T 的 4 小时(xtrabackup)

    逻辑导入时间一般是备份时间的 5 倍以上。

    备份恢复失败如何处理?

    首先在恢复之前就应该做足准备工作,避免恢复的时候出错。比如说备份之后的有效性检查、权限检查、空间检查等。如果万一报错,再根据报错的提示来进行相应的调整。

    ? mysqldump 和 xtrabackup 实现原理?

    1)mysqldump

    mysqldump 是最简单的逻辑备份方式。

    • 在备份 MyISAM 表的时候,如果要得到一致的数据,就需要锁表,简单而粗暴。
    • 在备份 InnoDB 表的时候,加上 –master-data=1 –single-transaction 选项,在事务开始时刻,记录下 binlog pos 点,然后利用 MVCC 来获取一致的数据,由于是一个长事务,在写入和更新量很大的数据库上,将产生非常多的 undo ,显著影响性能,所以要慎用。
    • 优点:简单,可针对单表备份,在全量导出表结构的时候尤其有用。
    • 缺点:简单粗暴,单线程,备份慢而且恢复慢,跨 IDC 有可能遇到时区问题

    2)xtrabackup

    xtrabackup 实际上是物理备份+逻辑备份的组合。

    • 在备份 InnoDB 表的时候,它拷贝 ibd 文件,并一刻不停的监视 redo log 的变化,append 到自己的事务日志文件。在拷贝 ibd 文件过程中,ibd文件本身可能被写”花”,这都不是问题,因为在拷贝完成后的第一个 prepare 阶段,xtrabackup 采用类似于 Innodb 崩溃恢复的方法,把数据文件恢复到与日志文件一致的状态,并把未提交的事务回滚。
    • 如果同时需要备份 MyISAM 表以及 InnoDB 表结构等文件,那么就需要用 flush tables with lock 来获得全局锁,开始拷贝这些不再变化的文件,同时获得 binlog 位置,拷贝结束后释放锁,也停止对 redo log 的监视。

    如何从 mysqldump 产生的全库备份中只恢复某一个库、某一张表?

    具体可见 《MySQL 全库备份中恢复某个库和某张表以及 mysqldump 参数 –ignore-table 介绍》 文章。

    聊聊 MySQL 集群?

    艿艿:这块艿艿懂的少,主要找了一些网络上的资料。

    ? 对于简历中写有熟悉 MySQL 高可用方案?

    我一般先问他现在管理的数据库架构是什么,如果他只说出了主从,而没有说任何 HA 的方案,那么我就可以判断出他没有实际的 HA 经验。

    不过这时候也不能就是断定他不懂 MySQL 高可用,也许是没有实际机会去使用,那么我就要问 MMM 以及 MHA 以及 MM + keepalived 等的原理、实现方式以及它们之间的优势和不足了,一般这种情况下,能说出这个的基本没有。

    • MMM 那东西好像不靠谱,据说不稳定,但是有人在用的,和 mysql-router 比较像,都是指定可写的机器和只读机器。
    • MHA 的话一句话说不完,可以搜索下相关博客。

    聊聊 MySQL 安全?

    感兴趣的胖友,可以看看:

    MySQL 有哪些日志?

    • 错误日志:记录了当 mysqld 启动和停止时,以及服务器在运行过程中发生任何严重错误时的相关信息。

    • 二进制文件:记录了所有的 DDL(数据定义语言)语句和 DML(数据操纵语言)语句,不包括数据查询语句。语句以“事件”的形式保存,它描述了数据的更改过程。(定期删除日志,默认关闭)。

      就是我们上面看到的 MySQL binlog 日志。

    • 查询日志:记录了客户端的所有语句,格式为纯文本格式,可以直接进行读取。(log 日志中记录了所有数据库的操作,对于访问频繁的系统,此日志对系统性能的影响较大,建议关闭,默认关闭)。

    • 慢查询日志:慢查询日志记录了包含所有执行时间超过参数long_query_time(单位:秒)所设置值的 SQL 语句的日志。(纯文本格式)

      重要,一定要开启。

    另外,错误日志和慢查询日志的详细解释,可以看看 《MySQL 日志文件之错误日志和慢查询日志详解》 文章。

    聊聊 MySQL 监控?

    你是如何监控你们的数据库的?

    监控的工具有很多,例如 Zabbix ,Lepus ,我这里用的是 Lepus

    对一个大表做在线 DDL ,怎么进行实施的才能尽可能降低影响?

    使用 pt-online-schema-change ,具体可以看看 《MySQL 大表在线 DML 神器–pt-online-schema-change》 文章。

    另外,还有一些其它的工具,胖友可以搜索下。

    展开全文
  • 前端面试锦集

    千次阅读 多人点赞 2019-07-20 13:41:45
    3.优化CSS(压缩合并css,如margin-top,margin-left…) 4.网址后加斜杠(如www.campr.com/目录,会判断这个“目录是什么文件类型,或者是目录。) 5.标明高度和宽度(如果浏览器没有找到这两个参数,它需要一边...

    前端面试锦集

    1. 自我介绍:
    2. List item.html常用的标签
      Div,span,p,h1-h5,input,img,strong,a,hr,br
      http://www.divcss5.com/html/h50219.shtml
    3. html5的新特性
      (1)websocket:https://www.runoob.com/html/html5-websocket.html
      WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
      HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
      在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
    4. ajax的工作原理:
      通过XmlHttpRequest对象来向服务器发出异步请求,从服务器获得数据,然后用Javascript来操作DOM而更新页面
      Ajax优势:
      页面无刷新,在页面内与服务器通信,给用户的体验非常好
      使用异步方式与服务器通信,不需要打断用户操作,具有更加迅速的响应能力
      可以嫌少服务器的负担,利用客户端闲置能力来处理任务
      Ajax工作流程:
      创建Ajax对象(XmlHttpRequest)
      判断数据的传输方式(get/post)
      打开连接open()
      发送send()
      ajax的js写法
    //get方法
    var ajax = new XMLHttpRequest();
    ajax.open('get','getStar.php?starName='+name);
    ajax.send();
    ajax.onreadystatechange = function () {
       if (ajax.readyState==4 &&ajax.status==200) {
        console.log(ajax.responseText);//输入相应的内容
        }
    }
    
    //post方法
    var xhr = new XMLHttpRequest();
    //post请求一定要添加请求头才行不然会报错
    xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
     xhr.open('post', '02.post.php' );
    xhr.send('name=fox&age=18');
    xhr.onreadystatechange = function () {  if (xhr.readyState == 4 && xhr.status == 200) {
        console.log(xhr.responseText);
      } 
    };
    
    1. js基本数据类型:Boolean null string number undefined
      复杂数据类型:object
      全局数据类型:Marth

    2. 浏览器前端发送url到服务器的过程
      https://www.cnblogs.com/max-tlp/p/6827956.html
      大体分为三步:
      一、浏览器向服务器发送请求(在地址栏中输入网页地址)
      二、服务器收到请求找到资源返回给浏览器
      三、浏览器解析服务器返回的内容(页面显示的内容)。
      URL结构,DNS解析,DNS缓存,三次tcp握手,http请求,http响应,浏览器渲染,前后端交互,四次挥手https://www.jianshu.com/p/417ac86ef086

    3. 响应式布局怎么实现
      (1) 原生代码实现:@media screen可以查询当前浏览器的尺寸,因此可采用该方法对同一个页面设置不同的CSS样式,来满足不同分辨率要求。
      (2) 采用bootstrap框架布局

    4. css实现水平垂直居中
      水平居中 margin:0 auto;
      垂直居中: https://www.cnblogs.com/zhouhuan/p/vertical_center.html

    5. 页面是怎样加载和渲染的过程

    6. 数组的操作

    7. vue.js的好处

    8. html5比html4好在哪里

    9. canvas调用了那些api

    10. js获取页面url

    11. 状态码有哪些 作用是什么 404 401 400 500 200 302等

    12. ajax请求实现的过程:
      AJAX 是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

    13. 前端技术栈有哪些
      vuejs:是一套构建用户界面的渐进式框架。它最大的优势就是组件化,用这个框架可以把很多元素封装成组件,最后功能的开发变成了组件的拼装,极大的提高了代码的复用。
      reactjs,boots TV,nodejs

    14. js出现点击上一个模块下一个模块同样被点击应如何处理。事件捕获事件冒泡

    15. 函数声明、函数表达式及匿名函数的区别和调用指向https://www.cnblogs.com/zhouhuan/p/vertical_center.html
      https://blog.csdn.net/weixin_44505375/article/details/86499744
      函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
      函数表达式 :var fnName = function () {…};使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
      匿名函数:function () {}; 使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,

    16. 立即执行函数(自执行函数)
      https://blog.csdn.net/qq_42051213/article/details/80767256
      立即执行函数的两种常见形式:( function(){…} )()和( function (){…} () ),一个是一个匿名函数包裹在一个括号运算符中,后面再跟一个小括号,另一个是一个匿名函数后面跟一个小括号,然后整个包裹在一个括号运算符中,这两种写法是等价的。要想立即执行函数能做到立即执行,要注意两点,一是函数体后面要有小括号(),二是函数体必须是函数表达式而不能是函数声明。
      除了使用()运算符之外,!,+,-,=等运算符都能起到立即执行的作用。这些运算符的作用就是将匿名函数或函数声明转换为函数表达式,如下图所示,函数体是函数声明的形式,使用运算符将其转换为函数表达式之后就可达到立即执行效果。
      使用立即执行函数的好处:通过定义一个匿名函数,创建了一个新的函数作用域,相当于创建了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏污染全局的命名空间。此时若是想访问全局对象,将全局对象以参数形式传进去即可。

    17. 什么是跨域,解释一下
      跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
      同源:是指,域名,协议,端口均相同,localhost和127.0.0.1虽然都指向本机,但也属于跨域。
      解决办法:
      1、JSONP:
      使用方式就不赘述了,但是要注意JSONP只支持GET请求,不支持POST请求。
      2、代理:
      例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。
      3、PHP端修改header(XHR2方式)
      在php接口脚本中加入以下两句即可:
      header(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
      header(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式

    18. JS中的位置和宽度:clientWidth、offsetWidth、scrollWidth等区别
      clientWidth = width+左右padding
      offsetWidth = width + 左右padding + 左右boder
      scrollWidth:获取指定标签内容层的真实宽度(可视区域宽度+被隐藏区域宽度比如滚动条)。
      width:content内容宽度。
      https://blog.csdn.net/qq_42089654/article/details/80515916

    19. 牛客网 https://www.nowcoder.com/profile/308592184/myFollowings

    20. 线程与进程的区别
      一个程序至少有一个进程,一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。
      另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
      线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
      从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

    21. 如何对网站的文件和资源进行优化?

    22. 请说出三种减少页面加载时间的方法。(加载时间指感知的时间或者实际加载时间)
      1.优化图片
      2.图像格式的选择(GIF:提供的颜色较少,可用在一些对颜色要求不高的地方)
      3.优化CSS(压缩合并css,如margin-top,margin-left…)
      4.网址后加斜杠(如www.campr.com/目录,会判断这个“目录是什么文件类型,或者是目录。)
      5.标明高度和宽度(如果浏览器没有找到这两个参数,它需要一边下载图片一边计算大小,如果图片很多,浏览器需要不断地调整页面。这不但影响速度,也影响浏览体验。当浏览器知道了高度和宽度参数后,即使图片暂时无法显示,页面上也会腾出图片的空位,然后继续加载后面的内容。从而加载时间快了,浏览体验也更好了。)
      6.减少http请求(合并文件,合并图片)。

    23. 请写一个简单的幻灯效果页面
      如果不使用JS来完成,可以加分。(如:纯CSS实现的幻灯片效果)
      (1) js实现方法

    //css
        <style type="text/css">
            * {
                margin: 0;
                padding: 0;
            }
            li{
                list-style: none;
            }
            #demo{
                width: 600px;
                height: 400px;
                position: relative;
                margin: 0 auto;
            }
            #list{
                position: relative;
                width: 100%;
                height: 100%;
            }
            #list li{
                width: 100%;
                height: 100%;
                position: absolute;
                opacity: 1;
            }
            #list li img{
                width: 100%;
                height: 100%;
            }
            #btn{
                position: absolute;
                right: 10px;
                bottom: 10px;
                z-index: 10;
    
            }
            #btn li{
                position: relative;
                float: left;
                width: 20px;
                height: 20px;
                background-color: #fb7a24;
                color: #ffffff;
                margin: 6px;
                text-align: center;
                border-radius: 90px;
                font-size: 13px;
            }
            #btn li:hover{
                background-color: #FF0000;
            }
            .current{
                background-color: #FF0000!important;
            }
        </style>
    <div id="demo">
        <ul id="list">
            <li><img src="img/img_1.jpg" alt=""></li>
            <li><img src="img/img_2.jpg" alt=""></li>
            <li><img src="img/img_3.jpg" alt=""></li>
            <li><img src="img/img_4.jpg" alt=""></li>
            <li><img src="img/img_5.jpg" alt=""></li>
        </ul>
        <ul id="btn">
            <li class="current">1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </ul>
    
    </div>
    //js
    <script type="text/javascript">
    
        var list=document.getElementById('list');
        var oLi=list.getElementsByTagName('li');
        console.log(oLi)
        var btn=document.getElementById('btn');
        var oImg=btn.getElementsByTagName('li');
        console.log(oImg)
       var time=null;
        var b=0;
        function show(a) {
            for(var i=0;i<oLi.length;i++)
            {
                oLi[i].style.opacity=0;
                oImg[i].className='';
            }
            oLi[a].style.opacity=200;
            oImg[a].className='current';
        }
            function autoPlay() {
            time=setInterval(function () {
                b++;
                if(b>=oImg.length)
                    b=0;
                show(b)
            },1000);
    }
    autoPlay();
        
       //鼠标控制
        for(var i=0;i<oImg.length;i++){
            oImg[i].index = i;
            oImg[i].onmouseover=function () {
                clearInterval(time);
                show(this.index)
            }
            oImg[i].onmouseout=function(){
                autoPlay();
            }
        }
    
    
    
    </script>
    

    (2)css3实现方法

    <div class="banner"></div>
    //css3
     <style type="text/css">
            .banner{
                width: 600px;
                height: 400px;
                margin: 15px auto;
                background-size: cover;
                background-position: center;
                animation-name: mymove;
                animation-duration: 20s;
                animation-timing-function: linear;
                animation-iteration-count: infinite;
            }
            @-webkit-keyframes mymove {
                0% {background: url(" img/img_1.jpg") no-repeat;}
               25% {background: url(" img/img_2.jpg") no-repeat;}
               50% {background: url(" img/img_3.jpg") no-repeat;}
               75% {background: url(" img/img_4.jpg") no-repeat;}
               100% {background: url(" img/img_5.jpg") no-repeat;}
           }
        </style>
    
    1. .什么是FOUC(无样式内容闪烁)?你如何来避免FOUC?
      FOUC(Flash Of Unstyled Content)–文档样式闪烁
      而引用CSS文件的@import就是造成这个问题的罪魁祸首。IE会先加载整个HTML文档的DOM,然后再去导入外部的CSS文件,因此,在页面DOM加载完成到CSS导入完成中间会有一段时间页面上的内容是没有样式的,这段时间的长短跟网速,电脑速度都有关系。解决方法简单的出奇,只要在 <head>之间加入一个<link>或者<script>元素就可以了。
    2. doctype(文档类型)的作用是什么?你知道多少种文档类型?
      此标签可告知浏览器文档使用哪种HTML或XHTML规范。该标签可声明三种DTD类型,分别表示严格版本、过渡版本以及基于框架的HTML文档。
    3. 使用XHTML的局限有哪些?
      XHTML 元素必须被正确地嵌套。
      XHTML 元素必须被关闭。
      标签名必须用小写字母。
      XHTML 文档必须拥有根元素。
      所有的 XHTML 文档必须拥有 DOCTYPE 声明,并且 html、head、title 和 body 元素必须存在。虽然代码更加的优雅,但缺少容错性,不利于快速开发。
    4. data-属性的作用是什么?
      data-* 属性用于存储页面或应用程序的私有自定义数据。data-* 属性赋予我们在所有 HTML 元素上嵌入自定义 data 属性的能力。存储的(自定义)数据能够被页面的 JavaScript 中利用,以创建更好的用户体验(不进行 Ajax 调用或服务器端数据库查询)。
      data-* 属性包括两部分:
      属性名不应该包含任何大写字母,并且在前缀 “data-” 之后必须有至少一个字符
      属性值可以是任意字符串
    5. 请描述一下cookies,sessionStorage和localStorage的区别?
      sessionStorage 和 localStorage 是HTML5 Web Storage API 提供的,可以方便的在web请求之间保存数据。有了本地数据,就可以避免数据在浏览器和服务器间不必要地来回传递。
      共同点:都是保存在浏览器端,且同源的。
      区别:
      1.cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。
      2.存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
      3.数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。
      4.作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。
      5.Web Storage 支持事件通知机制,可以将数据更新的通知发送给监听者。
      6.Web Storage 的 api 接口使用更方便。
    6. 描述下“reset”CSS文件的作用和使用它的好处。
      因为浏览器的品种很多,每个浏览器的默认样式也是不同的,所以定义一个css reset可以使各浏览器的默认样式统一。
    7. 22.列举不同的清除浮动的技巧,并指出它们各自适用的使用场景。
      1.使用空标签清除浮动。这种方法是在所有浮动标签后面添加一个空标签定义css clear:both.弊端就是增加了无意义标签。
      2.使用overflow。给包含浮动元素的父标签添加css属性overflow:auto;zoom:1;zoom:1用于兼容IE6。
      3.使用after伪对象清除浮动。该方法只适用于非IE浏览器。具体写法可参照以下示例。使用中需注意以下几点。一、该方法中必须为需要清除浮动元素的伪对象中设置height:0,否则该元素会比实际高出若干像素;二、content属性是必须的,但其值可以为空,content属性的值设为”.”,空亦是可以的。
      4.浮动外部元素
      此三种方法各有利弊,使用时应择优选择,比较之下第二种方法更为可取。
    8. 解释下CSS sprites,以及你要如何在页面或网站中使用它。
      CSS Sprites其实就是把网页中一些背景图片整合到一张图片文件中,再利用CSS的“background-image”,“background-repeat”,“background-position”的组合进行背景定位,background-position可以用数字能精确的定位出背景图片的位置。这样可以减少向服务端发起请求的次数,提高速率。
    9. 你用过媒体查询(响应式布局),或针对移动端的布局/CSS吗?
      媒体查询,就是响应式布局。通过不同的媒介类型和条件定义样式表规则。媒介查询让CSS可以更精确作用于不同的媒介类型和同一媒介的不同条件。
      语法结构及用法:@media 设备名 only (选取条件) not (选取条件) and(设备选取条件),设备二{sRules}。
      示例如下:
     /* 当浏览器的可视区域小于980px */
    
     @media screen and (max-width: 980px) {
     #wrap {width: 90%; margin:0 auto;}
     }
     /* 当浏览器的可视区域小于650px */
     @media screen and (max-width: 650px) {
    #header {height: auto;}
    }
    
    1. 你熟悉SVG样式的书写吗?
      SVG 意为可缩放矢量图形
      https://blog.csdn.net/dusea/article/details/49474349
      什么是SVG?
      SVG 指可伸缩矢量图形 (Scalable Vector Graphics)
      SVG 用来定义用于网络的基于矢量的图形
      SVG 使用 XML 格式定义图形
      SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失
      SVG 是万维网联盟的标准
      SVG 与诸如 DOM 和 XSL 之类的 W3C 标准是一个整体
      书写示例如下:
    <?xml version="1.0" standalone="no"?>
        <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
        <svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
            <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>
        </svg>
    
    1. css盒模型
    标准宽高
    标准模型content的宽度
    IE模型content+padding+border的宽度

    盒模型的组成:content,padding,border,margin
    css如何设置两种模型
    这里用到了CSS3 的属性 box-sizing

    /* 标准模型 */
    box-sizing:content-box;
    
     /*IE模型*/
    box-sizing:border-box;
    

    37.display属性的作用

    属性作用
    none此元素不会被显示
    block此元素将显示为块级元素,此元素前后会带有换行符
    inline默认。此元素会被显示为内联元素,元素前后没有换行符
    inline-block行内块元素
    1. css之position属性的作用
      relative 相对定位 相对于自身的位置定位,可以用left,bottom,top,right来改变位置
      fixed 固定定位 相对于整个页面来定位,滚动之后仍旧在那个位置
      absolute 绝对定位 如果父元素设置相对定位,那么就是针对父元素来定位的,否则就是针对浏览器来定位,可以用left,top,bottom,right来改变位置
      static 默认定位 默认即静止定位,是按照元素在(x)html出现的顺序依次分配位置,static是不受top right left bottom影响的。若要清除定位时可使用static来消除定位的影响。
    2. js事件代理。
      JavaScript事件代理则是一种简单的技巧,通过它你可以把事件处理器添加到一个父级元素上,这样就避免了把事件处理器添加到多个子级元素上。当我们需要对很多元素添加事件的时候,可以通过将事件添加到它们的父节点而将事件委托给父节点来触发处理函数。这主要得益于浏览器的事件冒泡机制。事件代理用到了两个在JavaSciprt事件中常被忽略的特性:事件冒泡以及目标元素。
    function getEventTarget(e) {
        var e=e||window.event;
        var src= e.target||e.srcElement;
        return src;
    }
    
    1. 解释下JavaScript中this是如何工作的。
      this永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。匿名函数或不处于任何对象中的函数指向window 。
      1.如果是call,apply,with,指定的this是谁,就是谁。
      2.普通的函数调用,函数被谁调用,this就是谁。
    2. document.write()方法为什么一般不用
    <html>
    <head></head>
    <body>
    <script type="text/javascript">document.write("<p>Hello</p>");</script>
    </body>
    </html>
    

    当页面加载完毕,你会在页面看到 Hello。查看源文件就是上面的代码。
    但如果页面已经加载完毕,再使用 document.write,那就会覆盖整个文档。

    <html>
    <head></head>
    <body>
    <script type="text/javascript">
    // 当点击鼠标时调用 document.write
    document.onclick = function() {
    document.write("<span>Javascript</span>");
    };
    </script>
    </body>
    </html>
    

    因为鼠标动作是在页面加载完毕后执行的,所以整个页面会被 Javascript所覆盖。现在查看源文件就只会看到 <span>Javascript</span>

    1. 请指出document.onload和document.ready两个事件的区别。
      页面加载完成有两种事件,一是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件),二是onload,指示页面包含图片等文件在内的所有元素都加载完成。
    2. 如何实现下列代码:[1,2,3,4,5].duplicator();//[1,2,3,4,5,1,2,3,4,5]
    var arr = [1,2,3,4,5];
        Array.prototype.duplicator = function (){
            var len = this.length;
            for(var i=0;i<len;i++){
                this.push(this[i]);
            }
            return this;
        }
           console.log(arr.duplicator())
    
    1. 什么是”use strict”;?使用它的好处和坏处分别是什么?
      在代码中出现表达式-“use strict”; 意味着代码按照严格模式解析,这种模式使得Javascript在更严格的条件下运行。
      好处:
      消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
      消除代码运行的一些不安全之处,保证代码运行的安全;
      提高编译器效率,增加运行速度;
      为未来新版本的Javascript做好铺垫。
      坏处
      同样的代码,在”严格模式”中,可能会有不一样的运行结果;一些在”正常模式”下可以运行的语句,在”严格模式”下将不能运行。
    2. javascript的typeof返回哪些数据类型.
      string,boolean,Array,undefined,object,function
    3. js强制类型转换和隐式类型转换
      强制类型转换:parseIn,parseFloat,number
      隐式类型转换:== ===
    4. splite和join的区别
      splite 是字符串切割成数组
      join 将数组合并成字符串
    5. splice和slice操作数组的区别
      splice(1,0,‘ww’) 在1的位置添加www
      splce(1,1,‘rrr’) 从位置1开始的1个元素 用rr替代删除的元素
      slice(start,end)从start位置开始选取元素到end截止 并且返回选择的元素
    6. 尾部 push() pop() 头部unshif() shif()
    7. js继承的几种方式:https://blog.csdn.net/wyw223/article/details/86567897
    8. js事件流:https://segmentfault.com/a/1190000004463384?utm_source=tag-newest
      DOM事件流包括三个阶段。
      事件捕获阶段
      处于目标阶段
      事件冒泡阶段
    9. CSS有哪些属性是可以继承的?
      常见:
        1、字体系列属性
        font-family:字体系列
        font-weight:字体的粗细
        font-size:字体的大小
        font-style:字体的风格
        2、文本系列属性
        text-indent:文本缩进
        text-align:文本水平对齐
        line-height:行高
        word-spacing:单词之间的间距
        letter-spacing:中文或者字母之间的间距
        text-transform:控制文本大小写(就是uppercase、lowercase、capitalize这三个)
        color:文本颜色
        3、元素可见性:
        visibility:控制元素显示隐藏
        4、列表布局属性:
        list-style:列表风格,包括list-style-type、list-style-image等
        5、光标属性:
        cursor:光标显示为何种形态
    10. 手动写动画最小的时间间隔是多久:
      多数显示器的默认频率是60HZ,即每秒刷新60次。所以理论上的最小间隔是 1/60*1000ms = 16.7ms

    关于vue的面试题:

    https://segmentfault.com/a/1190000018225708?utm_source=tag-newest

    数据结构

    1、数组
    2、栈
    3、队列
    4、链表
    5、树
    6、散列表
    7、堆
    8、图
    https://blog.csdn.net/yeyazhishang/article/details/82353846

    计算机网络

    IOS七层 TCP/UDP/IP等:
    https://blog.csdn.net/Trap_In_Study/article/details/87731638

    展开全文
  • CSS Sprites其实就是把网页中一些背景图片整合到一张图片文件中,再利用CSS的“background-image”,“background- repeat”,“background-position”的组合进行背景定位,background-position可以用数字能精确的...
  • 前端面试题(持续更新中)

    万次阅读 多人点赞 2019-11-06 17:16:33
    dom.getBoundingClientRect().width/height:getBoundingClientRect()方法得到8个值,除了 width 和 height 外的属性x、y、left、top、right和bottom都是相对于视口(viewport)的左上角位置而言的。 4、什么是BFC?...
  • 前端面试题

    万次阅读 多人点赞 2019-08-08 11:49:01
    我想让行内元素跟上面的元素距离10px,加margin-top和padding-top可以吗? 45 CSS的盒模型由什么组成? 45 说说display属性有哪些?可以做什么? 46 哪些css属性可以继承? 46 css优先级算法如何计算? 46 ...
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
    C#基础教程-c#实例教程,适合初学者。 第一章 C#语言基础 本章介绍C#语言的基础知识,希望具有C语言的读者能够基本掌握C#语言,并以此为基础,能够进一步学习用C#语言编写window应用程序和Web应用程序。...
  • Bootstrap

    千次阅读 多人点赞 2016-10-13 15:12:30
    终于下定决心来学习bootstrap了,2016-10-03。bootstrap安装 先看CDN:<!-- 放在head标签内 --> <!-- 新 Bootstrap 核心 CSS 文件 --> ...-- 可选的Bo
  • HTML学习

    千次阅读 多人点赞 2016-10-08 17:31:27
    graph with line breaks p > < br /> 元素是一个空的 HTML 元素。由于关闭标签没有任何意义,因此它没有结束标签。 < br > 还是 < br /> 您也许发现 < br > 与 < br /> 很相似。 在 XHTML、XML 以及...
  • 布局的时候经常使用到水平居中,所以 margin: ...绝对定位的时候则会使用left 和top = 50%的做法 再配合margin的外边距的方式 margin 进行居中处理。 8. opacity: 0.5; 透明度处理 使用opacity进行透明化处理。
  • 1.MPAndroidChart组合图使用(平滑曲线,折线(效果图里的直线)) 2.趋势图分析,趋势图的实现 3.markView不显示趋势线上的数据 4.MPAndroidChart数据精度问题解决 5.markView显示数据格式问题解决 三丶核心代码 1....
  • PySpark TopK 问题(分组TopK) 记录几种利用PySpark计算TopK的方法,准备使用两个例子,其中第一个例子是计算不同院系,不同班,不同学科的成绩前K名的分数。第二个例子以文本数据为例,计算在不同文本类别下出现TopK ...
  • 深入浅出vertical-align和line-height

    千次阅读 2018-07-06 14:00:51
    ‘inline-block’的基线是正常流中最后一个line box的基线, 除非,这个line box里面既没有line boxes或者本身’overflow’属性的计算值而不是’visible’, 这种情况下基线是margin底边缘。 这段文档中出现了很多专有...
  • Android-Command Line Tools-1

    千次阅读 2018-07-03 16:05:36
    一、概览 Android SDK由多个软件包组成,这些包是应用程序开发所必需的。这个页面列出了最重要的命令行工具,这些工具是由它们配送的包组织。 你可以安装和更新每一个软件包,使用Android Studio的SDK Manager ...
  • Leetcode分类解析:组合算法

    千次阅读 2016-07-04 23:47:17
    Leetcode分类解析:组合算法所谓组合算法就是指:在解决一些算法问题时,需要产生输入数据的各种组合、排列、子集、分区等等,然后逐一确认每种是不是我们要的解。从广义上来说,组合算法可以包罗万象,甚至排序、...
  • input+select组合使用封装

    千次阅读 2018-08-15 14:35:13
    一.封装成js ;(function($) { var tNum = 0; var InputSelect = function($element, options) { tNum ++; this.sNum = tNum; this.$element = $element; options = options || {};... v...
  • pyecharts 组合

    千次阅读 2020-06-14 23:39:50
    from pyecharts.charts import Bar, Line, Grid from pyecharts import options # 1. 准备数据 cate = ["湖北", "四川", "重庆", "河北", "云南"] Confirmed_diagnosis_data = [450, 300, 232, 224, 144] death_...
  • import tushare as ts from pyecharts.faker import Faker from pyecharts import options ...from pyecharts.charts import Bar,Kline,Line,Grid from pyecharts.globals import ThemeType df=ts.get_hist_da...
  • 最近面试遇到一个问题就是,Spark实现对统计 “年级, 班级, 学生, 分数” 的Top3; 除了此外问题还有: 每个班级分数前三名同学的名字以及分数; 各省指标数量前三的市的名字; 测试数据: 一年级、一...
  • 关于 -webkit-line-clamp 详解

    千次阅读 2016-08-25 11:56:00
    最近需要做个商品列表,在手机屏幕不太一样的市场里,如何做到列表中刚刚好显示2行,偶然间发现淘宝的手机版有用到-webkit-line-clamp来实现这种效果 ...为了实现该效果,它需要组合其他外来的WebK...
  • C++ 面试题大全

    千次阅读 多人点赞 2017-05-31 09:42:54
    1.new、delete、malloc、free关系 delete会调用对象的析构函数,和new对应,free只会释放内存,new调用构造函数。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放...
  • 树导航效果图:组合模式的描述:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。我们把部分用Leaf表示, 把整体用Composite表示。组合模式是有一定...
  • 鼎新TIPTOP GP/鼎捷易拓ERP系统介绍

    万次阅读 2018-09-13 15:17:39
    一、鼎捷易拓TIPTOP GP简介: 易拓ERP(TOP GP ERP)是以大型集团化企业为主要客户对象的ERP解决方案,并以“集团统筹、全球掌控”为核心理念。迄今已经协助数千家企业信息化实施,其范围覆盖快速成长型企业、大中...
  • 每条背景的组合 */ private ArrayList<BgColor> getBg () { ArrayList<BgColor> bgList = new ArrayList(); bgList.add( new BgColor( 10 , 20 , Color.YELLOW)); //参数信息:纵坐标从0到20设置颜色...
  • Pyecharts之折线图与柱状图组合绘制

    千次阅读 2021-02-27 09:32:13
    绘制折线图 #绘制 line_ensur=Line() 1、 绘制 确诊数折线图 #添加横轴 line_ensur.add_xaxis(x_data) #添加纵轴 line_ensur.add_yaxis(series_name='确诊数', y_axis=ensue_num, color='#4b0101', label_opts=opts....
  • ul li span 小组合

    千次阅读 2014-03-25 14:30:07
     line-height: 1.35em;  padding: 4px 20px; /* 内部填充的距离 10px 10px 10px 55px*/  text-decoration: none;  white-space: nowrap; /* 对于文本内的空白处,不会换行,文本会在在同一行上继续,...
  • from pyecharts import options as ...from pyecharts.charts import Bar, Line from pyecharts.faker import Faker # 数据 v1 = [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3] v2 = [2...
  • pyecharts/charts/composite_charts/包中的四个模块分别定义了组合图表类,其中pyecharts/charts/composite_charts/timeline.py模块只定义了时间线类Timeline。 Timeline类继承自图表基类Base,作用为在一个echarts...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,383
精华内容 17,353
关键字:

line组合top