python3 相关知识内容

python面向对象三大特征,面向对象的三大特性是指:封装、继承 和 多态。
python 是一门解释性动态语言

封装(隐藏)
'''
    隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只
    对外暴露“相关调用方法”。
    通过前面学习的“私有属性、私有方法”的方式,实现“封装”。Python 追求简洁的
    语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。
'''

继承
'''
    继承可以让子类具有父类的特性,提高了代码的重用性。
    从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。
'''

多态,可以理解为同一个东西,展现不同的形态
'''
    多态是指同一个方法调用由于对象不同会产生不同的行为。生活中这样的例子比比皆是:
    同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是“敲几行代码”。
'''

运行 python 的三种方式
1. 用 python 自带的 IDLE shell 运行 python
2. 用 CMD命令行 运行 Python 程序
3. 通过 pycharm 来运行 Python 程序

第一节
1. 关闭交互式窗口:
    (1) ctrl + z + enter
    (2) 输入 quit() 命令
    (3) 直接关闭命令行窗口

2. 中断程序执行:ctrl + C
3. 注释
    3.1 行注释:使用 # ,后面接着的都是说明,解释器不会读取,一个# 注释一行
    3.2 段注释:使用三个半角单引号,可以注释多行 ,如下所示,三个连续的单引号卷起来的,都会被当作注释,不会被读取
        ''' XXXXXXXX
            XXXXXXXX
            刚刚开始学习python ,请多多指教
            XXXXXXXX
        '''
        批量注释快捷键 : ctrl + /

    3.3 编辑器的字体大小调节
        设置 - 编辑器 - 常规 - 勾选 -- 使用 Ctrl + 鼠标轮更改字体大小

4. Python 标识符命名规则
    开发中,我们通常约定俗称遵守如下规则

    (1)模块和包名  全小写字母,尽量简单,若多个单词之间用下划线隔开 例如:math os sys
    (2)函数名      全小写字母,多个单词之间用下划线隔开             例如:phone my_name
    (3)类名        首字母大写,采用驼峰原则,多个单词时,           例如:Phone MyPhone MyClass
                   每个单词第一个字母大写,其余部分小写
    (4)常量名      全大写字母,多个单词使用下划线隔开               SPEED MAX_SPEED

第三节 - 变量
变量的命名规则
3.1. 变量由数字、字母、下划线组成,但是数字不能作为首字符,即 第一个字符必须是字母表中字母或下划线
3.2. 变量命名要见名知意
3.3. 保留字 不能作为变量名, python3 中有 35 个 保留字
3.4. 标识符对大小写敏感,即区分大小写,abc 和 ABC , 是两个变量。

    >>> import keyword
    >>> keyword.kwlist
    >>> #得到以下 保留字
        __peg_parser__  del         import     return
        and             elif        in         True
        as              else        is         try
        assert          except      lambda     while
        async           False       None       with
        await           finally     nonlocal   yield
        break           for         not
        class           from        or
        continue        global      pass
        def             if          raise
        # 从上面的 35 个保留字,可以看出,只有 3 个是特别的 True / False / None ,第一个字母是大写,又由于 Python 是区分大小写的,

3.4. python 内置函数最好不要作为变量名使用,否则内置函数会失效
3.5. 变量要先定义,后使用
3.6. 变量区分大小写
     判断一个变量是否存在,
     看变量是否分配了内存空间,可通过 id 函数来查看变量内存地址

      >>> s = 'hello world'  # 这是个定义的变量
      >>> s
      'hello world'
      >>> id(s)
      2799360608112          # 通过函数 id 来确认变量的内存地址来判断变量是否存在
;
3.6. 常用函数和方法

    len          字符串的长度,一个中文也是一个字符
    startswith   以指定字符串开头
    endswith     以指定字符串结尾
    find         第一次出现指定字符串的位置
    rfind        最后一个出现指定字符串的位置
    count        指定字符串出现了几次
    isalnum      所有字符全是字母或数字
    # 去除空格或者指定的字符串
    strip        首位都去除空格或者指定的字符串
    lstrip       左边去除空格或者指定的字符串
    rstrip       右边去除空格或者指定的字符串
    # 大小写转换
    capitalize   产生新的字符串,首字母大写
    title        产生新的字符串,每个单词都首字母大写
    upper        产生新的字符串,所有字母全转成大写
    lower        产生新的字符串,所有字母全转成小写
    swapcase     产生新的字符串,所有字母大小写转换
    # 格式排版
    center       中间对齐
    ljust        左对齐
    rjust        右对齐
    >>> a = 'sun'
    >>> a.center(10,'@')
    '@@@sun@@@@'
    >>> a.center(10) # 缺省值是空格
    '   sun    '
    >>> a.ljust(10,'*') # 左对齐
    'sun*******'
    >>> a.rjust(10,'*') # 右对齐
    '*******sun'
    # 其他方法
    isalnum     检测是否为字母或数字
    isalpha     检测是否只由字母组成(含汉字)
    isdigit     检测是否只由数字组成
    isspace     检测是否为空白符
    isupper     检测是否大写字母
    islower     检测是否小写字母

3.7 字符串的格式化

    format() 的基本用法
    基本语法是通过{} 和 :代替以前的 %
    format 函数可以接受不限个参数,位置可以不按顺序
        >>> a = '名字是:{0},年龄是:{1}'
        >>> a.format('中华','18')
        '名字是:中华,年龄是:18'
        >>> a = '名字是:{name},年龄是:{age}'
        >>> a.format(age=18,name='中华')
        '名字是:中华,年龄是:18'
    # 拓展1 填充与对齐
    填充常和对齐一起使用
    ^、<、> 分别是居中、左对齐、右对齐,后面带宽度
    : 号后面带填充的字符,只能是一个字符,不指定的话,默认用空格填充
        >>> '{:*>8}'.format('245') # * 是填充符, > 是指右对齐, 8 指的是输出的字符串的宽度
        '*****245'
    # 拓展2 数字格式化
    浮点数通过 f, 整数通过 d 进行需要的格式。
    >>> a = '我爱{0},爱了{1:.2f}秒'
    >>> a.format('中华',31415926.78326)
    '我爱中华,爱了31415926.78秒'

3.8 split() 分割 和 join()合并
    split() 可以基于指定分隔符将字符串分隔成多个子字符串(存储到列表中)。如果不指定分隔符,则默认使用空白(换行符、空格、制表符)。示例如下

    >>> a = 'to be or not to be'
    >>> a.split()
    ['to', 'be', 'or', 'not', 'to', 'be']
    >>> a.split('be') # 指定分隔符为 'be'
    ['to ', ' or not to ', '']
    >>> b = 'to     be   or     not   to   be     '
    >>> b.split()
    ['to', 'be', 'or', 'not', 'to', 'be'] # 空格符很多,依然都是去掉空格符

    # 测试 +拼接符 和 join() 的不同的效率
    import time
    time_sta = time.time() # 起始时间
    a = ''
    for i in range(10000000): # 循环 一千万次
        a += 'sxt'
    time_end = time.time() # 结束时间
    print('运算时间:'+str(time_end - time_sta))
    # 运算时间:22.25295090675354

    time_sta_02 = time.time()
    lis = []
    for i in range(10000000):
        lis.append('sxt')
    b = ''.join(lis)
    time_end_02 = time.time()
    print('运算时间:'+str(time_end_02 - time_sta_02))
    # 运算时间:0.7832164764404297
    # 结论:由此可以得出,同样是拼接,join 比 '+' 执行的效率快太多了

3.9 可变字符串
    在 python 中,字符串属于不可变对象,不支持原地修改,如果需要修改其中的值,只能创建新的字符串对象。
    但是我们经常确实需要原地修改字符串,可以使用 io.StringIO 对象或 array 模块。

    >>> import io
    >>> s = 'hello, zhonghua'
    >>> sio = io.StringIO(s)
    >>> sio
    <_io.StringIO object at 0x0000020F1ABA2C10>
    >>> sio.getvalue()
    'hello, zhonghua'
    >>> sio.seek(7) # 指针挪到索引 7 的位置
    7
    >>> sio.write('g') # 用 g 替换指针停留位置的字符串
    1
    >>> sio.getvalue()
    'hello, ghonghua' # 在这里可以看到字符串已经改变了,改变的过程中,并没有产生新的字符串
    >>> s
    'hello, zhonghua'
    >>> id(sio)
    2263896173584

第四节 - 整数
4.1 基本数据类型
    数字类型
        整数   int     :整数是没有小数的数
            整数类型支持 十进制 无前缀、二进制 0b/0B、八进制 0o/0O、十六进制 0x/0X
            整数没有大小限制,想存多大的数据,就存多大的数据
        浮点数 float   :浮点数是带有小数的数,浮点数有两种表示方式,十进制和科学计数法【注意:浮点数存在访问限制,计算结果会发生溢出】
        布尔型 bool    :True False # 首字母必须大写,因为在 python 是区分大小写的
            为 False 的情况:数字0       bool(0)                   # 返回 : False
                             空字符串    bool('') 或者 bool(“”)    # 返回 : False
                             空列表      bool([])                  # 返回 : False
                             空元组      bool(())                  # 返回 : False
                             空集合      bool({})                  # 返回 : False 这两个是一样的符号,{}
                             空字典      bool({})                  # 返回 : False 这两个是一样的符号,{}
        复数   complex :复数一般的表示形式 : a + bj ,其中a 表示实部,b表示虚部;复数对象可用属性 real 和 imag 查看复数的实部和虚部

            (1+2j).real  # 1.0
            (1+2j).imag  # 2.0

    字符串类型:字符串是由字符组成的序列,且用引号包含。
        python 中的引号包含单引号、双引号、三引号,单双引号作用相同,
        -- 函数 int :int('1') ,返回 数字 1,指将这个字符串 1 转换成 数字 1
        -- 函数 str :str(1) ,返回 字符串 1,指将这个数字 1 转换成 字符串 1
        字符串的连接:'a'+'b',字符串与字符串之间用加号(+) 进行连接,例如: 'a'+'b' -- 返回的是 'ab'
        字符串的复制:'a'*N  ,字符串后面跟 * N,表示重复字符串N次,
                     # 例如:'abc'*3 -- 返回的是'abcabcabc' 这里可以看到重复了abc这个字符串3次,并且是拼接在一起的
        字符串的转义符:转义字符  含义
                         \n       换行
                         \r       回车
                         \0       空值
                         \'       单引号
                         \"       双引号
                         \\       \
                         # 说明:如果在字符串中不需要转义字符生效,那么可以在字符串前面添加 R 或者 r 。
        字符串切片:python 中使用切片来截取字符串中的部分字符,字符串中可通过方括号运算符[]来获取相应索引位置的字符。
        python 中的索引方式有两种:一种是正序,从 0 开始;一种是逆序,从 -1 开始
            切片的表达方式:s[start:end:step]
                # 说明:s表示字符串,要截取的字符串;
                #       start 表示要截取的第一个字符所在的索引(截取时包含该字符)。如果不指定,默认为 0,也就是从字符串的开头截取,
                #       end   表示要截取的最后一个字符所在的索引(截取时不包含该字符)。如果不指定,默认为字符串的长度
                #       step  切片步长,指的是从 start 索引处的字符开始,每 step 个距离获取一个字符,直至 end 索引出的字符。step 默认值为 1,当省略该值时,最后一个冒号也可以省略
                #     【注意】一是start和end都可以为空,但是他们之间的冒号,不能少,
                #             二是字符串是不可变对象,所以不可以对字符串切片赋值,
        字符串常见函数和方法:
                             (1) len()函数     :获取字符串的长度
                             (2) ord()函数     :把字符转化成ASCLL表中的数字
                             (3) chr()函数     :把数字转化成ASCLL表中的字符
                             (4) title()方法   :把单词的第一个字母转化成大写
                             (5) upper()方法   :把整个字符串转化成大写
                             (6) lower 方法    :把整个字符串转化成小写
                                s = 'ABC'
                                print(s.lower())
                                # 输出
                                abc
                             (7) rstrip()方法  :把右边的空格去掉
                             (8) lstrip()方法  :把左边的空格去掉
                             (9) strip()方法   :去掉两边的空格
                          【注意】函数是可以直接写的,例如 len()
                                  方法是属于对象的,需要跟在对象后面,例如 :s.title() ,s是某个变量
        字符串驻留机制
            仅保留一份相同且不可变字符串的方法,不同的值被存放在字符串驻留池中。
            Python 支持字符串驻留机制,对于符合标识符规则的字符串(仅包含下划线(_)、字母和数字)会启用字符串驻留机制
                   # 变量由数字、字母、下划线组成,但是数字不能作为首字符,即 第一个字符必须是字母表中字母或下划线
            强调:下划线(_)、字母和数字
            例如下所示,两个'中华',在驻留池中,只存在一份; 'dd#' 不符合标识符驻留池规则,所以是两个对象
            >>> a = '中华'
            >>> b = '中华'
            >>> a is b
            True # 说明 a 和 b 是指向同一个对象,所以地址也是一样的
            >>> c = 'dd#'
            >>> d = 'dd#'
            >>> c is d
            False # 说明 c 和 d 不是指向同一个对象,所以地址也是不一样的,这里是两个对象,因为不符合驻留机制
            >>> c == d
            True # 说明 c 和 d 的地址不一样,但是值是一样的,
            '''
                is 比较的是对象的地址;双等号 == 比较的是对象的值
            '''

4.2 组合数据类型
    4.2.1 列表
        1) 创建列表
            python 中的列表类似其他编程语言中的数组 。
            python 中创建列表的方式有三种:
                   一是使用中括号[]进行创建;[1,2,3]

                        >>> [1,2,3]
                        [1, 2, 3]
                   二是使用函数 list() ;
                       >>> list('abcdef')
                       ['a', 'b', 'c', 'd', 'e', 'f']
                列表的值可以是任何类型
                列表的值是可以重复的
                    >>> a = 'hello boys and hello girls'
                    >>> b = a.split()
                    >>> b
                    ['hello', 'boys', 'and', 'hello', 'girls']# 这里面有两个'hello'
               列表中的元素下标或索引默认从 0 开始。反方向是从 -1 开始的
                   三是发现一种新建列表的方法,
                        >> string = 'hello world boys and girls'
                        >> s = string.split()
                        >> s
                        >> ['hello', 'world', 'boys', 'and', 'girls'] # 输出值是 以字符串中的空格隔开的列表
                   四是通过 range 函数创建整数列表
                        语法格式如下:range([start,]end[,step])
                        start 的缺省值是 0
                        end   不可为空
                        step  的缺省值是1
                        >>> a = list(range(1,10))
                        >>> a
                        [1, 2, 3, 4, 5, 6, 7, 8, 9]
                        >>> b = list(range(15,-3,-2))
                        >>> b
                        [15, 13, 11, 9, 7, 5, 3, 1, -1]
                   五是通过推导式创建列表
                        >>> a = [x*2 for x in range(10)]
                        >>> a
                        [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
                        >>> b = [x*2 for x in range(100) if x%9 == 0]
                        >>> b
                        [0, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 198]
               列表的函数,除了list,还有
                    len(list) 函数,返回列表元素个数
                    max(list) 函数,返回列表元素最大值
                    min(list) 函数,返回列表元素最小值
        2) 给列表添加元素
            append()  # 这个关键字是 方法,使用时必须接上 对象;没有创建新的对象
                >>> s = [32,23,76,3,7,1.9]
                >>> s.append(100)
                >>> s
                [32, 23, 76, 3, 7, 1.9, 100]
            insert()  # 这个关键字是 方法,使用时必须接上 对象;将指定的元素,插入到列表的指定位置
                >>> s=[32, 23, 76, 7, 1.9, 100]
                >>> s.insert(3,7)# 在索引 3 的位置,插入 数字 7
                >>> s
                [32, 23, 76, 7, 7, 1.9, 100]
        3) 从列表中获取元素
            变量名[下标]
            >>> a = ['hello', 'nowcoder', 'boys', 'and', 'girls'] # 先定义 a 变量
            >>> b = a[-1]                                         # 索引 -1 位置的字符串赋值给 变量 b
            >>> b
            'girls'
        4) 从列表中删除元素
            remove(元素) 删除首次出现的指定元素,若不存在该元素,则报异常;通过元素或值进行删除,括号里面必须是列表中的元素
                         # 这个关键字是 方法,使用时必须接上 对象
                >>> b = ['hello', 'boys', 'and', 'hello', 'girls']
                >>> b.remove('hello')
                >>> b
                ['boys', 'and', 'hello', 'girls']
            del 删除指定的列表或者指定的索引的元素             # 这个关键字是 函数
                >>> a = ['hello', 'boys', 'and', 'hello', 'girls']
                >>> del a[2] # 删除指定的索引的元素
                >>> a
                ['hello', 'boys', 'hello', 'girls']
                >>> del(a) # 删除指定的列表
                >>> a
                Traceback (most recent call last):
                  File "<pyshell#19>", line 1, in <module>
                    a
                NameError: name 'a' is not defined

            pop(index) 把列表中最后一元素给输出,或者说删除 # 这个关键字是 方法,使用时必须接上 对象 ,index表示索引或者下标,缺省值是 -1
                >>> b
                ['boys', 'and', 'hello', 'girls']
                >>> b.pop()
                'girls'
                >>> b
                ['boys', 'and', 'hello']
                >>> b.pop(0)
                'boys'
                >>>
        5) 列表常见操作
            (1) count()    # 方法;统计某一元素在列表出现的次数
                    >>> s                #
                    [3, 7, 23, 32, 76]   #
                    >>> s.count(3)       # 执行 count ,计算 3 在列表 s 中出现几次
                    1                    # 3 出现一次
            (2) extend     # 方法;把一个列表添加到另外一个列表后面,等同于 ;列表 + 列表;没有创建新的对象
                    >>> a = [1,2]    # 赋值 a 列表
                    >>> b = [3,4]    # 赋值 b 列表
                    >>> a.extend(b)  # 运算 extend , 这时候拼接起来的新列表的名称,还是 a
                    >>> a            # 导出
                    [1, 2, 3, 4]     #
            (3) index()    # 方法;获取某一元素的下标,如果该元素出现多次,则从左往右数起的第一次出现的为准
            (4) reverse()  # 方法;把列表内的元素倒序排序
            (5) sort()     # 方法;把列表中的元素按照从小到大的顺序排列,修改源列表,不生成新的列表
                    >>> s = [32,23,76,3,7,1.9]   # 赋值列表 s
                    >>> s                        #
                    [32, 23, 76, 3, 7, 1.9]      #
                    >>> s.sort()                 # 从小到大重新排序
                    >>> s                        # 查看列表 s
                    [1.9, 3, 7, 23, 32, 76]      # 输出
                    # 也可以是降序
                    >>> s.sort(reverse = True)
                    >>> s
                    [76, 32, 23, 7, 3, 1.9]
                    # 还有一种随机排序 不过要用 随机模块 random
                    >>> import random # 随机模块 random
                    >>> random.shuffle(s) # shuffle 洗牌
                    >>> s
                    [32, 1.9, 76, 23, 7, 3]
                sorted 函数,排序,生成新的列表,不对原列表做修改,默认是升序排列
                    >>> s
                    [32, 1.9, 76, 23, 7, 3]
                    >>> s = sorted(s)
                    >>> s
                    [1.9, 3, 7, 23, 32, 76]
                    >>> b = sorted(s,reverse=True)# 用方法 reverse 倒序,
                    >>> b
                    [76, 32, 23, 7, 3, 1.9]
                    ;
            (6) clear()    # 方法;清空列表
                    >>> s                   #
                    [1.9, 3, 7, 23, 32, 76] #
                    >>> s.clear()           # 执行 清空列表
                    >>> s                   #
                    []                      # 列表为空
            (7)copy # 复制列表,
        6) 列表切片
            lists[start:end:step]
                 # start  开始位置(索引)
                 # end    结束位置(索引)
                 # step   步长 默认值为 1
                 # 【注意】 输出的值不包含 end 位置上的索引指向的值
        7) 嵌套列表,也就是多维列表
            >>> a = ['a', 'b', 'c']
            >>> n = [1, 2, 3]
            >>> x = [a, n]
            >>> x
            [['a', 'b', 'c'], [1, 2, 3]]# 二维列表
            >>> x[0]
            ['a', 'b', 'c']
            >>> x[0][1]
            'b'
        8) sum 函数,对数值型列表的所有元素进行求和操作,对非数值型列表运算则会报错。
            >>> b
            [200, 300, 400]
            >>> sum(b)
            900

    4.2.2 元组
            元组 0 个或者多个元素用半角逗号 ',' 隔开 并且 外框是括号 '()' 的组合
          总结:
            1. 元组的核心特点是:不可变序列的,而列表是可变的
            2. 元组的访问和处理速度比列表快
            3. 与整数和字符串一样,元组可以作为字典的键,列表则永远不能作为字典的键使用
            4. 元组内的元素是可以重复的
                >>> x = (1,1,1,1.2)
                >>> x
                (1, 1, 1, 1.2)
            # 外框是括号'()',组内元素用半角逗号','隔开 ,组内可以没有元素
        1) 创建元组
            1.1 创建元组非常简单,在命令行中用逗号分隔一些数据,就会自动创建元组。
            1.2 另外,元组也可以通过小括号创建和用函数 tuple 创建。
                >>> 1,2,3                        # 在命令行中用逗号分隔一些数据,就会自动创建元组
                (1, 2, 3)                        #
                >>> (4,5,6)                      # 通过小括号创建
                (4, 5, 6)                        #
                >>> ()                           #
                ()                               #
                >>> tuple('123456')              # 用函数 tuple 创建
                ('1', '2', '3', '4', '5', '6')   #
                >>> (1,)                         # 通过小括号创建
                (1,)                             #
                >>> (1)                          # 请注意 这里没有用逗号分隔开,输出的是一个整数 1 ,因而不是元组
                1                                #
            1.3 生成器推导式创建元组
                从形式上看,生成器推导式与列表推导式类似,只是生成器推导式使用小括号。
                列表推导式直接生成列表对象,生成器推导式生成的不是列表也不是元组,二是一个生成器对象。
                    我们可以通过生成器对象,转化成列表或者元组,也可以使用生成器对象的 __next__()方法进行遍历,
                或者直接作为迭代器对象来使用。
                不管什么方式使用,元素访问结束后,如果需要重新访问其中的元素,必须重新创建该生成器对象。
                >>> s = (x*2 for x in range(5))
                >>> type(s)
                <class 'generator'># 此时的 s 只是一个生成器,类似一次性使用杯子,只能用一次
                >>> s
                <generator object <genexpr> at 0x000001CE882AA0B0>
                >>> tuple(s)
                (0, 2, 4, 6, 8)
                >>> list(s)
                [] # 第二次调用 s 的时候是一个空的,需要再重新生成一次
                >>> s = (x*2 for x in range(5))
                >>> s.__next__()
                0
                >>> s.__next__()
                2
                >>> s.__next__()
                4
                >>> s.__next__()
                6
                >>> s.__next__()
                8
                >>> s.__next__()
                Traceback (most recent call last):
                  File "<pyshell#28>", line 1, in <module>
                    s.__next__()
                StopIteration

        2) 访问元组
           访问元组通过下标或索引方式访问,类似列表元组也有切片,# 元组的下标也是从 0 开始的
            lists[start:end:step]
                 # start  开始位置(索引)
                 # end    结束位置(索引)
                 # step   步长 默认值为 1
                 # 【注意】 输出的值不包含 end 位置上的索引指向的值
                >>> s = ('a','b','c','d','e','f','g')    # 赋值元组给 s
                >>> s                                    #
                ('a', 'b', 'c', 'd', 'e', 'f', 'g')      #
                >>> s[0]                                 # 切片下标为 0 的位置
                'a'                                      #
                >>> s[0:2]                               # 切片下标为 0 到2(不包含2) 的位置
                ('a', 'b')                               #
        3) 更新元组
            元组不允许被修改,但可以对元组重新赋值和进行拼接 。
        4) 删除元组
            元组元素不允许被删除,但是可以删除整个元组 。
        5) 元组的内置函数
            (1) len()    # 求元组的元素的个数
            (2) max()    # 求元组的最大值
            (3) min()    # 求元组的最小值
            (4) tuple()  # 将列表转换为元组。
                         # 您不能向 tuple 增加元素。Tuple 没有 append 或 extend 方法。
                         # 您不能从 tuple 删除元素。Tuple 没有 remove 或 pop 方法。
                         # 然而, 您可以使用 in 来查看一个元素是否存在于 tuple 中。
                         # Tuple 比 list 操作速度快。如果您定义了一个值的常量集,并且唯一要用它做的是不断地遍历它,请使用 tuple 代替 list。
                         # 如果对不需要修改的数据进行 “写保护”,可以使代码更安全。使用 tuple 而不是 list 如同拥有一个隐含的 assert 语句,说明这一数据是常量。
                         # 如果必须要改变这些值,则需要执行 tuple 到 list 的转换。
        6) zip
           函数 zip(列表1,列表2,...) 将多个列表对应位置的元素组合成为元组,并返回这个zip对象。
            >>> a = [10,20,30]
            >>> b = [40,50,60]
            >>> c = [70,80,90]
            >>> d = zip(a,b,c)
            >>> type(d)
            <class 'zip'>
            >>> list(d)
            [(10, 40, 70), (20, 50, 80), (30, 60, 90)] # 对应位置的元素组合成为元组

    4.2.3 字典
        字典的特点:无序可变序列、键值对 且 键不重复
        1) 创建字典
            有四种方式创建字典,
              一是使用花括号;
                >>> d = {'a':1,'b':2}                  # 一是使用花括号
                >>> d                                  #
                {'a':1,'b':2}                          #
              二是使用 dict 函数;
                >>> dic = dict(((1,10),(2,20),(3,30))) # 二是使用 dict 函数
                >>> dic                                #
                {1:10,2:20,3:30}                       #
                >>> dic = {1:10,1:20,1:30,4:90}        # 在这里可以看到 1 这个键值出现了两次,因为键是不重复的,那么输出的字典就会值保留一个键值
                >>> dic
                {1:30,4:90}                            # 重复出现的键值,会以最后一个键值作为保存
              三是使用 zip() 创建字典对象;
                >>> a = ('name','age')
                >>> b = ('Sunny',18)
                >>> c = dict(zip(a,b))
                >>> c
                {'name': 'Sunny', 'age': 18}
              四是通过 fromkeys 创建值为空的字典
                >>> a = dict.fromkeys(['name','age','job'])
                >>> a
                {'name': None, 'age': None, 'job': None} # 创建值为空的字典,None 是一个值,表示的就是 空,意思是键还没有被赋值

        2) 访问字典
          1. 使用键 key 来访问,变量名 [key]
            >>> dic = {1:10,2:20,3:30,4:40}      # 创建字典
            >>> dic                              # 显示字典
            {1: 10, 2: 20, 3: 30, 4: 40}         #
            >>> dic[1]                           # 访问字典中键为 1 的键值
            10                                   #
            >>> dic[2]                           # 访问字典中键为 2 的键值
            20                                   #
            >>> dic[3]                           # 访问字典中键为 3 的键值
            30                                   #
            >>> dic[4]                           # 访问字典中键为 4 的键值
            40                                   #
            >>> dic[5]                           # 访问字典中键为 5 的键值
            Traceback (most recent call last):   # 没有键为 5 ,报错
              File "<pyshell#47>", line 1, in <module>
                dic[5]
            KeyError: 5
          2. 通过 get() 方法获得"值"。推荐使用。优点是:指定键不存在,返回 None;
             也可以设定指定键不存在是默认返回的对象。推荐使用 get() 获取"值对象"。
            >>> a
            {'name': 'Sunny', 'age': 18}
            >>> a.get('name')
            'Sunny'
            >>> print(a.get('ddd')) # 如果指定的键值不存在,则返回 None
            None
            >>> a.get('sss','不存在') # 如果指定的键值不存在,则返回指定的值
            '不存在'
          3. items 方法,列出所有的键值对
            >>> a.items()
            dict_items([('name', 'Sunny'), ('age', 18)])

          4. 列出所有的键,列出所有的值
            >>> a.keys()                # 列出所有的键
            dict_keys(['name', 'age'])
            >>> a.values()              # 列出所有的值
            dict_values(['Sunny', 18])

          5. len() 键值对的个数
            >>> len(a)
            2

          6. 检测一个'键'是否在字典中
            >>> a
            {'name': 'Sunny', 'age': 18}
            >>> 'name' in a
            True


        3) 更新字典
          3.1使用键 key 来更新
            变量名 [key] = value,给字典新增键值对,如果'键'已经存在,则覆盖旧的键值对;如果不存在,则新增'键值对'
            >>> dic = {1: 10, 2: 20, 3: 30, 4: 40}  # 创建字典
            >>> dic                                 # 展示字典
            {1: 10, 2: 20, 3: 30, 4: 40}            #
            >>> dic[1]                              # 访问键为 1 的键值
            10                                      #
            >>> dic[1] = 99                         # 将键为 1 的键值重新赋值为 99
            >>> dic                                 #
            {1: 99, 2: 20, 3: 30, 4: 40}            #
            >>> dic[5] = 888                        # 注意,源字典是没有键 5 的,现在添加了键 5 和赋值 888,就能够生成新的键对
            >>> dic                                 #
            {1: 99, 2: 20, 3: 30, 4: 40, 5: 888}    # 新的键对{5:888} 添加在了字典 dic 中

          3.2 使用 update() 将新字典中所有的键值对全部添加到旧字典对象上。如果 key 有重复,则直接覆盖。
                >>> a = {'name': '中华', 'age': 18, 'job': 'prpgrammer'}
                >>> b = {'name': 'zhangsan', 'money': 1000, 'sex': '男', '星座': '天蝎座'}
                >>> a.update(b) # update 是方法 将后者更新到前者去。
                >>> a
                {'name': 'zhangsan', 'age': 18, 'job': 'prpgrammer', 'money': 1000, 'sex': '男', '星座': '天蝎座'}
                >>> b
                {'name': 'zhangsan', 'money': 1000, 'sex': '男', '星座': '天蝎座'}

        4) 字典中元素的删除,可以使用 del() 方法,或者 clear() 删除所有键值对,或者 pop() 删除指定的键值对,并返回对应的'值对象'
              4).1
                >>> a = {'name': 'zhangsan', 'money': 1000, 'sex': '男', '星座': '天蝎座'}
                >>> del a['name']
                >>> a
                {'money': 1000, 'sex': '男', '星座': '天蝎座'}
                >>> del(a['money'])
                >>> a
                {'sex': '男', '星座': '天蝎座'}
                >>> b = a.pop('sex') #  方法 pop() 删除指定的键值对,并返回对应的'值对象'
                >>> b
                '男'
                >>> a
                {'星座': '天蝎座'}
                >>> a.clear() # 清空字典
                >>> a
                {}
                >>> del dic                   # 删除整个字典
                >>> dic                       # 已无字典 dic ,无法显示,所以报错
                Traceback (most recent call last):
                  File "<pyshell#52>", line 1, in <module>
                    dic
                NameError: name 'dic' is not defined

              4).2 popitem() : 方法,随机删除并返回该键值对。字典是'无序可变序列',因此没有第一个元素、最后一个元素的概念;
                  popitem 弹出随机的项,因为字段并没有"最后的元素"或者其他有关顺序的概念。若想一个借一个的移除并处理项,这个方法就非常有效。
                  (因为不用首先获取键的列表)
                    >>> a = {'money': 1000, 'sex': '男', '星座': '天蝎座'}
                    >>> a.popitem()
                    ('星座', '天蝎座')
                    >>> a
                    {'money': 1000, 'sex': '男'}
                    >>> a.popitem()
                    ('sex', '男')
                    >>> a
                    {'money': 1000}

              4).3 序列解包
                序列解包可以用于元组、列表、字典。序列解包可以让我们方便的对多个变量赋值。
                序列解包用于字典是,默认是对'键'进行操作;如果需要对键值对操作,则需要使用 items();
                    如果需要对'值'进行操作,则需要使用'values()'
                    >>> x,y,z = (10,20,30)
                    >>> x
                    10
                    >>> y
                    20
                    >>> z
                    30
                    >>> [a,b,c] = [20,30,40]
                    >>> a
                    20
                    >>> b
                    30
                    >>> c
                    40
                    >>> a = {'money': 1000, 'sex': '男', '星座': '天蝎座'}
                    >>> x,y,z = a
                    >>> x
                    'money'
                    >>> y
                    'sex'
                    >>> z
                    '星座'
                    >>> x,y,z = a.values()
                    >>> x
                    1000
                    >>> y
                    '男'
                    >>> z
                    '天蝎座'
                    >>> x,y,z = a.items()
                    >>> x
                    ('money', 1000)
                    >>> y
                    ('sex', '男')
                    >>> z
                    ('星座', '天蝎座')

    4.2.4 集合
        集合里面的元素是无序的,且元素不能重复 。 # 集合中的元素,可以无序排列,且每一个元素都是唯一的
        实际上,集合底层是字典实现,集合的所有元素都是字典中的"键对象"。因此是不能重复的且唯一的。
        集合的外框是 花括号 '{}' ,元素之间用 半角逗号 ',' 隔开
        1)集合的创建
            创建集合的方式有两种:一种是使用花括号;二是使用函数 set()
                >>> sets = {1,2,3}           # 使用花括号 创建集合
                >>> sets                     #
                {1, 2, 3}                    #
                >>> st = set([1,2,3,4,5])    # 使用函数 set() 创建集合
                >>> st                       #
                {1, 2, 3, 4, 5}              #
            集合的添加,使用 add() 方法
                >>> s = {1,2,3,4}
                >>> s.add('中华')
                >>> s
                {1, 2, 3, 4, '中华'}
                >>> s.add(1) # 元素不能重复,1 是已经存在的元素了,所以不能再添加,但是不会报错,
                >>> s
                {1, 2, 3, 4, '中华'}
            集合的删除,remove() 删除指定的元素;clear() 清空整个集合
                >>> s
                {1, 2, 3, 4, '中华'}
                >>> s.remove(1)
                >>> s
                {2, 3, 4, '中华'}
                >>> s.clear()
                >>> s
                set()
                >>> type(s)
                <class 'set'>

        2)集合的访问
            由于集合的无序性,不能使用索引进行访问,但是可使用迭代把集合中的数据读取出来
        3)集合类型的操作符
            操作符    描述
             S - T    差集,返回在 S 中但不在 T 中的元素
             S & T    并集,返回同时在 S 和 T 中的元素       # 类似 与
             S ^ T    返回 S 和 T 的非共同元素               # 类似 异或
             S | T    并集,返回 S 和 T 中的所有元素         # 类似 非
                # 上述中,S 和 T 代表两个集合 。
                >>> S = {1,2,3,4}      # 定义集合 S
                >>> T = {2,3,4,5,6}    # 定义集合 T
                >>> S - T              # 用集合 S 减去 T ,相同的部分去掉,剩下 S 中不同于 T 中的元素
                {1}                    #
                >>> S & T              # 集合 S 和集合 T 中相同的元素
                {2, 3, 4}              #
                >>> S ^ T              # 返回 S 和 T 的非共同元素
                {1, 5, 6}              #
                >>> S|T                #
                {1, 2, 3, 4, 5, 6}     # 返回 S 和 T 中的所有元素,因为集合的特性:唯一性,所以重复的元素会自动删除 。
                >>> a = {1,3,'sxt'}
                >>> b = {'he','it','sxt'}
                >>> a^b
                {1, 3, 'he', 'it'}

第十一节 - 运算符
在python中包含七种运算符,
分别是 算法运算符、比较运算符、赋值运算符、逻辑运算符、
       位运算符、成员运算符、身份运算符
其中,前四种是必须掌握的,

    11.1 算术运算符
        相加       :x+y
        相减       :x-y
        相乘       :x*y
        相除       :x/y      # 在python 中,除数不为 0 ;商值,是一个浮点数,例如 4/2,得到的值是 2.0
        相余       :x%y      # 取余数,余数是一个整数,也可以是浮点数 例如:1.5%2,返回的是 1.5,这是个浮点数
        幂运算     :x**y
        整除       :x//y     # 取商,只会提取小数点左边的整数,返回的数据类型是 int
        divmod()   :同时得到商和余数。例如 divmod(10,3),返回的是一个元组:(3,1),其中 3 是商,1 是余数

    11.2 比较运算符,又叫 关系运算符
        判断两个数是否相等       :x==y    #
        判断两个数是否不相等     :x!=y    #
        x是否大于y               :x>y     #
        x是否小于y               :x<y     #
        x是否大于等于y           :x>=y    # 要么大于,要么等于,只要满足一个条件就为真
        x是否小于等于y           :x<=y    # 要么小于,要么等于,只要满足一个条件就为真
    【注意】比较运算符的结果是 bool 类型,要么 True ,要么 False 。

    11.3 赋值运算符
        赋值      :=       # 这是个赋值符号
        加法赋值  :+=      # 先做加法,再赋值,例如:a = 3,那么 a += 1 即表示 a = a + 1,也就是 a = 4
        减法赋值  :-=      # 先做减法,再赋值,例如:a = 3,那么 a -= 1 即表示 a = a - 1,也就是 a = 2
        乘法赋值  :*=      # 先做乘法,再赋值,例如:a = 3,那么 a *= 2 即表示 a = a * 2,也就是 a = 6
        除法赋值  :/=      # 先做除法,再赋值,例如:a = 6,那么 a /= 3 即表示 a = a / 3,也就是 a = 2.0 ,这里要切记,python 中除法的商是浮点数
        取模赋值  :%=      # 先出除法,再赋值,例如:a = 6,那么 a %= 2 即表示 a = a / 2 = 6/2 = 3,余数为 0 ,然后将余数赋值给 a,也就是 a = 0
        幂赋值    :**=     # 先做幂运算,再赋值,例如:a = 2,那么 a **= 3,即表示 a = a * a * a = 2*2*2 = 8
        取整赋值  ://=     # 先做除法,然后将商变为 整数,再赋值回去,例如:a = 16,那么 a //= 3 表示 16/3=5余1,然后将5赋值回去
    ;
    11.4 逻辑运算符
        与 : and
        或 :  or
        非 : not
    说明:a and b ,如果 a 为假,则返回 a 的值,否则返回 b 的值;
          a  or b ,如果 a 为真,则返回 a 的值,否则返回 b 的值
          not , 真假转化;例如:赋值 a = 2,执行 not a ,输出 False # 因为 a  2,所以 a 为真,加上 not 之后,变成假的,就得到 False 。

    ;
    11.5 位运算符

        位与运算符      &  # 当两者都为 1 输出 1,其余为 0  ,参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0;
        位或运算符      |  # 当两者都为 0 输出 0,其余为 1  ,只要对应的二个二进位有一个为1时,结果位就为1;
        位异或运算符    ^  # 当两者都不同,输出 1 ,其余为 0 ,当两对应的二进位相异时,结果为1;
        位取反运算符    ~  # 对数据的每个二进制位取反,即把1变为0,把0变为1;
        左移运算符      << # 运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
        右移运算符      >> # 把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数
            【注意】位运算符针对二进制来运算的,其中按位 取反运算符 为单目运算符
            提示:
            十进制   二进制
        0        0
        1        1
        2        10
        3        11
        4        100
        5        101
        6        110
        7        111

        例如 : &
        1&1 = 1
        1&0 = 0
        0&0 = 0

        例如 : |
        1|1 = 1
        1|0 = 1
        0|0 = 0

        例如 : ^
        1^1 = 0
        1^0 = 1
        0^0 = 0

        例如 : ~
        ~5 = -6  解释:将二进制数+1之后乘以-1,即~x = -(x+1),-(101 + 1) = -110

        例如 : << # 左移运算符
        >> a = 60
        >> a << 2 # 输出结果 240 ,二进制解释:1111 0000;还有一种解释就是 60 乘以 2,再乘以2,a << n,即表示 a乘以2的n次幂

        例如 : >> # 右移运算符
        >> a = 60
        >> a >> 2 # 输出结果 15 ,二进制解释:0000 1111;还有一种解释就是 a >> n,即表示 a除以2的n次幂

11.6 成员运算符
    判断某个值是否存在某序列中
        指定的值  在序列中返回 True  : in
        指定的值不在序列中返回 True  : not in
    例如:

    >>> s = 'helloworld' # 赋值
    >>> 'a' in s         # 运算
    False                # 输出
    >>> 'h' in s         # 运算
    True                 # 输出
    >>> 'a' not in s     # 运算
    True                 # 输出
    >>> 'h' not in s     # 运算
    False                # 输出
    >>> 'hello' in s     # 多个字符运算
    True                 # 输出
    >>> lis = [1,2,3]    # 赋值 lis 整数1,2,3
    >>> 1 in lis         # 运算整数 1 是否在 lis 中
    True                 # 输出
    >>> '1' in lis       # 运算字符串'1' 是否在 lis 中
    False                # 输出
11.7 身份运算符
    用于比较两个对象的存储单元
        引用同一个对象返回 True     : is
        引用不是同一个对象返回 True : is not
    例如:

    >>> a = 1        # 赋值
    >>> b = 2        # 赋值
    >>> a is b       # 运算
    False            # 输出
    >>> a is not b   # 运算
    True             # 输出
【说明】
如果存在 a 和 b 两个变量
a is     b 类似 id(a) == id(b)
a is not b 类似 id(a) != id(b)
'''
    python 仅仅对比较小的整数对象进行缓存,(范围是[-5,256])缓存起来,而并非是所有的整数对象,
需要注意的是,这仅仅是在命令中执行,而在 Pycharm 或者保存为文件执行,结果是不一样的,这是因为解释器做了一部分优化(范围是[-5,+∞])
'''

第二十四节 基本输入输出函数
    24.1 输出函数 print
        print 函数一般格式是:print(value1[,value2,...,valueN,sep=' ',end='\n'])
        说明:

            (1)value1,value2,valueN 是要输出的值
            (2)sep 是值与值之间的分隔符,默认为空格
            (3)end 表示以什么结束,默认是以换行符结束
            格式化输出:format()
            例如,我要输出 'a is 1 b is 2' ,那么可以用下面两种方法
            >>> a = 1
            >>> b = 2
            >>> print('a is',a,'b is',b)
            a is 1 b is 2
            >>> print('a is',a,'and ','b is',b)         # 分割方式,这种方式只针对较少的输出值
            a is 1 and  b is 2
            >>> print('a is {} b is {}'.format(a,b))    # 运用格式化函数 format
            a is 1 b is 2
            >>> print('{0}{1}'.format(a,b)) # 指定索引式的输出,花括号里面的是索引值,会引导 format 里面的值一一对应的输入前面的花括号,索引值可重复
            >>> 12
            >>> # 另外,如下输出函数中,冒号 ':' 表示取输出值的整数部分,半角句号'.'表示保留小数,后面跟一个数字,表示保留小数的位数,
            >>> #'f' 表示浮点数 float ,请注意,这里保留的小数,会做 四舍五入
            >>> print('{}'.format(2.123456))
            2.123456
            >>> print('{:.4f}'.format(2.123456)) # 四舍五入,保留四位小数
            2.1235
            >>> print('{:.3f}'.format(2.123456)) # 四舍五入,保留三位小数
            2.123
            >>> print('{:.4}'.format(2.123456))  # 四舍五入,没有 f 跟在后面,表示保留的数值,一共就四个数字
            2.123
            >>> print('{:.5}'.format(2.123456))  # 四舍五入,没有 f 跟在后面,表示保留的数值,一共就五个数字
            2.1235
            ;
    24.2 输入函数 input
        input 函数会让程序暂停运行,并提示用户输入数据后回车。
        【注意】不管用户输入的是什么信息,input 函数一律作为字符串看待 。
    24.3 eval 函数
        eval 函数的作用是去掉字符串最外侧的引号,并按照 python 语句方式执行引号内的内容 。
            功能:将字符串 str 当成有效的表达式来求值并返回计算结果。
            以下是 eval() 方法的语法:
                            eval(expression[, globals[, locals]])
            expression -- 表达式。
            globals    -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
            locals     -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
        例如:
        >>> print('3+5')        # 输入的永远都是字符串
        3+5
        >>> print(eval('3+5'))  # eval 会把最外侧的引号去掉,执行里面的语句
        8

        再举例:
        d = dict(a=100,b=200)
        print(eval('a+b',d))
        # 执行结果
        300

    24.4 python 的流程控制语句
        有四种流程控制语句:顺序结构,条件结构,循环结构,跳转结构
         24.4.1 顺序结构:从上往下,逐条编译
         24.4.2 条件结构:先判断条件,如果为真,则执行条件下的语句,否则执行后面的判断语句,从上往下,逐条编译
            语法结构有:单分支结构、二分支结构、多分支结构 。
            语法结构如下: # (以下介绍的是多分支结构)
                if condition1:      # 条件后面必须紧跟一个冒号
                    statement1       # 执行语句与自己的判断条件必须相差4个单元格
                elif condition2:     # 条件后面必须紧跟一个冒号
                    statement2       # 执行语句与自己的判断条件必须相差4个单元格
                else:                # 条件后面必须紧跟一个冒号
                    statement3       # 执行语句与自己的判断条件必须相差4个单元格
            例如:如果天气好,就去爬山;如果是阴天,则在家学习;如果不好,则看电视 。
            cliamte = input('请输入天气:')
            if cliamte == 'good':
                print('爬山')
            elif cliamte == '阴天':
                print('学习')
            else:
                print('看电视')
        24.4.3 循环结构
            while 循环的表达式:
                while 条件:
                    语句块
                ''' 说明:(1)先判断条件是否成立,如果条件成立,就执行语句块;否则退出 while 循环
                          (2)while 循环必须要有循环结束的条件,否则就会进入死循环
                          【提示】按住 ctrl + F2 ,即可退出死循环,其实是终止执行语句。
                '''
            例如:输出从数字1到数字6的所有数字。
            i = 1            # 初始赋值
            while i <= 6:    # 判断条件
                print(i)     # 输出结果
                i += 1       # 自增赋值

            for 循环
            for <循环变量> in <遍历结构>:
                语句块
            例如 :输出1到100 的数字
            for i in range(1,101): # range 是 python 提供的一个数字串函数,表示从1到100(我写的是101 ,但是实际上是不包含101这个数字的)
                                   # 如果 range(100) 的话,你可以看作前面的缺省值是 0 ,即 range(0,100)
                print(i)
            例如2:# 输出1到100 中为偶数的数字
            for i in range(1,101):
                if i %2 == 0:
                    print(i,end='-')
            例如3:
            '''嵌套循环'''
            lists = [[1,2,3],[4,5,6]] # 列表中有列表
            for i in lists:
                for j in i:
                    print(j)
            例如4:
            lists = [1,2,3,[4,5,6]]
            for i in lists:
                # 判断对象是否为可迭代对象
                if isinstance(i,Iterable):# isinstance
                    for j in i :
                        print(j)
                else:
                    print(i)
            例如5:新的函数 break 表示跳出循环
            i = 1
            for i in range(1,11)
                print(i)
                i += 1
                break
            else:
                print('for end')
            # 这段循环,输出的结果仅为 1 ,从上往下,执行1次,就执行了 break ,就跳出循环,连 else 也不执行了,直接结束 。
    32. 跳转语句
        32.1 break
                break 语句用来终止循环语句,即使循环没有结束,也会强制停止循环;如果由两层或者多层循环,break 退出最内层循环。
                示例:
                (1)遍历字符串'python',当遇到字符'h'时,退出循环,
                s = 'python'
                for i in s:
                    if i == 'h':
                        break
                    print(i,end='')
                # 输出的结果为:pyt
                (2)当输入'quit'退出时,中断用户的输入,
                while True:
                    name = input('请输入数据:')
                    if name == 'quit':
                        break
                    print(name)
                # 当输入的值不是'quit'的时候,输入什么输出什么;当输入的是'quit'的时候,直接结束循环,停止执行语句
                (3)退出内循环
                for i in range(1,5):
                    print(i)
                    for j in range(1,5):
                        if j == 3:
                            break    # 当 j 遍历到 3 的时候,停止 j 循环,继续执行 i 循环 。
                        print(j)
        32.2 continue
                与 break 不同,continue 语句用于中断本次循环的执行,继续执行下一轮的循环 。
                示例:
                (1):输出100 以内的奇数
                for i in range(1,101):
                    if i % 2 == 0:
                        continue     # 如果 i 的模等于 0 ,则中断本次循环,不做处理,继续执行下一轮的循环 。
                    print(i)
                或者
                for i in range(1,101):
                    if i % 2 != 0:
                       # continue     # 如果 i 的模不等于 0 ,则中断本次循环,不做处理,继续执行下一轮的循环
                        print(i)
                打印九九乘法表
                for i in range(1,10):
                    for j in range(1,i+1):
                        print('{}x{}={}'.format(i,j,i*j),end = ' ')
                    print()

                # 打印九九乘法表,方法一
                for m in range(1,10):
                    for n in range(1,m+1):
                        print("{0}*{1}={2}".format(m,n,m*n),end = '\t')
                    print()
                #
                # print("*************************************************")
                #
                # # 打印九九乘法表,方法二
                for m in range(1,10):
                    for n in range(1,m+1):
                        print("{0}*{1}={2}".format(n,m,m*n),end = '\t')
                    print()
        32.3 循环代码优化
             虽然计算机越来越快,空间也越来越大,我们仍要在性能问题上“斤斤计较”。
             编写循环时,遵守下面三个原则可以大大提高运行效率,避免不必要的低效计算:
             1. 尽量减少循环内部不必要的计算;
             2. 嵌套循环中,尽量减少内层循环的计算,尽可能向外提;
             3.局部变量查询较快,尽量使用局部变量。

             其他优化手段:
             1. 链接多个字符串,使用 join() 而不使用+
             2. 列表进行元素插入和删除,尽量在列表尾部操作

        32.4 三元表达式
            例子:输入两个值,输出他们之间的较大值


    35. 函数定义和调用
        函数是将具有独立功能的代码块组织成一个整体,并具有特定的功能。
        作用能够加强代码的复用性,提高程序开发的效率。
        python 中的函数可分为内置函数和自定义函数,其中内置函数为 58 个
            自定义函数的格式如下:
                def func_name(argument): # def 即 define 申明;func_name 表示函数的名称,要遵循变量命名规则;
                # argument 参数,可选;【切记】后面跟着一个冒号,
                    func_body            # 函数体,可完成特定的功能
                    return value         # value 表示函数的返回值,可选
                ''' 说明:(1)函数定义后不能直接执行,必须调用才能运行,python 中的函数遵循先定义,后调用
                             函数调用格式:函数名称(arg)  # arg 即 argument 参数
                          (2)定义函数需遵循功能单一原则
                '''
                例如6:九九乘法表 函数
                def multipy(arg):
                    for i in range(1,arg):
                        for j in range(1,i + 1):
                            print('{}X{}={}'.format(i,j,i*j),end = ' ')
                        print()
                    # 调用函数
                    multipy(10)
    36. 函数参数
        36.1 形参和实参
            形参是定义函数时给的参数,形参不是实际存在的变量,其目的时接收函数外传入的值。
                # 形参是不分配内存空间的,所以形参不是实际存在的变量
            实参时调用函数时传入的参数,实参可以是常量、变量、表达式、函数等 。
            def test01(a,b,c):
                print(a,b,c))

            # 调用函数
            test01(1,2,3)
            # 输出
            1 2 3   # 函数test01中的 a,b,c 都是形参,调用函数写入的 1,2,3 都是实参

        36.2 位置参数
             位置参数 是函数最常见的一种传参方式,调用函数时实参的数量要和形参保持一致。
             调用函数时,填入的参数,需要和形参的数量保持一致,这些参数即是实参,也是位置参数

        36.3 默认值参数
            在定义函数时,给参数一个默认值;在调用函数时,如果没有给函数中的默认参数赋值,那么使用默认值。
            '''【注意】默认参数一定要放在非默认参数的后面,切记切记切记'''
            例如 7:
            def sums(a,b,c=10):   # 这两个参数 a、b 就是形参;c 就是默认参数,默认值是 10
                print(a + b + c)
            # 调用函数
            sums(1,2) # 这里是没有输入形参c 的值的,但是依然可以输出,不报错,因为c是默认值参数,如果c有输入值,优先取输入的值,如果没输入,则取默认值 10
        36.4 关键字参数,也被称为 命名参数
            关键字参数是在函数调用时,通过参数名来传递值,实参的顺序可以与形参的顺序不一致。
            例如 8:
            def sums(a,b):   # 这两个参数 a、b 就是形参
                print(a + b)
            # 调用函数
            sums(b = 1,a = 2) # 这里就是关键字输入了,指定形参的输入实际值,可以不遵循顺序一直原则 。

        36.5 可变参数
            可变参数指的是“可变数量的参数”,写一个形参就可以接收多个实参,输出"元组"或者"字典"
            可变参数在函数定义中由两种表现形式:
                *param   # 表示可以接收多个  位置参数,位置参数返回的是元组
                **param  # 表示可以接收多个关键字参数,可变参数返回的是字典

            位置可变参数 ''' *param '''
            例如 9:
            def sums(a,b,*c):    # 这两个参数 a、b 就是形参,c 是可变参数
                print(a , b , c) # 打印出参数的值
            # 调用函数
            sums(1,2,3,4,5)
            # 输出
            1 2 (3, 4, 5) # 这里 c 输出的是一个 元组

            关键字可变参数  ''' **param '''
            例如 10:
            def sums(a,b, **c):    # 这两个参数 a、b 就是形参,c 是可变参数
                print(a , b , c) # 打印出参数的值
            # 调用函数
            sums(1,2,c = 3,d = 4,e = 5,f = 6)
            # 输出
            1 2 {'c': 3, 'd': 4, 'e': 5, 'f': 6} # 这里 c 输出的是一个 字典

        36.6 组合参数
            在实际开发中,经常会把位置参数、默认参数、关键字参数、和可变参数混合在一起使用,
            那么需要注意的使用顺序必须是
                首先 位置参数       1
                再是 关键字参数     2
                然后 默认参数       3
                最后 可变参数       4
            例如 11:
            # 写一个函数:两个数的和
            def sums(a,b):   # 这两个参数 a、b 就是形参
                print(a + b)
            # 调用函数
            sums(1,2)        # 这里面的两个参数就是实参,按照位置对应关系,1 赋值给了 a ,2 赋值给了 b
        36.7 强制命名参数
            在带星号的"可变参数"后面增加新的参数,必须在调用的时候"强制命名参数"。
            【操作】强制命名参数的使用
            def f1(*a,b,c):
                print(a,b,c)

            # f1(2,3,4) # 会报错,由于 a 是可变参数,将 2,3,4 全部收集,造成 b 和 c 没有赋值,
            f1(2,3,b = 4,c = 5)
            # 执行结果
            (2,3),4,5

        36.8 lambda 表达式和匿名函数
             lambda 表达式可以用来声明匿名函数。lambda 函数是一种简单的、在同一行中定义函数的方法。
             lambda 函数实际生成了一个函数对象。
             lambda表达式的基本语法如下:
                            lambda arg1,arg2,arg3... : <表达式>
                    arg1/arg2/arg3 为函数的形参,<表达式>相当于函数体,运算结果是: 表达式的运算结果。
             例如:
             f = lambda a,b,c:a+b+c
             print(f(1,2,3))
             # 执行结果
             6 # 由此可见,a,b,c 这三个等同于形参,输出结果等于冒号后面的表达式计算得出的结果

             再比如:
             g = [lambda a:a*2,lambda b:b*3,lambda c:c*4]
             print(g[0](6),g[1](7),g[2](8))
            #执行结果
            12 21 32

    37. 函数调用
        1. 直接调用
        2. 把函数赋给一个变量
        3. 函数内调用函数
        4. 函数作为参数使用
            例如 12:
            def sum2(a,b):
                return a+b

            def sum3(arg,c):
                return arg+c
            # 调用函数
            print(sum3(sum2(1,3),6))
            # 输出 1+3+6 = 10
            10
    38. 变量的作用域
        根据作用域,变量可分为 局部变量 和 全局变量
        1. 全局变量
            全局变量是指在函数外定义的变量
            例如 13:
            a = 1 # 全局变量
            print(a)
            1 # 输出结果为 1
        2. 局部变量
            局部变量是指在函数内部使用的变量,只在函数内部使用,
            当函数调用完以后,局部变量会从内存中释放
            例如 14:
            a = 1 # 全局变量
            def prints():
                a = 2   # 局部变量 a = 2
                print(a)
            prints() # 调用函数 prints 输出结果为 2
            print(a) # 输出全局变量 a  输出结果为 1
        3. 函数内改变全局变量
            如果要在函数内部使用全局变量,只需要在函数内部的变量前加上关键字 global
            例如 15:
            a = 1 # 全局变量
            def prints():
                global a # 函数内改变全局变量 ,在被改变的变量前加关键字 global
                a += 2   # a = a + 2 = 1 + 2 = 3
                print(a)
            prints() # 调用函数 prints 输出结果为 3
            print(a) # 输出全局变量 a  输出结果为 3,因为用了 global 改变了全局变量 a 的值,
    39. 39.1 可变类型全局变量
            可变  :列表、字典        ,函数可使用也可不适用 global
            不可变:字符串、元组、集合,函数必须使用 globa

        39.2
            浅拷贝:不拷贝子对象的内容,只是拷贝子对象的引用;
            深拷贝:会连子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象。
        39.3 传递不可变对象时,不可变对象里面包含的子对象是可变的,则方法内修改了这个可变对象,源对象也发生了变化
            a = (10,20,[30,40])                       # 不可变对象里面包含的子对象是可变的,元组是不可变元素可重复的,列表是可变值也可重复
            print('a的id地址是:',id(a))

            def test01(m):
                print('m的第一次id地址是:',id(m))
                m[2][0] = 888                         # 这里,对元组里面的列表重新赋值,是可以进行的,
                print(m)
                print('m的第二次id地址是:', id(m))   # 地址依然没有改变

            test01(a)
            print('a的id地址是:',id(a))
            # 输出:
            a的id地址是: 2284442744256
            m的第一次id地址是: 2284442744256
            (10, 20, [888, 40])
            m的第二次id地址是: 2284442744256
            a的id地址是: 2284442744256

    40. 递归函数
        1. 递归函数的概念
           函数直接或间接调用自己本身
        2. 递归函数示例
            (1)求1到N 的整数的和
            例如 16:
            # 用非递归方式实现
            sum = 0
            for i in range(1,101):
                sum += i
            print(sum) # 输出值为 5050
            例如 17:
            # 用递归方式实现
            def sum(n):
                if n == 1:
                    return 1
                else:
                    return sum(n - 1)+ n
            print(sum(100)) # 输出值为 5050,递归的原理是:先分解,再归纳
            (2)求n的阶乘 n!
            例如 18:
            def fac(n):
                if n == 0 or n == 1:
                    return 1
                else:
                    return fac(n-1)*n
            print(fac(5)) # 输出值为 120 ,递归的原理是:先分解,再归纳
            (3)用函数实现用户输入的年份是否为闰年
            # 1. 能够被400整除
            # 2. 能够被4整除,但是不能被100整除
            以上两个条件 二选一
            例如 19:
            def leap(n):
                if (n % 400 == 0) or (n % 4 == 0 and n % 100 != 0):
                    return str(n) + '年是闰年'
                else:
                    return str(n) + '年不是闰年'
            # 调用函数
            year = int(input('请输入要检验的年份:'))
            print(leap(year))
            (4) 递归的栈、栈帧、堆
            def test01(n):
                print('test01:',n)
                if n==0:
                    print('over')
                else:
                    test01(n-1)
                print('test01***',n)
            # 调用函数
            test01(4)
            # 执行结果
            test01: 4
            test01: 3
            test01: 2
            test01: 1
            test01: 0
            over
            test01*** 0
            test01*** 1
            test01*** 2
            test01*** 3
            test01*** 4
            '''
            注意看上面的n 是倒序的,下面的是升序的,
            这是因为栈帧的递归函数的栈帧,遵循了"先进后出,后进先出"的原则
            '''

            (5) 嵌套函数,又称 内部函数
                即表示函数中有函数
                1. 封装 - 数据隐藏
                    外部无法访问"嵌套函数"
                2. 贯彻 DRY(Don't Repeat Yourself) 原则
                    嵌套函数,可以让我们再函数内部避免重复代码
                3. 闭包
                    后面会详细讲解
                # 测试嵌套函数的定义
                def PrintName(isChinese,name,familyname):
                    def innerprint(name,familyname):
                        print('{}·{}'.format(name,familyname))
                    if isChinese:
                        innerprint(familyname,name)
                    else:
                        innerprint(name,familyname)

                PrintName(True,'光明','陈')
                PrintName(False,'Ivanka','Trump')
                # 执行结果
                陈·光明
                Ivanka·Trump

            (6) nonlocal 关键字

                nonlocal 用来声明外层的局部变量
                global   用来声明全局变量
                【操作】使用 nonlocal 声明外层局部变量
                # 测试 nonlocal/global 关键字的用法
                a = '中华'  # 全局变量的 a
                def outer():
                    global a # 声明 a 指向的是全局变量的 a
                    b = 10
                    print('第一次打印 a :',a)
                    def inner():
                        global a  # 声明 a 指向的是全局变量的 a
                        nonlocal b  # 声明外部函数的局部变量
                        print("inner b",0)
                        b = 20
                        a = '中华万岁'
                        print('第二次打印 a :', a)
                    inner()
                    print('outer b',b)
                    print('第三次打印 a :', a)
                # 调用函数
                outer()
                # 执行结果
                第一次打印 a : 中华
                inner b 10
                第二次打印 a : 中华万岁
                outer b 20
                第三次打印 a : 中华万岁
            (7) LEGB 规则
                python 在查找"名称"时,时按照 LEGB 规则查找的:Local --> Enclosed --> Global --> Built in
                【解释】
                Local    :指的就是函数或者类的方法内部
                Enclosed :指的是嵌套函数(一个函数包裹另一个函数,闭包)
                Global   :指的是模块中的全局变量
                Built in :指的是 Python 为自己保留的特殊名称

                如果某个 name 映射在局部(local)命名空间中没有找到,
                接下来就会在闭包作用域(enclosed)进行搜索,
                如果闭包作用域也没有会遭找到,Python就会到全局(global)命名空间中进行查找,
                最后会在内建(built in)命名空间搜索
                (如果一个名称在所有命名空间中,没有找到,就会产生一个 NameError)。

    42. 函数存于模块
        1. 什么是模块
           将函数存储在独立的文件中,可隐藏程序代码的细节,重点放在程序业务逻辑上,有利于函数的复用性。
           模块本质上也是一个文件,但是不同的是模块是可供其他文件反复使用,模块扩展名也是 .py 文件 。
        2. 导入模块
            导入模块使用关键字 import
        3. 导入模块中指定函数
            格式如下:
                     from module_name import function_name_1,function_name_2,...,function_name_N # 指定从模块 module_name 导入函数 1-N
            # 创建模块,即新建一个 .py 的文件,文件里面是一些函数 或者 变量
            例如 20:
            # 新建一个 module.py 的文件,文件里面写上一个函数,一个变量,如下
            def prints():             # 函数 prints
                return 'helloworld'
            a = '123'                 # 变量 a
            # 然后就可以在另一个文件中调用这个模块里面的函数或者变量
            import module          # 导入模块 module
            print(module.prints)   # 调用模块中的函数 输出值为 'helloworld'
            print(module.a)        # 调用模块中的变量 输出值为 '123'
        4. 给函数指定别名
            from module_name import function_name_1 as f_n_1 , function_name_2 as f_n_2 , function_name_3 as f_n_3
        5. 给模块指定别名导入
            import module_name as alias_name
        6. 导入模块中的所有函数
            from module_name import *
            ''' 注意: 不推荐使用 * 导入所有的函数,因为如果模块的函数非常多的话,会导致反应慢 '''
    43. 文件的操作
        学习要点:
                1. 什么是文件
                    文件分为 文本文件 和 二进制文件;在计算机中,文件一般是以二进制的方式保存在电脑中。
                2. 文件的作用
                    将数据长期保存,在需要的时候使用,
                    长期存储设备:磁盘、U盘、移动硬盘、光盘
                3. 文件操作流程
                    一般步骤为:
                        打开文件或者新建文件 -> 读/写数据 -> 关闭文件
                        3.1 文件打开
                            在 python 中,可使用 open 函数打开一个文件,并返回操作这个文件的对象
                            变量名 = open(文件名,打开方式)
                            # 请注意,这个'文件名',不可为空;'打开方式',可为空亦可不为空,一般建议都写上
                            例如 21:
                            f = open('test.txt','w')
                            # 其中文件名是文件的实际名字,也可以是包含完整路径的名字。
                            # 打开方式用于控制哪种方式打开文件, open 函数提供了 7 中基本打开方式
                            7 种打开方式如下:
                            打开方式   说明
                            r          默认方式,以只读方式打开,文件必须存在,否则返回异常。               # read
                            w          只写方式打开文件。如果该文件存在会被覆盖,如果不存在那么会创建文件。 # write
                            x          只写方式打开文件。如果文件不存在,那么创建,如果存在,返回异常。     # 适合不存在的文件
                            a          文件追加,如果文件已存在,把新内容写入已有文件内容后面,如果不存在文件,则创建文件,然后写入。
                            b          二进制方式打开文件。
                            t          文本文件打开方式,是 默认方式。
                            +          与 r/w/x/a 一同使用,在原有功能基础上同时增加读写功能
                            # 说明:r/w/x/a 可以和 b/t/+ 组合使用,例如 rb、wb、ab、r+
                        3.2 文件关闭
                            关闭文件使用 close 函数
                            f.close()
    45. 文件的写入
            45.1 写数据
                 使用 write() 和 writelines() 来完成数据写入文件,
                45.1.1 write() 该方法将指定的数据写入文件,只能是接收字符串的写入,不能是其他的类型,
                    例如 22:
                    # open 打开文件; 'w' 如果文件不存在则创建,如果文件存在,则清空文件内容以待写入新的内容;encoding 在python中使用编码'utf-8'读取中文
                    f = open('test.txt','w',encoding = 'utf-8')
                    # 写入内容
                    f.write('低头思故乡')
                    # 关闭文件
                    f.close()
                45.1.2 writelines() 该方法将一个字符串序列整个写入文件
                    例如 23:
                    # open 打开文件; 'w' 如果文件不存在则创建,如果文件存在,则清空文件内容以待写入新的内容;encoding 在python中使用编码'utf-8'读取中文
                    f = open('test.txt','w',encoding = 'utf-8')
                    # 写入内容
                    lists = ['床前明月光,','疑似地上霜。','举头望明月,','低头思故乡。']
                    f.writelines(lists)
                    # 关闭文件
                    f.close()
            45.2 读数据
                 可用 read 、readline、readlines 完成数据的读取
                 45.2.1 read()
                        使用 read(size) 可以从文件中读取数据,size 表示要从文件中读取数据的长度,如果没有传入 size ,
                        那么表示读取文件中所有的数据,返回的字符串
                        ''' 这个函数返回的是字符串,切记 '''
                        例如 24:
                        f = open('test.txt','r',encoding= 'utf-8')
                        content = f.read()
                        print(content)
                45.2.2 readline() 该方法返回一个字符串,返回的内容为文件的一行内容
                        例如 25:
                        f = open('test.txt','r',encoding= 'utf-8')
                        content = f.readline()
                        print(content)
                45.2.3 readlines() 该方法从文件中读取所有的行,返回以每行为元素的列表
                        例如 26:
                        f = open('test.txt','r',encoding= 'utf-8')
                        content = f.readlines()
                        print(content)

    47. 面向对象 VS 面向过程
        面向过程强调的是步骤、是过程。
        面向对象强调的是封装性和代码的可复用性。
      47.1 对象的进化
           随着编程面临的问题越来越复杂,编程语言本身也在进化,从主要处理简单数据开始,随着数据变多进化"数组";
           数据类型变复杂,进化出了"结构体",
           处理数据的方式和逻辑变复杂,进化出了"对象"。
        比如:买一台笔记本电脑
        (1)面向过程的实现方式:
           1)网上查找资料看哪个品牌的电脑口碑好(苹果或联想还是戴尔)
           2)根据自己的预算来定电脑型号
           3)去实体店看自己喜欢的款式,业务员在身边不断的推荐
           4)花了1小时砍价,最后业务员同意优惠了200元
           5)成交
        (2)面向对象
           1)找一个懂电脑的高手
           2)给钱成交
        比如:解决吃红烧鱼的问题
        (1)面向过程
           1)养鱼
           2)等鱼长大
           3)杀鱼
           4)准备佐料
           5)红烧
           6)吃鱼
        (2)面向对象
           1)找一家做红烧鱼的饭店
           2)给钱交易
           3)吃鱼
        # 面向过程:每一步都是自己干,强调过程、步骤
        # 面向对象:能省则省,如果有现成,拿来即用
        # 不管是面向对象还是面向过程,都是解决问题的一种手段,各有优势,只是目前面向对象编程更加贴近实际。


    48. 类和对象
        面向对象中最重要的两个概念是:类和对象
        对象是面向对象编程的核心,把具有共同特征和行为的一组对象抽象出来,就成了类。
        1. 类
           (1)类
              类是抽象的,是蓝图
           (2)类,有三部分构成,
              1)类的名称,即类名
              2)类的属性,即一组数据
              3)类的方法,即行为
                   #比如:人类
                    类名:Person
                    属性:身高(height)、体重(weight)、年龄(age)
                    方法:跑步(run)、散步(walk)、说话(speak)
                   #比如:狗类
                    类名:Dog
                    属性:毛发、品种、名字
                    方法:叫、打滚
        2. 对象
           对象是类的实例化,是具体的,是现实存在的。一个类可实例为多个对象。

   49. 定义类
        类编码风格
          49.0.1. 类名首字母大写,多个单词之间采用驼峰原则。
          49.0.2. 实例名、模块名 采用小写,多个单词之间采用下划线隔开。
          49.0.3. 每个类,应紧跟“文档字符串”,说明这个类的作用。
          49.0.4. 可以用空行组织代码,但不能滥用。在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类

       1. 定义类的格式如下:
            class 类名:
                属性
                方法
            ''' 人类
             类名 Person 大驼峰法,类名的命名规则,必须符合变量的命名规则
             属性 身高 体重 年龄 和普通的变量非常类似,属性是属于某一个类的,不再属于全局的
             方法 吃饭 散步 说话 和普通的函数非常类似,方法是属于某一个类的,不再属于全局的
             '''
            例如 27:
            class Person:# 类名后面可以带括号 Person(),
                # 定义属性
                height = 180
                weight = 160
                age = 30
                def eat(self):# 方法后面必须有 (self)
                    pass

                def run(self):
                    pass

                def speak(self):
                    pass

            例如28:
            class Student: # 类名 一般首字母大写,多个单词采用驼峰原则

                def __init__(self,name,score): # self 必须位于第一个参数
                    self.name = name # 'gaoqi'
                    self.score = score # '60'

                def say_score(self): # self 必须位于第一个参数
                    print('{0}的分数是:{1}'.format(self.name,self.score))
            # 调用类
            s1 = Student('高淇',18)
            s1.say_score()
            s1.age = 20
            print(s1.age) ''' 这里可以看到 age 这个属性是在这里添加的,类里面并没有,但确实可以添加,
                              并且这个 age 属性,只属于s1这个对象,下面的 s2对象也想打印age,就会报错
                          '''
            s2 = Student('陈光明',28)
            s2.say_score()
            print(s1.age) # 报错,age 是s1这个对象的,s2并不具有
            # 执行结果
            高淇的分数是:18
            20
            陈光明的分数是:28

        2. 构造函数 __init__()
           类是抽象的,也称之为"对象的模板",我们需要通过类这个模板,创建类的实例对象,然后才能使用类定义的功能。
           我们前面说过一个 Python 对象包含三个部分:id (identity识别码)、type(对象类型)、value(对象的值)。

           现在我们可以进一步的说,一个Python对象包含如下部分:
           1. id (identity识别码)
           2. type(对象类型)
           3. value(对象的值)
                (1)属性(attribute)
                (2)方法(method)
        3.  对象
            将不同类型的数据、方法(即函数)放到一起,就是对象。比如:

            class Student:
                company = "SXT" #类属性
                count = 0       #类属性
                def __init__(self,name,score):
                    self.name = name          #实例属性
                    self.score = score
                    Student.count = Student.count+1
                def say_score(self):          #实例方法
                    print("我的公司是:",Student.company)
                    print(self.name,'的分数是:',self.score)

        4. 实例属性
           实例属性是从属于实例对象的属性,也称为“实例变量”。他的使用有如下几个要点:
            1. 实例属性一般在__init__()方法中通过如下代码定义:
                self.实例属性名 = 初始值
            2. 在本类的其他实例方法中,也是通过 self 进行访问:
                self.实例属性名
            3. 创建实例对象后,通过实例对象访问:
                obj01 = 类名() #创建对象,调用__init__()初始化属性
                obj01.实例属性名 = 值 #可以给已有属性赋值,也可以新加属

        5. 实例方法
            实例方法是从属于实例对象的方法。实例方法的定义格式如下:
                def 方法名(self [, 形参列表]):
                    函数体
            方法的调用格式如下:
                    对象.方法名([实参列表])

            '''
            函数和方法的区别
            1. 都是用来完成一个功能的语句块,本质一样。
            2. 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点。
            3. 直观上看,方法定义时需要传递 self,函数不需要
            '''
             实例对象的方法调用本质:
            class Student: # 类名 一般首字母大写,多个单词采用驼峰原则

                def __init__(self,name,score): # self 必须位于第一个参数
                    self.name = name # 'gaoqi'
                    self.score = score # '60'

                def say_score(self): # self 必须位于第一个参数
                    print('{0}的分数是:{1}'.format(self.name,self.score))

                a = Student()
                a.say_score() # 这是我们程序员写的时候调用的语句,但是在解释器中,Student.say_score(a)  是这样翻译的
                【形象比喻】
                a = Student()
                a.say_score() --> 【解释器翻译】 Student.say_score(a)
                 其他操作:
                    1. dir(obj)可以获得对象的所有属性、方法
                    2. obj.__dict__ 对象的属性字典
                    3. pass 空语句
                    4. isinstance(对象,类型) 判断“对象”是不是“指定类型”【这个 isinstance 很重要,请一定要熟悉】

        6. 类方法
           类方法是从属于"类对象"的方法。类方法通过装饰器 @classmethod 来定义,格式如下:
                 @classmethod
                 def 类方法名(cls[,形参列表]):
                    函数体
            要点如下:
            6.1 @classmethod 必须位于方法上卖弄一行
            6.2 第一个cls必须有;cls 指的就是"类对象"本身;
            6.3 调用类方法格式:“类名.类方法名(参数列表)”。参数列表中,不需要也不能给 cls 传值。
            6.4 类方法中访问实例属性和实例方***导致错误
            6.5 子类继承父类方法时,传入 cls 时子类对象,而非父类对象
            【操作】类方法使用测试
                    class Student:
                        company = 'SXT' # 类属性

                        @classmethod
                        def printCompany(cls):
                            print(cls.company)
                    # 调用类方法
                    Student.printCompany()

        7. 静态方法
           Python 中允许定义与"类对象"无关的方法,成为"静态方法"。
           "静态方法"和在模块中定义普通函数没有区别,只不过"静态方法"放到了"类的名字空间里面",需要通过“类调用”。

           静态方法通过装饰器 @staticmethod ,格式如下:
                @staticmethod
                def 静态方法名([形参列表]):
                    函数体

            要点如下:
                7.1 @staticmethod 必须位于方法上面一行
                7.2 调用静态方法格式:"类名.静态方法名(参数列表)"。
                7.3 静态方法中访问实例属性和实例方***导致错误
                【操作】静态方法使用测试
                class Student:
                    company = 'SXT' # 类属性

                    @staticmethod
                    def add(a,b): # 静态方法
                        print('{0}+{1}={2}'.format(a,b,(a+b)))
                        return a+b
                    # 调用:
                    Student.add(20,30)

        8.  '''
            093.类属性_内存分析创建类和对象的底层
            '''
            class Student:
                company = 'SXT' # 类属性
                count = 0       # 类属性

                def __init__(self,name,score):
                    self.name = name   # 实例属性
                    self.score = score # 实例属性
                    Student.count = Student.count + 1
                def say_score(self):
                    print('我的公司是:',Student.company)
                    print(self.name,'的分数是:',self.score)

            # a = Student('Sunny',99)
            # print(a.say_score())
            s1 = Student('Sunny',80)
            s1.say_score()
            print('一共创建{}个Student对象'.format(Student.count))
            # 执行结果
            我的公司是: SXT
            Sunny 的分数是: 80
            一共创建1个Student对象

        9. 方法的动态性
            # 测试方法的动态性
           在其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。
           方法签名包含3个部分:方法名、参数数量、参数类型
           Python 中,方法的参数没有类型(调用时确定参数的类型),参数的数量也可以由
           可变参数控制,因此,Python 中是没有方法的重载的,定义一个方法即可有多种调用方式,
           相当于实现了其他语言中的方法的重载。

           【重点】入股哦我们在类体中定义了多个崇明的方法,只有最后一个方法有效。
                   建议:不要使用重名的方法!Python 中方法没有重载。

            class Person:
                def work(self):
                    print('努力上班!')

            def play_game(s):
                print('{0}在玩游戏'.format(s))

            Person.play = play_game
            p = Person()
            p.work()
            p.play()
            a = play_game('张三')
            print(a)
                #执行结果
                努力上班!
                <__main__.Person object at 0x000001F8B07D2FD0>在玩游戏
                张三在玩游戏
                None
            # 方法没有重载,只有替换,如下
            def work2(s):
                print('好好工作,努力上班!赚大钱,娶媳妇')
            Person.work = work2 # 这里可以看到,新写的 work2 赋值给了类的work,等同于重写了 work ,原来work的结构体被删除
            p.work() # 执行结果是:好好工作,努力上班!赚大钱,娶媳妇

        10. 【私有属性】 和 【私有方法】
            【私有属性】
            Python 对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别,关于私有
            属性和私有方法,有如下要点:
                1. 通常我们约定,两个下划线的属性时私有的,其他为公共的;
                2. 类内部可以访问私有属性/方法;
                3. 类外部不能直接访问私有属性/方法,可以间接访问;
                4. 类外部可以通过'_类名__私有属性(方法)名' 访问私有属性(方法)

            【注意】方法本质上也是属性,只不过是可以通过()执行而已,所以,此处讲的私有属性和公有属性,
                    也同时也讲解了私有方法和公有方法的用法,
            # 私有属性 和 私有方法
            class Student:
                '''私有属性'''
                def __init__(self,name,age,high):
                    self.name = name    # 公有属性
                    self.age = age      # 公有属性
                    self.__high = high  # 私有属性
            # 执行
            a = Student('Sunny',18,'180')
            print(a.name)
            print(a.age)
            #print(a.high) # 会报错,提示对象没有这个属性,但确实是存在的,只是是私有属性
            print(a._Student__high) # 类外部可以通过'_类名__私有属性(方法)名' 访问私有属性(方法)

            【私有方法】
            私有方法 跟私有属性,差不多,例如:
            class Student:
                '''私有属性'''
                __company = '百战程序员'  # 类的私有属性

                def __init__(self, name, age, high):
                    self.name = name  # 公有属性
                    self.age = age  # 公有属性
                    self.__age = age  # 私有属性
                    self.__high = high  # 私有属性

                def __work(self):
                    print('好好工作,努力上班!赚大钱,娶媳妇')
                    print('年龄:{}'.format(self.__age))  # __age 是私有属性,但是在类的内部可以调用
                    print(Student.__company)  # __company 是私有属性,但是在类的内部可以调用

            # 执行
            a = Student('Sunny', 18, '180')
            print(a.name)
            print(a.age)
            # print(a.high) # 会报错,提示对象没有这个属性,但确实是存在的,只是是私有属性
            print(a._Student__high)  # 类外部可以通过'_类名__私有属性(方法)名' 访问私有属性(方法)
            a._Student__work()       # 类外部可以通过'_类名__私有属性(方法)名' 访问私有属性(方法)
            # 执行结果
            Sunny
            18
            180
            好好工作,努力上班!赚大钱,娶媳妇
            年龄:18
            百战程序员

        11. @property 装饰器_get和set方法
            @property 主要用于帮助我们处理属性的读操作、写操作。对于某一个属性,我们可以直接通过 "类名.属性名" 直接调用
            @property 可以将一个方法的调用方式编程"属性调用"。下面是一个简单的示例:

            class Employee:

                def __init__(self,name,salary):
                    self.__name = name
                    self.__salary = salary

                def get_salary(self):
                    return self.__salary

                def set_salary(self,salary):
                    if 1000<salary<50000:
                        self.__salary = salary
                    else:
                        print('录入错误!薪水在1000--50000这个范围')

            emp1 = Employee('Sunny','30000')
            print('第1次打印')
            print(emp1.get_salary())
            print('第2次打印')
            emp1.set_salary(2000)
            print('第3次打印')
            print(emp1.get_salary())
                # 执行结果
                第1次打印
                30000
                第2次打印
                第3次打印
                2000

            # 测试@property装饰器_get和set方法
            class Employee:

                def __init__(self,name,salary):
                    self.__name = name
                    self.__salary = salary

                @property
                def salary(self):
                    return self.__salary

                @salary.setter
                def salary(self,salary):
                    if 1000<salary<50000:
                        self.__salary = salary
                    else:
                        print('录入错误!薪水在1000--50000这个范围')
            emp1 = Employee('Sunny','30000')
            print('第4次打印')
            print(emp1.salary)
            print('第2次打印')
            emp1.salary = 2000 # 这里因为用了 @property 所以可以直接读取 私有属性,@salary ,重新赋值,再执行方法 salary
            print('第3次打印')
            print(emp1.salary)
            print('第4次打印')
            # 执行结果
            第4次打印
            30000
            第2次打印
            第3次打印
            2000
            第4次打印

        12. 属性和方法命名总结
            · _xxx   :保护成员,不能用“from module import * ”导入,只有类对象和子类对象能访问这些成员。
            · __xxx__:系统定义的特殊成员
            · __xxx  :类中的私有成员,只有类对象自己能访问,子类对象也不能访问。
                     (但,在类外部可以通过“对象名. _类名__xxx”这种特殊方式访问。Python 不存在严格意义的私有成员)
            注:再次强调,方法和属性都遵循上面的规则。

    50. 创建对象
        类是抽象的,是图纸;比如有一张车的图纸,那么可以根据车图纸造出具体的一辆车。
        也就是说可以根据类创建出具体的对象。在 python 中创建对象的方式如下:
        格式 :对象名 = 类名()
        # 定义汽车类
        例如 28:
        class Car():
            make = 'audi'
            model = 'a4'
            year = 2020
            # 方法
            def run(self):
                print('车正在奔跑')
        # 类的实例化
        my_car = Car()
        print(my_car.make, my_car.model, my_car.year)
        my_car.run() # 类的方法,可以通过赋值直接调用,而不用再次通过 print 打印输出
        # 类的属性可以修改
        my_car.make = 'BMW'
        my_car.model = 'X6'
        print(my_car.make)
        print(my_car.model)
        # 类可以实例化成多个对象,类和对象是一对多的关系
        your_car = Car()
        your_car.make = '别克'
        your_car.model = '君越'
        print(your_car.make)
        print(your_car.model)
        # 区分是否为同一个对象
        print(id(my_car))
        print(id(your_car))
        # 输出:
        audi a4 2020
        车正在奔跑
        BMW
        X6
        别克
        君越
        2652290904016
        2652290903824

        【多说一句】对象中的方法也是属性,只不过操作不一样

51. 魔法方法
    在 python 中,一般把这种 __xxx__()类型的方法称为魔法方法,因其具有特殊的功能。只要满足条件,就会自动调用。
    1. __init__ 方法
       作用:1)在创建对象时,给属性赋值
             2)__init__ 方法在类创建时,被默认自动调用
             3)__init__ 方法中的self不能少,Python 中会自动传对象的引用
        class 类名:
            def  __init__():
                pass
        例如 29:
        class Person():
            def __init__(self,name,age)
                self.name = name
                self.age = age
                self.job = '学生' # 默认值

            def speak(self):
                print('speak speak speak')

            def eat(self):
                print('eat eat eat')

        obj1 = Person('lisi', 20)   # 对象一
        print(obj1.name,obj1.age,obj1.job)   # 输出:lisi 20 学生
        obj2 = Person('wangwu',30)  # 对象二
        print(obj2.name,obj2.age,obj2.job)   # 输出:wangwu 30 学生

    2. __str__ 方法 # 打印自动调用的魔法方法,有利于保护类
       当用 print 打印对象时,会自动调用 __str__ 方法
       例如 30:
        class Person():
            def __init__(self,name,age):
                self.name = name
                self.age = age
                self.job = '学生' # 默认值

            def speak(self):
                print('speak speak speak')

            def eat(self):
                print('eat eat eat')

            def __str__(self):
                return '这不是一个对象' # 这里是 return ,切记

            obj1 = Person('lisi', 20)
            print(obj1)
            # 运行之后,print(obj1) 不会报错,因为里面有一个 打印自动调用的魔法方法 __str__ ,输出为 :这不是一个对象


    3. __del__ 方法,执行删除语句时,会自动调用该方法
       __del__ 方法,称为"析构方法",用于实现对象被销毁时所需的操作,比如:释放对象占用的资源,例如:打开的文件资源、网络连接等。
            Python 实现自动的垃圾回收,当对象没有被引用时(引用计数为0),由垃圾回收期调用 __del__ 方法。【引用计数为0】
            我们也可以通过 del 语句删除,从而保证调用 __del__ 方法。系统会自动提供 __del__ 方法,一般不需要自定义析构方法。
       当对象被执行删除动作时,会自动调用 __del__ 方法
       例如 30:
        class Person():
            def __init__(self,name,age)
                self.name = name
                self.age = age
                self.job = '学生' # 默认值

            def speak(self):
                print('speak speak speak')

            def eat(self):
                print('eat eat eat')

            def __str__(self):
                return '这不是一个对象' # 这里是 return ,切记

            def __del__(self):
                print('该对象被删除了')

            obj1 = Person('lisi', 20)
            del obj1 # 删除对象
            # 运行之后,不会报错,因为里面有一个 __del__ ,执行删除语句时,会自动调用该方法,输出为:该对象被删除了

        再例如:
            class Person:

                def __del__(self):
                    print('销毁对象{0}'.format(self))

            p1 = Person()
            print('执行P1')
            p2 = Person()
            print('执行P2')
            del p2
            print('程序结束1234')
            # 执行结果
            执行P1
            执行P2
            销毁对象<__main__.Person object at 0x000002745A3292B0> # 这个打印时 执行 del p2 时调用了 __del__ 打印的
            程序结束1234
            销毁对象<__main__.Person object at 0x000002745A329790> # 程序结束,p1 没有指向任何对象,释放资源,自动调用 __del__ 时打印的

    4. __call__ 方法和可调用对象
       定义了 __call__ 方法的对象,称为"可调用对象",即该对象可以像函数一样被调用。
            class SalaryAccount:
                '''输入月薪,计算 年薪、日薪、时薪'''
                def __call__(self,salary):
                    print('算工资啦...')
                    yearsalary = salary*12    # 年薪
                    monthsalary = salary      # 月薪
                    daysalary = salary//22.5  # 日薪 22.5 国家规定的每个月的平均工作天数
                    hoursalary = daysalary//8 # 时薪

                    return dict(yearsalary=yearsalary,monthsalary=salary,daysalary=daysalary,hoursalary=hoursalary)
            #调用方法
            s1 = SalaryAccount()
            print(s1(30000))
            #执行结果
            算工资啦...
            {'yearsalary': 360000, 'monthsalary': 30000, 'daysalary': 1333.0, 'hoursalary': 166.0}


    52. 理解 self
        self 可以理解为对象自身,当某个对象调用其方法时,Python解释器会自动把这个对象作为第一个参数传递给 self,
        而开发者只需要传递 self 后面的参数即可。
        例如 31:
        class Car:
            # self 表示对象的自身,类中的self可以是其他的名字,但是通常使用self
            def __init__(self,make,model):
                # 对象属性
                self.make = make
                self.model = model

            def getName(self):
                return self.make

            def getModel(self):
                return self.model

        my_car = Car('audi','a4')
        print(my_car.make,my_car.model)
        print(my_car.getName())
        # 输出
        audi a4
        audi

    54. 继承分为单继承和多继承

        54.1 继承的概念
             在现实生活中,继承一般指的是子女继承父辈的财产;
             在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物。
             1. 成员继承:子类继承了父类除构造方法之外的所有成员。
             2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为"重写"。
                【操作】继承和重写的案例
                class Person:
                    def __init__(self,name,age):
                        self.name = name
                        self.age = age

                    def say_age(self):
                        print(self.name,'的年龄是:',self.age)

                    def say_name(self):
                        print('我的名字是:{}'.format(self.name))
                        print('很多人名字是:',self.name)


                class Student(Person): #  Student(Person) 可以理解为 类Student 继承了 类 Person,所以他们是父子关系
                    def __init__(self,name,age,score):
                        Person.__init__(self,name,age) # 必须显式的调用父类初始化方法,不然解释器不会去调用
                        self.score = score # score 是子类新增的属性

                    def say_name(self):
                        '''重写父类的方法 say_name 但是并不会影响父类的 say_name'''
                        print('报告老师,我的名字是:{}'.format(self.name))


                a = Person('中华',18)
                a.say_age()
                a.say_name()

                b = Student('Sunny',20,90)
                b.say_age()
                b.say_name() '''重写父类的方法 say_name 但是并不会影响父类的 say_name'''
                    # 执行结果
                    中华 的年龄是: 18
                    我的名字是:中华
                    很多人名字是: 中华
                    Sunny 的年龄是: 20
                    报告老师,我的名字是:Sunny

        54.2 单继承示例
             动物 这个类中,可以有 猫、狗等,
             那么 '动物'就是父类,猫、狗等,都是子类,
             一个父类,可以有多个子类,而一个子类只能有一个父类,这就是 1对多的关系了,俗称:1:N
            例如 32:
            class Animal:
                '''定义类中的属性和方法'''
                name = 'animal'
                color = 'white'
                def run(self):
                    print('run run run')

                def roll(self):
                    print('roll roll roll')

                # 私有方法,在方法前面用两个下划线 __方法名称,私有的方法是不能被继承的
                def __eat(self):
                    print('eat eat eat')

            class Cat(Animal):
                def walk(self):
                    print('car walk')

            # 子类继承了父类中公有的属性和公有的方法,不能继承父类中的私有属性和方法
            obj = Cat()# 定义对象
            obj.walk()# 输出: car walk
            # 访问属性
            print(obj.name,obj.color) # 输出: animal white
            obj.run() # 输出:run run run
            obj.roll()# 输出:roll roll roll
            obj.eat() # 输出:报错,因为这个父类的方法 eat 是私有的,不能够被继承,所以报错。

        54.3 object 根类
             object 类是所有类的父类,因此所有的类都有 object 类的属性和方法。我们显然有必要深入研究一下 object 类的结构。
             对于我们继续深入学习 Python 很有好处。

             dir() 查看对象属性
             为了深入学习对象,我们先学习内置函数 dir() ,他可以让我们方便的看到指定对象所有的属性。
             再通过类的方法 mro() 或者类的属性 __mro__ 可以输出这个类的继承层次结构。
             【操作】查看类的继承层次结构
             class A: pass
             class B(A): pass
             class C(B): pass

             print(C.mro())
             # 运行结果
             [<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
             # 从结果可以看到,父子关系是往上走的,最后的父类是 object,这就说明了,object 是所有类的父类

            【操作】查看对象所有属性以及和 object 进行比对
            class Person:
                def __init__(self,name,age):
                    self.name = name
                    self.age = age

                def say_age(self):
                    print(self.name,'的年龄是:',self.age)

                def say_name(self):
                    print('我的名字是:{}'.format(self.name))
                    print('很多人名字是:',self.name)

                #执行
                obj = object()
                print(dir(obj))
                print('*'*10)
                s1 = Person('Sunny',18)
                print(dir(s1))
                # 运行结果
                ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__'
                , '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__'
                , '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__'
                , '__sizeof__', '__str__', '__subclasshook__']
                **********
                ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__'
                , '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__'
                , '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__'
                , '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age', 'say_name']
                '''
                从上面我们可以发现这样几个要点:
                1. Person 对象增加了六个属性:
                __dict__ __module__ __weakref__ age name say_age
                2. object 的所有属性,Person 类作为 object 的子类,显然包含了所有的属性。
                3. 我们打印 age、name、say_age,发现 say_age 虽然是方法,实际上也是属性。只不过,
                这个属性的类型是“method”而已。
                    age <class 'int'>
                    name <class 'str'>
                    say_age <class 'method'
                '''
                【注】关于 object 这些属性的详细学习,会在后面学习中逐个涉及。在此,无法一一展开

    55. 多重继承
        如果一个子类能够继承多个父类,称为 多继承 。
        Python 支持多重继承,一个子类可以有多个"直接父类"。这样,旧具备了"多个父类"的特点。但是由于这样会被"类的整体层次"搞得异常复杂,尽量避免使用。
        【操作】对于 C 类来说,是功更加强大了,但是对于整个架构来说,却是危害性特别大。耦合度极大增加,维护难。尽量避免使用。
        class A:
            def aa(self):
                print('aa')
        class B:
            def bb(self):
                print('bb')
        class C(A,B):
            def cc(self):
                print('cc')
        c = C()
        c.aa()
        c.bb()
        c.cc()
        # 运行结果
        aa
        bb
        cc

    56. MRO() 方法
        Python 支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将"从左到右"按顺序搜索。
        MRO (Method Resolution Order):方法解析顺序。我们可以通过 mro() 方法获得"类的层次结构"。方法解析顺序也是按照这个"类的层次结构"寻找的。
        【操作】
        class A:
            def say(self):
                print('aa')
        class B:
            def say(self):
                print('bb')
        class C(A,B):
            def cc(self):
                print('cc')
        c = C()
        c.say()
        # 运行结果,如果类 C 的父类的顺序换一下,改成 C(B,A),那么打印出来的就是 bb,
        aa

    57. super() 获得父类定义
        # 测试 super(),代表父类的定义,而不是父类的对象

        class A:

            def say(self):
                print('A  的地址是:',self)


        class B(A):

            def say(self):
                #A.say(self) # B里面有方法 say,A也有,后来的覆盖前面的,所以这里发生了重写。
                super().say() # 这里等同于上面的 A.say(self)
                print('B  的地址是:',self)

        s = B()
        s.say()
        # 运行结果
        A  的地址是: <__main__.B object at 0x000002A973762FA0>
        B  的地址是: <__main__.B object at 0x000002A973762FA0>

    58. 多态
        多态 是指同一个方法调用由于对象不同可能产生不同的行为。
            在现实生活中,我们有很多例子。例如:同样是调用人的休息方法,张三的休息是睡觉,李四的休息是玩游戏,Sunny的休息是敲代码。
            同样是吃饭的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。
            关于 多态,要注意一下 2 点:
            1. 多态是方法的多态,属性没有多态;
            2. 多态的存在有 2 个必要条件:继承、方法重写。
            class Man:
                def eat(self):
                    print('饿了,各国人吃饭的方法。')

            class Chinese(Man):
                def eat(self):
                    print('中国人用筷子吃饭。')

            class English(Man):
                def eat(self):
                    print('英国人用刀叉吃饭。')

            class Indian(Man):
                def eat(self):
                    print('印度人用右手吃饭。')

            def manEat(m):
                if isinstance(m,Man):
                    m.eat() # 多态,一个方法 调用,根据对象的不同,调用不同的方法。
                else:
                    print('输入错误,不能吃饭。')
            # 调用
            manEat(Man())
            manEat(Chinese())
            manEat(English())
            manEat(Indian())
            manEat(Man())
            # 运行结果
            饿了,各国人吃饭的方法。
            中国人用筷子吃饭。
            英国人用刀叉吃饭。
            印度人用右手吃饭。
            饿了,各国人吃饭的方法。

    59. 特殊方法和运算符重载
        解析:前面我们说过,在 Python 中,有一些内置函数,称为特殊的方法,有自己的功能,是软件自带的函数。
              但是这些内置函数还是可以重载,也就是重新加载,改变原有的功能。
        【操作】加法的功能,是一个 '+' 体现的,但是在实际中,解释器调用的是 '__add__' 函数
        a = 10
        b = 20
        c = a + b # 我们运算 a 和 b ,我们写成 'a+b' 而已,在实际运算中,解释器会调用这个函数'__add__'去计算。
        # c 等同于下面的 d 的运算
        d = a.__add__(b)
        # c 和 d 的结果都是 10 + 20 = 30
        【操作】
        class Person:
            def __init__(self,name):
                self.name = name

            def __add__(self, other): # __add__ 等同于 加法
                if isinstance(other,Person): # isinstance(对象,类型) 判断“对象”是不是“指定类型”
                    return '{0}--{1}'.format(self.name,other.name)
                else:
                    return '不是同类对象,不能相加'

            def __mul__(self, other):  # __mul__ 等同于 乘法
                if isinstance(other,int): # isinstance(对象,类型) 判断“对象”是不是“指定类型”
                    return self.name*other
                else:
                    return '不是同类对象,不能相加'

        p1 = Person('Sunny')
        p2 = Person('boy')
        x = p1 + p2
        print(x)
        print(p1*3) # 这里输入了一个 3 ,对应 __mul__ 形参的 other
        # 执行结果
        Sunny--boy
        SunnySunnySunny

        【常见的特殊方法统计如下】
        # 方法               说明             例子
        __init__             构造方法         对象创建:p = Person()
        __del__              析构方法         对象回收
        __repr__,__str__     打印,转换        print(a)
        __call__             函数调用         a()
        __getattr__          点号运算         a.xxx
        __setattr__          属性赋值         a.xxx = value
        __getitem__          索引运算         a[key]
        __setitem__          索引赋值         a[key]=value
        __len__              长度             len(a)

        【每个运算符实际上都对应了相应的方法,统计如下】
        # 运算符              特殊方法                 说明
        运算符 +             __add__                   加法
        运算符 -             __sub__                   减法
        <,<=,==              __lt__,__le__,__eq__      比较运算符
        >,>=,!=              __gt__,__ge__,__ne__      比较运算符
        |,^,&                __or__,__xor__,__and__    或、异或、与
        <<,>>                __lshift__,__rshift__     左移右移
        *,/,%,//             __mul__,__truediv__,__mod__,__floordiv__     乘,浮点除,模运算(取余),整数除
        **                   __pow__                   指数运算

    60. 特殊属性
        python 对象中包含了很多下划线开始和结束的属性,这些是特殊属性,有特殊用法。
        这里我们列出常见的特殊属性:
        【列表】 其中 obj 指的是普通的对象,class 指的是类名
        # 特殊方法                   含义
          dir(obj)                   对象的所有属性、方法
          obj.__dict__               对象的属性字典
          obj.__class__              对象所属的类
          class.__bases__            类的基类元组(多继承)
          class.__base__             类的基类
          class.__mro__              类层次结构
          class.__subclasses__()     子类列表
         【操作】
        # 111.特殊属性
        class A:
            pass

        class B:
            pass

        class C(B,A):

            def __init__(self,name):
                self.name = name

            def cc(self):
                print('cc')

        c = C(3)
        print('对象的所有属性、方法是:',dir(c))
        print('对象的属性字典是:',c.__dict__)
        print('对象所属的类是:',c.__class__)
        print('类的基类元组(多继承)是:',C.__bases__)
        print('类的基类是:',C.__base__)
        print('类层次结构是:',C.__mro__)
        print('父类的子类列表是:',A.__subclasses__())
        # 运行结果
        对象的所有属性、方法是: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'cc', 'name']
        对象的属性字典是: {'name': 3}
        对象所属的类是: <class '__main__.C'>
        类的基类元组(多继承)是: (<class '__main__.B'>, <class '__main__.A'>)
        类的基类是: <class '__main__.B'>
        类层次结构是: (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
        父类的子类列表是: [<class '__main__.C'>]

    61. 对象的浅拷贝和深拷贝
        61.1 变量的普通赋值:只是形成两个变量,实际上还是指向同一个对象
        61.2 浅拷贝:Python 拷贝一般是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象和拷贝对象会引用同一个子对象。
        61.3 深拷贝:使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象。源对象和拷贝对象所有的子对象也不同。
        【操作】
        # 112.对象的浅拷贝和深拷贝_内存分析
        import copy
        class MobilePhone:
            def __init__(self,cpu,screen):
                self.cpu = cpu
                self.screen = screen

        class CPU:
            def calculate(self):
                print('打印CPU模块的计算功能')
                print('cpu对象的地址:',self)

        class Screen:
            def show(self):
                print('打印Screen的显示功能')
                print('Screen对象的地址:',self)

        # 测试普通赋值
        print('测试普通赋值')
        c1 = CPU()
        c2 = c1
        print('c1的地址是:',c1)
        print('c2的地址是:',c2)
        # 测试浅拷贝 需要导入 copy 模块
        print('测试浅拷贝')
        s1 = Screen()
        m1 = MobilePhone(c1,s1)
        m2 = copy.copy(m1)
        print(m1,m1.cpu,m1.screen)
        print(m2,m2.cpu,m2.screen)
        # 测试深拷贝 需要导入 copy 模块
        print('测试深拷贝')
        s1 = Screen()
        m1 = MobilePhone(c1,s1)
        m2 = copy.deepcopy(m1)
        print(m1,m1.cpu,m1.screen)
        print(m2,m2.cpu,m2.screen)
        # 运行结果
        测试普通赋值
        c1的地址是: <__main__.CPU object at 0x000001B113C36FD0>
        c2的地址是: <__main__.CPU object at 0x000001B113C36FD0>

        测试浅拷贝
        <__main__.MobilePhone object at 0x000001B113C36A90> <__main__.CPU object at 0x000001B113C36FD0> <__main__.Screen object at 0x000001B113C36F70>
        <__main__.MobilePhone object at 0x000001B113C366A0> <__main__.CPU object at 0x000001B113C36FD0> <__main__.Screen object at 0x000001B113C36F70>

        测试深拷贝
        <__main__.MobilePhone object at 0x000001B113C36940> <__main__.CPU object at 0x000001B113C36FD0> <__main__.Screen object at 0x000001B113C36910>
        <__main__.MobilePhone object at 0x000001B113C365E0> <__main__.CPU object at 0x000001B113CC0490> <__main__.Screen object at 0x000001B113CC0520>
        '''
        从运行结果可以看出,
        1. 普通赋值,只是给新变量赋值同一个对象的地址,对象不拷贝
        2. 浅拷贝,看一下对应的地址,可以得出,浅拷贝只是复制了对象,但是对象的子对象不拷贝
        3. 深拷贝,看一下对应的地址,可以得出,深拷贝不仅复制了对象,也将对象的子对象拷贝了
        '''

    62. 组合
        'is-a' 关系,我们可以使用'继承',从而实现子类拥有父类的方法和属性。'is-a' 关系指的是类似这样的关系:狗是动物,dog is animal;狗类就应该继承动物类。

        'has - a' 关系,我们可以使用组合,也能实现一个类拥有另一个类的方法和属性。'has - a' 关系指的是这样的关系:手机拥有 CPU 。
        【继承】继承可以看我前面讲的,这里就不重复赘述了
        【操作】组合
        class MobilePhone:
            def __init__(self,cpu,screen):
                self.cpu = cpu
                self.screen = screen

        class CPU:
            def calculate(self):
                print('打印CPU模块的计算功能')
                print('cpu对象的地址:',self)

        class Screen:
            def show(self):
                print('打印Screen的显示功能')
                print('Screen对象的地址:',self)

        m = MobilePhone(CPU(),Screen())
        m.cpu.calculate()
        m.screen.show()
        # 运算结果
        打印CPU模块的计算功能
        cpu对象的地址: <__main__.CPU object at 0x00000287F8D42FD0>
        打印Screen的显示功能
        Screen对象的地址: <__main__.Screen object at 0x00000287F8D42FA0>

    63. 设计模式_工厂模式实现
          设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法,设计模式有很多种,
          比较流行的是:GOF(Goup Of Four)23 种设计模式。当然,我们没有必要全部学习,学习几个常用的即可。
          对于初学者,我们学习两个最常用的模式:工厂模式和单例模式。
          工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
            【操作】工厂模式
            class CarFactory:
                def createCar(self,brand):
                    if brand == "奔驰":
                        return Benz()
                    elif brand == "宝马":
                        return BMW()
                    elif brand == '比亚迪':
                        return BYD()
                    else:
                        return "未知品牌,无法创建
            # 这个是以前用的方式,一个一个写,不太方便
            class Benz:
                pass
            class BMW:
                pass
            class BYD:
                pass

            factory = CarFactory()
            c1 = factory.createCar("奔驰")
            c2 = factory.createCar("宝马")
            print(c1)
            print(c2)
            # 运行结果
            <__main__.Benz object at 0x000001BAAB286B80>
            <__main__.BMW object at 0x000001BAAB286B50>

    64. 设计模式_单例模式实现
            单例模式的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。
            单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,
        可以产生一个"单例对象",然后永久驻留内存中,从而极大的降低开销。
        '''【一个类只有一个实例】'''
        '''【提供一个访问该实例的全局访问点】'''
        '''【减少了对系统资源的开销】'''
        创建一次,初始化的时候也初始化一次
        【操作】
        # 115.设计模式_单例模式实现
        class MySingleton:
            __obj = None
            __init_flag = True

            def __new__(cls, *args, **kwargs):
                if cls.__obj == None:
                    cls.__obj = object.__new__(cls)

                return cls.__obj

            def __init__(self,name):
                if MySingleton.__init_flag:
                    print('__init__//////')
                    self.name = name
                    MySingleton.__init_flag = False

        a = MySingleton('aa')
        b = MySingleton('bb')
        print('*'*30)
        print(a)
        print(b)
        c = MySingleton('cc')
        print(c)
        # 执行结果,可见,三个对象的地址时一样的,都是指向 0x000001F8ADAB7FA0 ,遵循 【一个类只有一个实例】原则
        __init__//////
        ******************************
        <__main__.MySingleton object at 0x000001F8ADAB7FA0>
        <__main__.MySingleton object at 0x000001F8ADAB7FA0>
        <__main__.MySingleton object at 0x000001F8ADAB7FA0>

第二章 异常

    1. 导引问题
            在实际工作中,我们遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求;你的程序要打开某个文件,
       这个文件可能不存在或者文件格式不对;你要读取数据库的数据,数据可能是空的;我们的程序在运行着,但是内存或硬盘可能满了等等。
            软件程序在运行过程中,非常可能遇到刚刚提到的这些问题,我们称之为"异常",英文是:Exception,意思是例外。
       遇到这些例外情况,或者叫异常,我们怎么让写的程序做出合理的处理,而不至于程序崩溃呢?
            如果我们要拷贝一个文件,在没有异常机制的情况下,我们需要考虑各种
       异常情况,伪代码如下:
       【示例 6-1】伪代码使用 if 处理程序中可能出现的各种情况
        #将 d:/a.txt 拷贝到 e:盘
        if "d:/a.txt"这个文件存在:
            if e 盘的空间大于 a.txt 文件长度:
                if 文件复制一半 IO 流断掉:
                    停止 copy,输出:IO 流出问题!
                else:
                    copyFile("d:/a.txt","e:/a.txt")
            else:
                print("e 盘空间不够存放 a.txt!"
        else:
            print("a.txt 不存在!")
        这种方式,有两个坏处:
        1. 逻辑代码和错误处理代码放一起!
        2. 程序员本身需要考虑的例外情况较复杂,对程序员本身要求较高!
        那么,我们如何解决应对异常情况呢? python 的异常机制给我们提供了方便的处理方式。
        如上情况,如果是用 python 的异常机制来处理,示意代码如下
        (仅限示意,不能运行)
        #将 d:/a.txt 拷贝到 e:盘
        try:
            copyFile("d:/a.txt","e:/a.txt")
        except:
            print("文件无法拷贝")

        异常机制本质
        异常指程序运行过程中出现的非正常现象,例如用户输入错误、除数为零、需要处理的文件不存在、数组下标越界等。
        所谓异常处理,就是指程序在出现问题时依然可以正确的执行剩余的程序,而不会因为异常而终止程序执行。
        python 中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。
        下面较为完整的展示了 python 中内建异常类的继承层次:
        一级异常父类:BaseException (所有异常的父类,或者说根类)
        二级异常父类:KeyboardInterrupt、Exception、SystemExit、GeneratorExit
        三级异常父类:NameError、ValueError、AttributeError 等等

        【操作】查询 BaseException 的父类
        print('BaseException类层次结构是:',BaseException.__mro__)
        # 运行结果
        BaseException类层次结构是: (<class 'BaseException'>, <class 'object'>)

        # NameError、ValueError、AttributeError
        print('NameError类层次结构是:',NameError.__mro__)
        print('ValueError类层次结构是:',ValueError.__mro__)
        print('AttributeError类层次结构是:',AttributeError.__mro__)
        # 运行结果
        NameError类层次结构是: (<class 'NameError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
        ValueError类层次结构是: (<class 'ValueError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)
        AttributeError类层次结构是: (<class 'AttributeError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)

        python 中一切都是对象,异常也采用对象的方式来处理。
        处理过程:
            1. 抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给解释器。
            2. 捕获异常:解释器得到该异常后,寻找相应的代码来处理该异常

    2. try...一个 except 结构
        try...except 是最常见的异常处理结构。结构如下:

        try:
            被监控的可能引发异常的语句块
        except BaseException [as e]:
            异常处理语句块
        #
        try 块包含着可能引发异常的代码,except 块则用来捕捉和处理发生的异常。
        执行的时候,如果 try 块中没有引发异常,则跳过 except 块继续执行后续代码;
        执行的时候,如果 try 块中发生了异常,则跳过 try 块中的后续代码,跳到相应的 except 块中处理异常;【发生异常,跳过后续代码块,跳到 except 块中处理异常】
        异常处理完后,继续执行后续代码
        #
        【操作】# try...一个 except 结构
        while True:
            try:
                x = int(input('请输入一个数字:'))
                if x == 88:
                    print('输入正确,退出程序!')
                    break
                else:
                    print('输入错误。')
            except BaseException as e:
                print('输入错误:',e,',类型是',type(e),',请重新输入。')

        print('测试结束!')

    3. try...多个 except 结构
        上面的结构可以捕获所有的异常,工作中也很常见。
        但是,从经典理论考虑,一般建议尽量捕获可能出现的多个异常(按照先子类后父类的顺序),
        并且针对性的写出异常处理代码。为了避免遗漏可能出现的异常,可以在最后增加 BaseException ,BaseException 是所有异常处理的父类。
        结构如下:
        try:
            被监控的、可能引发异常的语句块
        except Exception1:
            处理 Exception1 的语句块
        except Exception2:
            处理 Exception2 的语句块
        ...
        except BaseException:
            处理可能遗漏的异常的语句块

        【示例】多个 except 结
        try:
            a = input("请输入被除数:")
            b = input("请输入除数:")
            c = float(a)/float(b)
            print(c)
        except ZeroDivisionError:
            print("异常:除数不能为 0")
        except TypeError:
            print("异常:除数和被除数都应该为数值类型")
        except NameError:
            print("异常:变量不存在")
        except BaseException as e:
            print(e)
            print(type(e))
        #执行结果:
        请输入被除数:10
        请输入除数:0
        异常:除数不能为 0

    4. try...except...else 结构
        try...except...else 结构增加了“else 块”。如果 try 块中没有抛出异常,则执行 else 块。
        如果 try 块中抛出异常,则执行 except 块,不执行 else 块。
        【示例】try...except...else 结构执行测
        try:
            a = input("请输入被除数:")
            b = input("请输入除数:")
            c = float(a)/float(b)
        except BaseException as e:
            print(e)
        else:
            print("除的结果是:",c)
        #执行结果
        #发生异常的执行情况(执行 except 块,没有执行 else):
        请输入被除数:5
        请输入除数:0
        float division by zero
        #没有发生异常的执行情况(执行完 try 块后,执行 else)
        请输入被除数:10
        请输入除数:5
        除的结果是: 2.0

    5. try...except...finally 结构
        try...except...finally 结构中,finally 块无论是否发生异常都会被执行;
        通常用来释放 try 块中申请的资源。
        【示例】try...except...finally 结构简单测
        try:
            a = input("请输入一个被除数:")
            b = input("请输入一个除数:")
            c = float(a)/float(b)
        except BaseException as e:
            print(e)
        else:
            print(c)
        finally:
            print("我是 finally 中的语句,无论发生异常与否,都执行!")
        print("程序结束!")
        # 执行结果如下:
        请输入被除数:10
        请输入除数:0
        float division by zero
        我是 finally 中的语句,无论是否发生异常都执行

        【示例】读取文件,finally 中保证关闭文件资源
        try:
            f = open("d:/a.txt",'r')# 这里需要注意的是:如果找不到 d:/a.txt,这个文件,参数 f 是不会被建立的。
            content = f.readline()
            print(content)
        except:
            print('文件未找到')
        finally:
            print('run is finally。关闭资源')
            try:
                f.close() #释放资源。此处也可能会发生异常。若发生异常,则程序终止,不会继续往下执行
            except BaseException as e:
                print(e)
        print("程序执行结束!")

    6. return 语句和异常处理问题
        由于 return 有两种作用:结束方法运行、返回值。
        我们一般不把 return 放到异常处理结构中,而是放到方法最后。
        '''
        尽量不要把 return 放到异常处理结构中,
        而是放到方法的最后面
        '''

    7. 常见异常的解决
        Python 中的异常都派生自 BaseException 类,本节我们测试和列出常见的一些异常,方便初学者掌握。
        【常见异常汇总】可以的话,尽量背一下,
       #异常名称                          说明
        ArithmeticError                   所有数值计算错误的基类
        AssertionError                    断言语句失败
        AttributeError                    对象没有这个属性
        BaseException                     所有异常的基类
        DeprecationWarning                关于被弃用的特征的警告
        EnvironmentError                  操作系统错误的基类
        EOFError                          没有内建输入,到达EOF标记
        Exception                         常规错误的基类
        FloatingPointError                浮点计算错误
        FutureWarning                     关于构造将来语义会有改变的警告
        GeneratorExit                     生成器(generator)发生异常来通知退出
        ImportError                       导入模块/对象失败
        IndentationError                  缩进错误
        IndexError                        序列中没有此索引(index)
        IOError                           输入/输出操作失败
        KeyboardInterrupt                 用户中断执行(通常是输入^C)
        KeyError                          映射中没有这个键
        LookupError                       无效数据查询的基类
        MemoryError                       内存溢出错误(对于Python解释器不是致命的)
        NameError                         未声明/初始化对象(没有属性)
        NotImplementedError               尚未实现的方法
        OSError                           操作系统错误
        OverflowError                     数值运算超出最大限制
        OverflowWarning                   旧的关于自动提升为长整型(long)的警告
        PendingDeprecationWarning         关于特性将会被废弃的警告
        ReferenceError                    弱引用(Weakreference)试图访问已经垃圾回收了的对象
        RuntimeError                      一般的运行时错误
        RuntimeWarning                    可疑的运行时行为(runtimebehavior)的警告
        StandardError                     所有的内建标准异常的基类
        StopIteration                     迭代器没有更多的值
        SyntaxError                       Python语法错误
        SyntaxWarning                     可疑的语法的警告
        SystemError                       一般的解释器系统错误
        SystemExit                        解释器请求退出
        TabError                          Tab和空格混用
        TypeError                         对类型无效的操作
        UnboundLocalError                 访问未初始化的本地变量
        UnicodeDecodeError                Unicode解码时的错误
        UnicodeEncodeError                Unicode编码时错误
        UnicodeError                      Unicode相关的错误
        UnicodeTranslateError             Unicode转换时错误
        UserWarning                       用户代码生成的警告
        ValueError                        传入无效的参数
        Warning                           警告的基类
        WindowsError                      系统调用失败
        ZeroDivisionError                 除(或取模)零(所有数据类型)

    8. with 上下文管理
        finally 块由于是否发生异常都会执行,通常我们放释放资源的代码。其实,我们可以通过 with 上下文管理,更方便的实现释放资源的操作。
        with 上下文管理的语法结构如下:
            with context_expr [ as var]:
                语句块
        with 上下文管理可以自动管理资源,在 with 代码块执行完毕后自动还原进入该代码之前的现场或上下文。
        不论何种原因跳出 with 块,不论是否有异常,总能保证资源正常释放。极大的简化了工作,在文件操作、网络通信相关的场合非常常用
        【示例】with 上下文管理文件操作
        with open("d:/bb.txt") as f:
            for line in f:
                print(line)
        #执行结果:
        gaoqi
        sxt
        baizhan

    9. trackback 模块
        【示例】使用 Traceback 模块打印异常信息
        #coding=utf-8
        import traceback

        try:
            print("step1")
            num = 1/0
        except:
            traceback.print_exc()

        【示例】使用 traceback 将异常信息写入日志文件
        #coding=utf-8
        import traceback
        try:
            print("step1")
            num = 1/0
        except:
            with open("d:/a.log","a") as f:
                traceback.print_exc(file=f)

    10. 自定义异常类
        程序开发中,有时候我们也需要自己定义异常类。
        自定义异常类一般都是运行时异常,通常继承 Exception 或其子类即可。命名一般以 Error、Exception 为后缀。
        自定义异常由 raise 语句主动抛出。
        【示例】自定义异常类和 raise 语句
        #coding=utf-8
        #测试自定义异常类
        class AgeError(Exception):
            def __init__(self,errorinfo):
                Exception.__init__(self)
                self.errorinfo = errorinfo
                print('Exception.__init__(self) / self.errorinfo 被调用')

            def __str__(self):
                return str(self.errorinfo)+',年龄错误!正确年龄应该在1 - 150 之间'
        ############测试代码################
        # a = AgeError(20)
        # print(a)
        if __name__ == '__main__': #如果为 True,则模块是作为独立文件运行,可以执行测试代码 # 可以看作恒成立 1 = 1,永为真
            print('结果为真')
            age = int(input('请输入一个年龄:'))
            if age<1 or age>150:
                raise AgeError(age)
            else:
                print('输入的年龄正确,为:',age)
        else:
            print('结果为假')
        # 执行结果如下:
        结果为真
        请输入一个年龄:200
        Exception.__init__(self) / self.errorinfo 被调用
        Traceback (most recent call last):
          File "D:\pycharm_code\第二章\09_自定义异常类_raise抛出异常.py", line 16, in <module>
            raise AgeError(age)
        __main__.AgeError: 200,年龄错误!正确年龄应该在1 - 150 之间

    11. Pycharm 开发环境的调试
        进行调试的核心是设置断点。程序执行到断点时,暂时挂起,停止执行。就像看视频按下停止一样,我们可以详细的观看停止处的每一个细节。

        11.1 断点
             程序运行到此处,暂时挂起,停止执行。我们可以详细在此时观察程序的运行情况,方便做出进一步的判断。
             1. 设置断点:
                (1) 在行号后面单击即可增加断点
                (2) 在断点上再单击即可取消断点
             2. 进入调试视图
                我们通过如下三种方式都可以进入调试视图:
                (1) 单击工具栏上的按钮:【一个类似甲壳虫的按钮】
                (2) 右键单击编辑区,点击:debug ‘模块名’
                (3) 快捷键:shift+F9
                进入调试视图后,布局如下:
                    左侧为“浏览帧”:
                    调试器列出断点处,当前线程正在运行的方法,每个方法对应一个“栈帧”。最上面的是当前断点所处的方法。
                    变量值观察区:
                    调试器列出了断点处所在方法相关的变量值。我们可以通过它,查看变量的值的变化。也可以通过 ,增加要观察的变量。
             3. 调试操作区
                我们通过上图中的按钮进行调试操作,它们的含义如下:
               |-----------------------------------------------------------------------------------------------------|
               |中文名称         |  英文名称             |  图标和快捷键   |    说明                                 |
               |-----------------|-----------------------|-----------------|-----------------------------------------|
               |显示当前所有断点 |  show ExecutionPoint  |  Alt+F10        |                                         |
               |-----------------|-----------------------|-----------------|-----------------------------------------|
               |单步调试:       |  step over            |  F8             |    若当前执行的是一个函数,则会把这个   |
               |遇到函数跳过     |                       |                 |    函数当做整体一步执行完。不会进入这   |
               |                 |                       |                 |    个函数内部                           |
               |-----------------|-----------------------|-----------------|-----------------------------------------|
               |单步调试:       |   step into           |  F7             |    若当前执行的是一个函数,则会进入这   |
               |遇到函数进入     |                       |                 |    个函数内部                           |
               |-----------------|-----------------------|-----------------|-----------------------------------------|
               |跳出函数         |   step out            |  Shift+F8       |    当单步执行到子函数内时,用 step out  |
               |                 |                       |                 |    就可以执行完子函数余下部分,并返回   |
               |                 |                       |                 |    到上一层函数                         |
               |-----------------|-----------------------|-----------------|-----------------------------------------|
               |执行的光标处     |   run to cursor       |  Alt+F9         |    一直执行,到光标处停止,用在循环内   |
               |                 |                       |                 |    部时,点击一次就执行一个循           |
               |-----------------------------------------------------------------------------------------------------|
全部评论

相关推荐

06-07 17:17
嘉兴学院 教师
心爱的idea:你孩
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
05-28 12:15
点赞 评论 收藏
分享
07-02 18:09
门头沟学院 Java
苍穹外卖和谷粒商城这俩是不是烂大街了,还能做吗?
想去重庆的鸽子在吐槽:你不如把这俩做完自己搞明白再优化点再来问 何必贩卖焦虑
点赞 评论 收藏
分享
评论
1
16
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务