• python基础


    /******对象******/

    -python面向对象的语言
    -一切皆对象
    -程序运行当中,所有的数据都是存储到内存当中然后在运行
    -对象就是内存中专门从来存储数据的一块区域
    -对象实际上就是一个容器,专门用来存储数据      
    -像数值、字符串、布尔值、None都是对象
    对象的结构
    - 每个对象中都要保存三种数据
    - id(标识)
    > id用来标识对象的唯一性,每一个对象都有唯一的id
    > 对象的id就相当于人的身份证号一样
    > 可以通过id()函数来查看对象的id
    > id是由解析器生成的,在CPython中,id就是对象的内存地址
    > 对象一旦创建,则它的id永远不能再改变

    - type(类型)
    > 类型用来标识当前对象所属的类型
    > 比如:int str float bool 。。。
    > 类型决定了对象有哪些功能
    > 通过type()函数来查看对象的类型
    > Python是一门强类型的语言,对象一旦创建类型便不能修改


    - value(值)
    > 值就是对象中存储的具体的数据
    > 对于有些对象值是可以改变的
    > 对象分成两大类,可变对象 不可变对象
    可变对象的值可以改变
    不可变对象的值不能改变,之前学习的对象都是不可变对象

    变量和对象
    - 对象并没有直接存储到变量中,在Python中变量更像是给对象起了一个别名
    - 变量中存储的不是对象的值,而是对象的id(内存地址),
    当我们使用变量时,实际上就是在通过对象id在查找对象
    - 变量中保存的对象,只有在为变量重新赋值时才会改变
    - 变量和变量之间是相互独立的,修改一个变量不会影响另一个变量

    类型转换
    - 所谓的类型转换,将一个类型的对象转换为其他对象
    - 类型转换不是改变对象本身的类型,而是根据当前对象的值创建一个新对象

    is比较两个对象的id
    ==比较两个对象的值

    /******条件控制******/

    编写一个程序,获取一个用户输入的整数。然后通过程序显示这个数是奇数还是偶数
    num=int(input('请输入一个整数:'))
    if num%2==0:
        print(num,'这是一个偶数')
    else:
        print(num,'这是一个奇数')
    编写一个程序,检查任意一个年份是否是闰年。
    如果一个年份可以被4整除不能被100整除,或者可以被400整除,这个年份就是闰年
    year=int(input('请输入一个年份:'))
    if year%4==0 and year%100!=0 or year%400==0:
        print(year,'这一年是闰年')
    else:
        print(year,'这一年是平年')
    编写一个程序,获取用户输入的狗的年龄,然后通过程序显示其相当于人类的年龄。
    如果用户输入负数,请显示一个提示信息
    狗的前两年每一年相当于人类的10.5岁,然后每增加一年就增加四岁。
    那么5岁的狗相等于人类的年龄就应该是10.5+10.5+4+4+4 = 33岁
    dog_age=float(input('请输入狗的年龄:'))
    like_person_age=0
    if dog_age<0:
        print('你的输入不合法')
    if dog_age>0:
        if dog_age<=2:
            like_person_age=dog_age*10.5
        else:
            like_person_age=2*10.5
            like_person_age+=(dog_age-2)*4
    
        print(dog_age,'岁的狗年纪相当于',like_person_age,'岁的人')
    输入小明的期末成绩:
    当成绩为100时,'奖励一辆BMW'
    当成绩为[80-99]时,'奖励一台iphone'
    当成绩为[60-79]时,'奖励一本参考书'
    其他时,什么奖励也没有
    res=int(input('请输入成绩:'))
    if 0<=res<=100:
        if res==100:
            print('奖励一辆BMW')
        elif 80<=res<=99:
            print('奖励一台iPhone')
        elif 60<=res<=79:
            print('奖励一本参考书')
        else:
            print('什么奖励也没有')
    else:
        print('输入不合法')
    女方家长要嫁女儿,当然要提出一定的条件:
    高:180cm以上; 富:1000万以上; 帅:500以上;
    如果这三个条件同时满足,则:'我一定要嫁给他'
    如果三个条件有为真的情况,则:'嫁吧,比上不足,比下有余。'
    如果三个条件都不满足,则:'不嫁!'
    sg=180
    cf=1000
    yz=500
    g=int(input('请输入身高(厘米):'))
    f=int(input('请输入财富(万):'))
    s=int(input('请输入颜值:'))
    if g>=sg and f>=cf and s>=yz:
        print('我一定要嫁给他')
    elif g>=sg or f>=cf or s>=yz:
        print('嫁吧,比上不足,比下有余')
    else:
        print('不嫁')

    /******while循环******/

    while语句在执行时,先对while后的条件表达式进行求值判断
    如果判断为True,则执行循环体(代码块),执行完毕继续判断,直到结果为False,循环中止。
    循环的三个条件:
    初始化表达式,
    条件表达式
    更新表达式
    i=0
    #初始化表达式
    while i<10:
    #条件表达式
        i+=1
        #更新表达式
        print(i,'hello')
    求100以内所有的奇数之和
    方法一:
    i=0
    sum=0
    #创建一个变量保存结果
    while i<100:
        i+=1
        if i%2!=0:
            sum+=i
    print(sum)
    方法二:
    i=1
    sum=0
    while i<100:
        sum+=i
        i+=2
    print(sum)
    求100以内所有7的倍数之和,以及个数
    i=0
    sum=0
    while i<100:
        i+=1
        if i%7==0:
            sum+=i
    print(sum)
    
    i=7
    sum=0
    count=0
    while i<100:
        count+=1
        sum+=i
        i+=7
    print('总和为:',sum,   '总次数为:',count)
    水仙花数是指一个 n 位数(n≥3 ),它的每个位上的数字的 n 次幂之和等于它本身(例如:1**3 + 5**3 + 3**3 = 153)。
    求1000以内所有的水仙花数
    i=100
    while i<1000:
        a=i//100
        b=i//10%10
        # b=(i-a*100)//10
        c=i%10
        if a**3+b**3+c**3==i:
            print(i)
        i+=1
    获取用户输入的任意数,判断其是否是质数。质数是只能被1和它自身整除的数,1不是质数也不是合数
    num=int(input('请输入一个任意的大于1的整数:'))
    i=2
    flag=True
    #创建一个变量用来记录num是否为质数,默认为质数
    while i <num:
        if num %i==0:
            flag=False
        i+=1
    
    if flag:
        print(num,'是质数')
    else:
        print(num,'不是质数')
    规则:玩家掷两个骰子,每个骰子点数为1-6,如果第一次点数和为7或11,则玩家胜;如果点数和为2、3或12,则玩家输庄家胜。若和为其他点数,则记录第一次的点数和,玩家继续掷骰子,直至点数和等于第一次掷出的点数和则玩家胜;若掷出的点数和为7则庄家胜。
    
    from random import randint
    
    i = randint(1, 6)
    j = randint(1, 6)
    oth = [4, 5, 6, 8, 9, 10]
    sum=i+j
    if sum in (7,11):
        print('点数是%d,%d  you win' % (i, j))
    elif sum in (2,3,12):
        print('点数是%d,%d  you fall' % (i, j))
    elif i + j in oth:
        f = i + j
        while True:
            print('第一次投掷点数%d'%f)
            m = randint(1, 6)
            n = randint(1, 6)
            print('继续投掷%d'%(m+n))
            if m+n==f:
                print('you win  点数是%d %d' % (i, j))
                break
            elif m+n==7:
                print('you fall 点数是%d %d'%(i,j))
                break

    /******循环嵌套******/

     创建一个循环来控制图形的高度

    循环嵌套时,外层循环每执行一次,内层循环执行一圈

    * * * * *
    * * * * *
    * * * * *
    * * * * *
    * * * * *
    i=0
    while i <5:
        #创建一个内层循环来控制图形的宽度
        j=0
        while j<5:
            print('* ',end='')
            j+=1
        print()
        i+=1
    * * * * * 
    * * * * 
    * * * 
    * * 
    * 
    i=0
    while i <5:
        #创建一个内层循环来控制图形的宽度
        j=0
        while j<5-i:
            print('* ',end='')
            j+=1
        print()
        i+=1
    * 
    * * 
    * * * 
    * * * * 
    * * * * * 
    i=0
    while i <5:
        #创建一个内层循环来控制图形的宽度
        j=0
        while j<i+1:
            print('* ',end='')
            j+=1
        print()
        i+=1
    九九乘法表
    i=1
    while i<10:
        j=1
        while j<i+1:
            print(str(j)+'*'+str(i)+'='+str(i*j),end=' ')
            j+=1
        print()
        i+=1
    求100以内的所有质数
    i=2
    while i<=100:
        #创建一个变量,记录i的状态,默认为i是质数
        flag=True
        # 判断i是否是质数
        #获取所有可能成为i的因数的数
        j=2
        while j<i:
            #判断i能否被i整除
            if i %j==0:
                #i能被j整除,证明i不是质数,修改flag为False
                flag=False
            j+=1
            #验证并输出结果
        if flag:
            print(i)
        i+=1
    质数练习优化
    from time import *
    #优化前:15.3s
    #第一次优化:1.6s
    #第三次优化:0.05s
    begin=time()
    i=2
    while i<=10000:
        flag=True
        j=2
        while j<=i**0.5:#第二次优化
            if i %j==0:
                flag=False
                #一旦进入判断,则证明i一定不是质数,此时内层循环没有继续执行的必要
                break#第一次优化
            j+=1
        if flag:
            pass
            # print(i)
        i+=1
    
    end=time()
    print(end-begin)
    break可以用来立即退出循环语句(包括else)
    continu可以用来跳过当次循环
    break和continue都是只对离他最近的循环起作用

    /******练习1******/

    import time
    
    print('=================================欢迎来到唐僧大战白骨精=================================')
    role = input('身份选择
    '
                 '1.唐僧
    '
                 '2.白骨精
    '
                 '请选择:')
    
    if role == '1':
        print('你好,唐僧')
    elif role == '2':
        print('太不要脸了,系统自动给你分配为唐僧')
    else:
        print('你好,你是唐僧')
    
    player_att = 2
    player_life = 2
    
    boss_life = 10
    boss_att = 10
    print('*' * 50)
    print('唐僧,你的生命值是 %s你的攻击力是 %s' % (player_life, player_att))
    print('*' * 50)
    
    time.sleep(1)
    while True:
        print('=' * 50)
        print('请选择操作:')
        print('1.练级')
        print('2.打boss')
        print('3.逃跑')
        chose = input('请选择你要进行的操作:')
    
        if chose == '1':
            player_life += 2
            player_att += 2
            print('*' * 50)
            print('唐僧,恭喜你升级了,你的生命值是%s你的攻击力是%s' % (player_life, player_att))
        elif chose == '2':
            boss_life -= player_att
            print('-' * 50)
            print('唐僧攻击白骨精')
            if boss_life <= 0:
                print('白骨精受到了%s点伤害,挂了,唐僧胜利 ' % player_att)
                break
    
            player_life -= boss_att
            print('-' * 50)
            print('白骨精攻击唐僧')
            if player_life <= 0:
                print('唐僧受到了%s点伤害,游戏结束 ' % boss_att)
                break
    
        elif chose == '3':
            print('唐僧跑了,游戏结束')
            break
    
        else:
            print('你的输入有误,请重新输入')
    练习2
    
    
    猜数字游戏
    计算机出一个1~100之间的随机数由人来猜
    计算机根据人猜的数字分别给出提示大一点/小一点/猜对了
    import random
    
    answer = random.randint(1, 100)
    counter = 0
    while True:
        counter += 1
        number = int(input('请输入: '))
        if number < answer:
            print('大一点')
        elif number > answer:
            print('小一点')
        else:
            print('恭喜你猜对了!')
            break
    print('你总共猜了%d次' % counter)
    if counter > 7:
        print('你的智商余额明显不足')

     /******数据结构******/

    #字符串

    def main():
        str1 = 'hello, world!'
        # 通过len函数计算字符串的长度
        print(len(str1))  # 13
        # 获得字符串首字母大写的拷贝
        print(str1.capitalize())  # Hello, world!
        # 获得字符串变大写后的拷贝
        print(str1.upper())  # HELLO, WORLD!
        # 从字符串中查找子串所在位置
        print(str1.find('or'))  # 8
        print(str1.find('shit'))  # -1
        # 与find类似但找不到子串时会引发异常
        # print(str1.index('or'))
        # print(str1.index('shit'))
        # 检查字符串是否以指定的字符串开头
        print(str1.startswith('He'))  # False
        print(str1.startswith('hel'))  # True
        # 检查字符串是否以指定的字符串结尾
        print(str1.endswith('!'))  # True
        # 将字符串以指定的宽度居中并在两侧填充指定的字符
        print(str1.center(50, '*'))
        # 将字符串以指定的宽度靠右放置左侧填充指定的字符
        print(str1.rjust(50, ' '))
        str2 = 'abc123456'
        # 从字符串中取出指定位置的字符(下标运算)
        print(str2[2])  # c
        # 字符串切片(从指定的开始索引到指定的结束索引)
        print(str2[2:5])  # c12
        print(str2[2:])  # c123456
        print(str2[2::2])  # c246
        print(str2[::2])  # ac246
        print(str2[::-1])  # 654321cba
        print(str2[-3:-1])  # 45
        # 检查字符串是否由数字构成
        print(str2.isdigit())  # False
        # 检查字符串是否以字母构成
        print(str2.isalpha())  # False
        # 检查字符串是否以数字和字母构成
        print(str2.isalnum())  # True
        str3 = '  jackfrued@126.com '
        print(str3)
        # 获得字符串修剪左右两侧空格的拷贝
        print(str3.strip())
    
    
    if __name__ == '__main__':
        main()

    #列表

    - 列表是Python中的一个对象
    - 对象(object)就是内存中专门用来存储数据的一块区域
    - 之前我们学习的对象,像数值,它只能保存一个单一的数据
    - 列表中可以保存多个有序的数据
    - 列表是用来存储对象的对象
    - 列表的使用:
      1.列表的创建
      2.操作列表中的数据

    def main():
        list1 = [1, 3, 5, 7, 100]
        print(list1)
        list2 = ['hello'] * 5
        print(list2)
        # 计算列表长度(元素个数)
        print(len(list1))
        # 下标(索引)运算
        print(list1[0])
        print(list1[4])
        # print(list1[5])  # IndexError: list index out of range
        print(list1[-1])
        print(list1[-3])
        list1[2] = 300
        print(list1)
        # 添加元素
        list1.append(200)
        list1.insert(1, 400)
        list1 += [1000, 2000]
        print(list1)
        print(len(list1))
        # 删除元素
        list1.remove(3)
        if 1234 in list1:
            list1.remove(1234)
        del list1[0]
        print(list1)
        # 清空列表元素
        list1.clear()
        print(list1)
    
    
    if __name__ == '__main__':
        main()
    def main():
        fruits = ['grape', 'apple', 'strawberry', 'waxberry']
        fruits += ['pitaya', 'pear', 'mango']
        # 循环遍历列表元素
        for fruit in fruits:
            print(fruit.title(), end=' ')
        print()
        # 列表切片
        fruits2 = fruits[1:4]
        print(fruits2)
        # fruit3 = fruits  # 没有复制列表只创建了新的引用
        # 可以通过完整切片操作来复制列表
        fruits3 = fruits[:]
        print(fruits3)
        fruits4 = fruits[-3:-1]
        print(fruits4)
        # 可以通过反向切片操作来获得倒转后的列表的拷贝
        fruits5 = fruits[::-1]
        print(fruits5)
    
    
    if __name__ == '__main__':
        main()
    import sys
    
    
    def main():
        f = [x for x in range(1, 10)]
        print(f)
        f = [x + y for x in 'ABCDE' for y in '1234567']
        print(f)
        # 用列表的生成表达式语法创建列表容器
        # 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
        f = [x ** 2 for x in range(1, 1000)]
        print(sys.getsizeof(f))  # 查看对象占用内存的字节数
        print(f)
        # 请注意下面的代码创建的不是一个列表而是一个生成器对象
        # 通过生成器可以获取到数据但它不占用额外的空间存储数据
        # 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
        f = (x ** 2 for x in range(1, 1000))
        print(sys.getsizeof(f))  # 相比生成式生成器不占用存储数据的空间
        print(f)
        for val in f:
            print(val)
    
    
    if __name__ == '__main__':
        main()

    #序列

    序列是Python中最基本的一种数据结构
    - 数据结构指计算机中数据存储的方式
    - 序列用于保存一组有序的数据,所有的数据在序列当中都有一个唯一的位置(索引)
    并且序列中的数据会按照添加的顺序来分配索引
    - 序列的分类:
      可变序列(序列中的元素可以改变):
        > 列表(list)
    不可变序列(序列中的元素不能改变):
        > 字符串(str)
        > 元组(tuple)

    #字典 

    字典属于一种新的数据结构,称为映射(mapping)
    - 字典的作用和列表类似,都是用来存储对象的容器
    - 列表存储数据的性能很好,但是查询数据的性能的很差
    - 在字典中每一个元素都有一个唯一的名字,通过这个唯一的名字可以快速的查找到指定的元素
    - 在查询元素时,字典的效率是非常快的
    - 在字典中可以保存多个对象,每个对象都会有一个唯一的名字
      这个唯一的名字,我们称其为键(key),通过key可以快速的查询value
      这个对象,我们称其为值(value)
      所以字典,我们也称为叫做键值对(key-value)结构
      每个字典中都可以有多个键值对,而每一个键值对我们称其为一项(item)

    copy()方法用于对字典进行浅复制,复制以后的对象和原对象是相互独立的

    浅复制会简单复制对象内部的值,如果值也是一个可变对象,这个可变对象不会被复制

    深拷贝,完全拷贝,完全独立于原对象

    ---遍历字典

    keys() 该方法返回字典的所有key,返回一个序列,序列中保存字典的所有键

    values()该方法返回字典的所有value,返回一个序列,序列中保存字典的所有值

    items()该方法返回字典的所有项,返回一个序列,序列中包含所有双值子序列,双值分别是字典中的key和value

    def main():
        scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
        # 通过键可以获取字典中对应的值
        print(scores['骆昊'])
        print(scores['狄仁杰'])
        # 对字典进行遍历(遍历的其实是键再通过键取对应的值)
        for elem in scores:
            print('%s	--->	%d' % (elem, scores[elem]))
        # 更新字典中的元素
        scores['白元芳'] = 65
        scores['诸葛王朗'] = 71
        scores.update(冷面=67, 方启鹤=85)
        print(scores)
        if '武则天' in scores:
            print(scores['武则天'])
        print(scores.get('武则天'))
        # get方法也是通过键获取对应的值但是可以设置默认值
        print(scores.get('武则天', 60))
        # 删除字典中的元素
        print(scores.popitem())
        print(scores.popitem())
        print(scores.pop('骆昊', 100))
        # 清空字典
        scores.clear()
        print(scores)
    
    
    if __name__ == '__main__':
        main()

    集合(set)
    - 集合和列表非常相似
    - 不同点:
      1.集合中只能存储不可变对象
      2.集合中存储的对象是无序(不是按照元素的插入顺序保存)
      3.集合中不能出现重复的元素

     

    def main():
        set1 = {1, 2, 3, 3, 3, 2}
        print(set1)
        print('Length =', len(set1))
        set2 = set(range(1, 10))
        print(set2)
        set1.add(4)
        set1.add(5)
        set2.update([11, 12])
        print(set1)
        print(set2)
        set2.discard(5)
        # remove的元素如果不存在会引发KeyError
        if 4 in set2:
            set2.remove(4)
        print(set2)
        # 遍历集合容器
        for elem in set2:
            print(elem ** 2, end=' ')
        print()
        # 将元组转换成集合
        set3 = set((1, 2, 3, 3, 2, 1))
        print(set3.pop())
        print(set3)
        # 集合的交集、并集、差集、对称差运算
        print(set1 & set2)
        # print(set1.intersection(set2))
        print(set1 | set2)
        # print(set1.union(set2))
        print(set1 - set2)
        # print(set1.difference(set2))
        print(set1 ^ set2)
        # print(set1.symmetric_difference(set2))
        # 判断子集和超集
        print(set2 <= set1)
        # print(set2.issubset(set1))
        print(set3 <= set1)
        # print(set3.issubset(set1))
        print(set1 >= set2)
        # print(set1.issuperset(set2))
        print(set1 >= set3)
        # print(set1.issuperset(set3))
    
    
    if __name__ == '__main__':
        main()

    #可变对象
    - 每个对象中都保存了三个数据:
      id(标识)
      type(类型)
      value(值)

     ==和is

     ==和!=比较的是对象的value是否相等

    is和is not比较的是对象的id是否相等(比较两个对象是否为同一个对象)

     

    /******函数******/

    函数简介(function)

      - 函数也是一个对象
      - 对象是内存中专门用来存储数据的一块区域
      - 函数可以用来保存一些可执行的代码,并且可以在需要时,对这些语句进行多次的调用
      - 创建函数:
        def 函数名([形参1,形参2,...形参n]) :
          代码块
        - 函数名必须要符号标识符的规范
         (可以包含字母、数字、下划线、但是不能以数字开头)
      - 函数中保存的代码不会立即执行,需要调用函数代码才会执行
      - 调用函数:
        函数对象()
      - 定义函数一般都是要实现某种功能的

    函数的参数
      - 在定义函数时,可以在函数名后的()中定义数量不等的形参,
        多个形参之间使用,隔开
      - 形参(形式参数),定义形参就相当于在函数内部声明了变量,但是并不赋值
      - 实参(实际参数)
        - 如果函数定义时,指定了形参,那么在调用函数时也必须传递实参,
          实参将会赋值给对应的形参,简单来说,有几个形参就得传几个实参

    实参的传递方式:

      位置参数:就是将对应位置的实参赋值给对应位置的形参 

      关键字参数:可以不按照形参定义的顺序去传递,而直接根据参数名去传递参数

      位置参数和关键字参数可以混合使用,混合使用时,必须将位置参数写到关键字参数前面

        函数在调用时,解析器不会检查实参的类型,实参可以传递任意类型的对象  

        在函数中对形参进行重新赋值,不会影响其他变量

     

    不定长参数:

      在定义函数时,可以在形参前加上一个*,这个形参将会获取到所有的实参,它将会将所有的实参保存到一个元组中

      *a会接收到所有的位置实参,并且将这些实参统一保存到一个元组中(装包)

      带*的形参只能有一个,可以和其他参数配合使用

      可变参数不是必须写在最后,但是注意,带*的参数后的所有参数,必须以关键字参数的形式传递

      *形参只能接收位置参数,而不能接收关键字参数

      **形参可以接收其他的关键字参数,它会将这些参数统一保存到一个字典中,字典的key就是参数的名字,字典的value就是参数的值

      **形参只能有一个,并且必须写在所有参数的最后

      参数的解包(拆包)

        传递参数时,也可以在序列类型的参数前加*,这样它会自动将序列中的元素依次作为参数传递,这里要求元素中的个数必须和形参的个数一直

        通过**来对一个字典进行解包 

    返回值:就是函数执行以后返回的结果

      可以通过return来制定函数的返回值

      可以直接使用函数的返回值,也可以通过一个变量来接收函数的返回值

      return后面跟什么值,函数就会返回什么值,可以跟任意的对象,返回值甚至可以是一个函数

      如果仅仅写一个return,或者不写return,则相当于return None

      在函数中,return后的代码都不会执行,return一旦执行函数自动结束  

     

    作用域:指的是变量生效的区域

      在python中有两种作用域

        全局作用域

          全局作用域在程序执行时创建,在程序执行结束时销毁

          所有函数以外的区域都是全局作用域

          在全局作用域中定义的变量,都属于全局变量,全局变量可以在程序的任意位置被访问

        函数作用域

          函数作用域在函数调用时创建,在调用结束时销毁

          函数每调用一次就会产生一个新的函数作用域

          在函数作用域中定义的变量,都是全局变量,它只能在函数内部被访问

        变量的查找

          当我们使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有则继续去上一级作用域中寻找,以此类推

          直到找到全局作用域,依然没有找到,则会抛出异常

          如果希望在函数内部修改全局变量,则需要使用global关键字来声明变量

     

    命名空间:指的是变量存储的位置,每一个变量都需要存储到指定的命名空间当中

      每一个作用域都有一个自己的命名空间,实际上就是一个字典,是一个专门用来存储变量的字典

      locals()用来获取当前作用域的命名空间

      globals()可以用来在任意位置获取全局命名空间

     

    --递归--

    简单来理解就是自己去引用自己

    递归式函数,在函数中自己调用自己

    无穷递归,如果这个函数被调用,程序的内存会溢出,效果类似于死循环

    它的整体思想是,将一个大问题分解为一个个的小问题,知道问题无法分解,再去解决问题

    递归式函数的两个条件:

      1、基线条件:

        问题可以被分解为的最小问题,当满足基线条件时,递归就不再执行了

      2、递归条件:

        将问题继续分解的条件

    递归和循环类似,基本上是可以互相替代的

      循环编写较容易,阅读起来稍难,递归相反

     创建一个函数,可以用来求任意数的阶乘

    def factorial(n):
        '''
            该函数用来求任意数的阶乘
            参数:
                n 要求阶乘的数字
        '''
        # 创建一个变量,来保存结果
        result = n  
        for i in range(1,n):
            result *= i
        return result    
    # 求10的阶乘    
    print(factorial(10))
    
    
    def factorial(n):
        # 基线条件 判断n是否为1,如果为1则此时不能再继续递归
        if n == 1 :
            # 1的阶乘就是1,直接返回1
            return 1
        # 递归条件    
        return n * factorial(n-1)
    
    print(factorial(10))
    创建一个函数 power 来为任意数字做幂运算 n ** i
    def power(n , i):
        '''
            power()用来为任意的数字做幂运算
    
            参数:
                n 要做幂运算的数字
                i 做幂运算的次数
        '''
        # 基线条件
        if i == 1:
            # 求1次幂
            return n
        # 递归条件
        return n * power(n , i-1)
    
    print(power(8,6))    
    创建一个函数,用来检查一个任意的字符串是否是回文字符串,如果是返回True,否则返回False
    回文字符串,字符串从前往后念和从后往前念是一样的
    abcba
    abcdefgfedcba
    先检查第一个字符和最后一个字符是否一致,如果不一致则不是回文字符串
    如果一致,则看剩余的部分是否是回文字符串
    检查 abcdefgfedcba 是不是回文
    检查 bcdefgfedcb 是不是回文
    检查 cdefgfedc 是不是回文
    检查 defgfed 是不是回文
    检查 efgfe 是不是回文
    检查 fgf 是不是回文
    检查 g 是不是回文
    def hui_wen(s):
        '''
            该函数用来检查指定的字符串是否回文字符串,如果是返回True,否则返回False
    
            参数:
                s:就是要检查的字符串
        '''
        # 基线条件
        if len(s) < 2 :
            # 字符串的长度小于2,则字符串一定是回文
            return True
        elif s[0] != s[-1]:
            # 第一个字符和最后一个字符不相等,不是回文字符串
            return False    
        # 递归条件    
        return hui_wen(s[1:-1])
    print(hui_wen('abcdefgfedcba'))
    另解:
    def hui_wen(s):
        '''
            该函数用来检查指定的字符串是否回文字符串,如果是返回True,否则返回False
    
            参数:
                s:就是要检查的字符串
        '''
        # 基线条件
        if len(s) < 2 :
            # 字符串的长度小于2,则字符串一定是回文
            return True
        # 递归条件    
        return s[0] == s[-1] and hui_wen(s[1:-1])
    
    print(hui_wen('abcdefgfedcba'))    
    
    

    函数式编程
    - 在Python中,函数是一等对象
    - 一等对象一般都会具有如下特点:
      ① 对象是在运行时创建的
      ② 能赋值给变量或作为数据结构中的元素
      ③ 能作为参数传递
      ④ 能作为返回值返回

    - 高阶函数
    - 高阶函数至少要符合以下两个特点中的一个
      ① 接收一个或多个函数作为参数
      ② 将函数作为返回值返回

    
    
    filter()
    filter()可以从序列中过滤出符合条件的元素,保存到一个新的序列中
    参数:
    1.函数,根据该函数来过滤序列(可迭代的结构)
    2.需要过滤的序列(可迭代的结构)
    返回值:
    过滤后的新序列(可迭代的结构)
    匿名函数 lambda 函数表达式 (语法糖)
    lambda函数表达式专门用来创建一些简单的函数,他是函数创建的又一种方式
    语法:lambda 参数列表 : 返回值
    匿名函数一般都是作为参数使用,其他地方一般不会使用

    map()函数可以对可迭代对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回
    sort()
    该方法用来对列表中的元素进行排序
    sort()方法默认是直接比较列表中的元素的大小
    在sort()可以接收一个关键字参数 , key
    key需要一个函数作为参数,当设置了函数作为参数
    每次都会以列表中的一个元素作为参数来调用函数,并且使用函数的返回值来比较元素的大小
    sorted()
    这个函数和sort()的用法基本一致,但是sorted()可以对任意的序列进行排序
    并且使用sorted()排序不会影响原来的对象,而是返回一个新对象
    闭包:
    # 将函数作为返回值返回,也是一种高阶函数
    # 这种高阶函数我们也称为叫做闭包,通过闭包可以创建一些只有当前函数能访问的变量,可以将一些私有的数据藏到的闭包中
    形成闭包的要件
    ① 函数嵌套
    ② 将内部函数作为返回值返回
    ③ 内部函数必须要使用到外部函数的变量

    装饰器:
    我们需要对函数进行扩展,可以直接通过修改函数中的代码来完成这个需求,但是会产生以下一些问题
    ① 如果要修改的函数过多,修改起来会比较麻烦
    ② 并且不方便后期的维护
    ③ 并且这样做会违反开闭原则(OCP)
    程序的设计,要求开放对程序的扩展,要关闭对程序的修改
    def begin_end(old):
        '''
            用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束
    
            参数:
                old 要扩展的函数对象
        '''
        # 创建一个新函数
        def new_function(*args , **kwargs):
            print('开始执行~~~~')
            # 调用被扩展的函数
            result = old(*args , **kwargs)
            print('执行结束~~~~')
            # 返回函数的执行结果
            return result
    
        # 返回新函数        
        return new_function
    f = begin_end(fn)
    f2 = begin_end(add)
    f3 = begin_end(mul)
    向begin_end()这种函数我们就称它为装饰器
    通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
    在开发中,我们都是通过装饰器来扩展函数的功能的
    在定义函数时,可以通过@装饰器,来使用指定的装饰器,来装饰当前的函数
    可以同时为一个函数指定多个装饰器,这样函数将会安装从内向外的顺序被装饰

    /******类与对象******/

      面向对象(oop)

    - Python是一门面向对象的编程语言
    - 所谓的面向对象的语言,简单理解就是语言中的所有操作都是通过对象来进行的
    - 面向过程的编程的语言
    - 面向过程指将我们的程序的逻辑分解为一个一个的步骤,
      通过对每个步骤的抽象,来完成程序

         这种编程方式,符合我们人类的思维,编写起来相对比较简单

      - 但是这种方式编写代码的往往只适用于一个功能,
        如果要在实现别的功能,即使功能相差极小,也往往要重新编写代码,
        所以它可复用性比较低,并且难于维护

     

    面向对象的编程语言
    - 面向对象的编程语言,关注的是对象,而不关注过程
    - 对于面向对象的语言来说,一切都是对象 

    - 面向对象的编程思想,将所有的功能统一保存到对应的对象中
      比如,妈妈功能保存到妈妈的对象中,孩子的功能保存到孩子对象中
      要使用某个功能,直接找到对应的对象即可
    - 这种方式编写的代码,比较容易阅读,并且比较易于维护,容易复用。
    - 但是这种方式编写,不太符合常规的思维,编写起来稍微麻烦一点

     

    类(class)
    - 我们目前所学习的对象都是Python内置的对象
    - 但是内置对象并不能满足所有的需求,所以我们在开发中经常需要自定义一些对象
    - 类,简单理解它就相当于一个图纸。在程序中我们需要根据类来创建对象
    - 类就是对象的图纸!
    - 我们也称对象是类的实例(instance)
    - 如果多个对象是通过一个类创建的,我们称这些对象是一类对象
    - 像 int() float() bool() str() list() dict() .... 这些都是类
    - a = int(10) # 创建一个int类的实例 等价于 a = 10
    - 我们自定义的类都需要使用大写字母开头,使用大驼峰命名法(帕斯卡命名法)来对类命名

    - 类也是一个对象!
    - 类就是一个用来创建对象的对象!
    - 类是type类型的对象,定义类实际上就是定义了一个type类型的对象

     

    ## 使用类创建对象的流程
      1.创建一个变量
      2.在内存中创建一个新对象
      3.将对象的id赋值给变量

    ## 类的定义
    - 类和对象都是对现实生活中的事物或程序中的内容的抽象
    - 实际上所有的事物都由两部分构成:
      1.数据(属性)
      2.行为(方法)

    - 在类的代码块中,我们可以定义变量和函数,
    变量会成为该类实例的公共属性,所有的该类实例都可以通过 对象.属性名 的形式访问
    函数会成为该类实例的公共方法,所有该类实例都可以通过 对象.方法名() 的形式调用方法

    - 注意:
    方法调用时,第一个参数由解析器自动传递,所以定义方法时,至少要定义一个形参!

    - 属性和方法查找的流程
      当我们调用一个对象的属性时,解析器会先在当前对象中寻找是否含有该属性,
        如果有,则直接返回当前的对象的属性值,
        如果没有,则去当前对象的类对象中去寻找,如果有则返回类对象的属性值,
        如果类对象中依然没有,则报错!

    - 类对象和实例对象中都可以保存属性(方法)
      - 如果这个属性(方法)是所有的实例共享的,则应该将其保存到类对象中
      - 如果这个属性(方法)是某个实例独有,则应该保存到实例对象中

    - 一般情况下,属性保存到实例对象中,而方法需要保存到类对象中

     

    目前来讲,对于Person类来说name是必须的,并且每一个对象中的name属性基本上都是不同
    而我们现在是将name属性在定义为对象以后,手动添加到对象中,这种方式很容易出现错误
    我们希望,在创建对象时,必须设置name属性,如果不设置对象将无法创建
    并且属性的创建应该是自动完成的,而不是在创建对象以后手动完成

     

    -在类中可以定义一些特殊方法(魔术方法)
    特殊方法都是以__开头,__结尾的方法
    特殊方法不需要我们自己调用,不要尝试去调用特殊方法
    特殊方法将会在特殊的时刻自动调用
    学习特殊方法:
    1.特殊方法什么时候调用
    2.特殊方法有什么作用
    创建对象的流程
    p1 = Person()的运行流程
    1.创建一个变量
    2.在内存中创建一个新对象
    3.__init__(self)方法执行
    4.将对象的id赋值给变量

    init会在对象创建以后离开执行
    init可以用来向新创建的对象中初始化属性
    调用类创建对象时,类后边的所有参数都会依次传递到init()中
    def __init__(self,name):
    # print(self)
    # 通过self向新建的对象中初始化属性
    self.name = name
    类的基本结构 
      class 类名([父类]) :
    
        公共的属性...
    
        # 对象的初始化方法
        def __init__(self,...):
          ...
    
        #其他的方法 
        def method_1(self,...):
          ...
    
        def method_2(self,...):
          ...
    
        ...

    - 练习:
    尝试自定义一个表示狗的类(Dog)
    属性:
      name
      age
      gender
      height
      ...
    方法:
      jiao()
      yao()
      run()
      ... 

    class Dog:
        '''
            表示狗的类
        '''
        def __init__(self , name , age , gender , height):
            self.name = name
            self.age = age
            self.gender = gender
            self.height = height
    
        def jiao(self):
            '''
                狗叫的方法
            '''
            print('汪汪汪~~~')
    
        def yao(self):
            '''
                狗咬的方法
            '''  
            print('我咬你~~')
    
        def run(self):
            print('%s 快乐的奔跑着~~'%self.name)     
    目前我们可以直接通过 对象.属性 的方式来修改属性的值,这种方式导致对象中的属性可以随意修改
    非常的不安全,值可以任意修改,不论对错
    现在我们就需要一种方式来增强数据的安全性
    1.属性不能随意修改(我让你改你才能改,不让你改你就不能改)
    2.属性不能修改为任意的值(年龄不能是负数)

     

    定义一个类描述数字时钟
    import time
    class Clock(object):
        '''
        数字时钟
        '''
    
        def __init__(self,hour=0,minute=0,second=0):
            self._hour=hour
            self._minute=minute
            self._second=second
    
        def run(self):
            self._second+=1
            if self._second==60:
                self._second=0
                self._minute+=1
                if self._minute==60:
                    self._minute=0
                    self._hour+=1
                    if self._hour==24:
                        self._hour=0
    
        def show(self):
            return '%02d:%02d:%02d'%(self._hour,self._minute,self._second)
    
    
    def main():
        clock=Clock(23,59,58)
        while True:
            print(clock.show())
            time.sleep(1)
            clock.run()
    
    if __name__ == '__main__':
        main()

    /******封装******/

    封装是面向对象的三大特性之一
    封装指的是隐藏对象中一些不希望被外部所访问到的属性或方法
    如何隐藏一个对象中的属性?
    - 将对象的属性名,修改为一个外部不知道的名字
    如何获取(修改)对象中的属性?
    - 需要提供一个getter和setter方法使外部可以访问到属性
    - getter 获取对象中的指定属性(get_属性名)
    - setter 用来设置对象的指定属性(set_属性名)
    使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
    1.隐藏了属性名,使调用者无法随意的修改对象中的属性
    2.增加了getter和setter方法,很好的控制的属性是否是只读的
    如果希望属性是只读的,则可以直接去掉setter方法
    如果希望属性不能被外部访问,则可以直接去掉getter方法
    3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
    4.使用getter方法获取属性,使用setter方法设置属性
    可以在读取属性和修改属性的同时做一些其他的处理
    5.使用getter方法可以表示一些计算的属性
    class Dog:
        '''
            表示狗的类
        '''
        def __init__(self , name , age):
            self.hidden_name = name
            self.hidden_age = age
    
        def say_hello(self):
            print('大家好,我是 %s'%self.hidden_name) 
    
        def get_name(self):
            '''
                get_name()用来获取对象的name属性
            '''    
            # print('用户读取了属性')
            return self.hidden_name
    
        def set_name(self , name):
            # print('用户修改了属性')
            self.hidden_name = name
    
        def get_age(self):
            return self.hidden_age
    
        def set_age(self , age):
            if age > 0 :
                self.hidden_age = age    
    调用setter来修改name属性 
    d.set_name('小黑')
    d.set_age(-10)
    可以为对象的属性使用双下划线开头,__xxx
    双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
    其实隐藏属性只不过是Python自动为属性改了一个名字
    实际上是将名字修改为了,_类名__属性名 比如 __name -> _Person__name
    class Person:
        def __init__(self,name):
            self.__name = name
    
        def get_name(self):
            return self.__name
    
        def set_name(self , name):
            self.__name = name
    
    p = Person('孙悟空')
    
    print(p.__name) __开头的属性是隐藏属性,无法通过对象访问
    p.__name = '猪八戒'
    print(p._Person__name)
    p._Person__name = '猪八戒'
    
    print(p.get_name())
    使用__开头的属性,实际上依然可以在外部访问,所以这种方式我们一般不用
    一般我们会将一些私有属性(不希望被外部访问的属性)以_开头
    一般情况下,使用_开头的属性都是私有属性,没有特殊需要不要修改私有属性
    class Person:
        def __init__(self,name):
            self._name = name
    
        def get_name(self):
            return self._name
    
        def set_name(self , name):
            self._name = name   
    
    p = Person('孙悟空')
    
    print(p._name)
    property装饰器,用来将一个get方法,转换为对象的属性
    添加为property装饰器以后,我们就可以像调用属性一样使用get方法
    使用property装饰的方法,必须和属性名是一样的
    @property    
    def name(self):
        print('get方法执行了~~~')
        return self._name
    
    # setter方法的装饰器:@属性名.setter
    @name.setter    
    def name(self , name):
        print('setter方法调用了')
        self._name = name        

    /******继承和多态******/

    有一个类,能够实现我们需要的大部分功能,但是不能实现全部功能
    如何能让这个类来实现全部的功能呢?
    ① 直接修改这个类,在这个类中添加我们需要的功能
    - 修改起来会比较麻烦,并且会违反OCP原则
    ② 直接创建一个新的类
    - 创建一个新的类比较麻烦,并且需要大量的进行复制粘贴,会出现大量的重复性代码
    ③ 直接从Animal类中来继承它的属性和方法
    - 继承是面向对象三大特性之一
    - 通过继承我们可以使一个类获取到其他类中的属性和方法
    - 在定义类时,可以在类名后的括号中指定当前类的父类(超类、基类、super)
    子类(衍生类)可以直接继承父类中的所有的属性和方法

    通过继承可以直接让子类获取到父类的方法或属性,避免编写重复性的代码,并且也符合OCP原则
    所以我们经常需要通过继承来对一个类进行扩展 
    定义一个类 Animal(动物)
    这个类中需要两个方法:run() sleep()
    class Animal:
        def run(self):
            print('动物会跑~~~')
    
        def sleep(self):
            print('动物睡觉~~~')
    class Dog(Animal):
        def bark(self):
            print('汪汪汪~~~') 
    class Hashiqi(Dog):
        def fan_sha(self):
            print('我是一只傻傻的哈士奇')    
    在创建类时,如果省略了父类,则默认父类为object
    object是所有类的父类,所有类都继承自object
    issubclass() 检查一个类是否是另一个类的子类
    isinstance()用来检查一个对象是否是一个类的实例
    如果这个类是这个对象的父类,也会返回True
    所有的对象都是object的实例
    如果在子类中如果有和父类同名的方法,则通过子类实例去调用方法时,
    会调用子类的方法而不是父类的方法,这个特点我们成为叫做方法的重写(覆盖,override)
    当我们调用一个对象的方法时,
    会优先去当前对象中寻找是否具有该方法,如果有则直接调用
    如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法,
    如果没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,则报错
    super()方法
    class Animal:
        def __init__(self,name):
            self._name = name
    
        def run(self):
            print('动物会跑~~~')
    
        def sleep(self):
            print('动物睡觉~~~')
    
        @property
        def name(self):
            return self._name
    
        @name.setter    
        def name(self,name):
            self._name = name

    # 父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法
    class Dog(Animal):
    
        def __init__(self,name,age):
            # 希望可以直接调用父类的__init__来初始化父类中定义的属性
            # super() 可以用来获取当前类的父类,
            #   并且通过super()返回对象调用父类方法时,不需要传递self
            super().__init__(name)
            self._age = age
    
        def bark(self):
            print('汪汪汪~~~') 
    
        def run(self):
            print('狗跑~~~~')   
    
        @property
        def age(self):
            return self._age
    
        @age.setter    
        def age(self,age):
            self._age = age
    
    d = Dog('旺财',18) 
    
    print(d.name)       
    print(d.age)      
    多重继承
    class A(object):
        def test(self):
            print('AAA')
    
    class B(object):
        def test(self):
            print('B中的test()方法~~')
    
        def test2(self):
            print('BBB') 
    在Python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类
    可以在类名的()后边添加多个类,来实现多重继承
    多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
    在开发中没有特殊的情况,应该尽量避免使用多重继承,因为多重继承会让我们的代码过于复杂
    如果多个父类中有同名的方法,则会现在第一个父类中寻找,然后找第二个,然后找第三个。。。
    前边父类的方法会覆盖后边父类的方法
    类名.__bases__ 这个属性可以用来获取当前类的所有父类    
    print(C.__bases__) (<class '__main__.B'>,)
    print(B.__bases__) (<class 'object'>,)

    print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>)

    多态
    多态是面向对象的三大特征之一
    多态从字面上理解是多种形态
    狗(狼狗、藏獒、哈士奇、边牧 。。。)
    一个对象可以以不同的形态去呈现

    # 定义两个类
    class A:
        def __init__(self,name):
            self._name = name
    
        @property
        def name(self):
            return self._name
            
        @name.setter
        def name(self,name):
            self._name = name   
    
    class B:
        def __init__(self,name):
            self._name = name
    
        def __len__(self):
            return 10
    
        @property
        def name(self):
            return self._name
            
        @name.setter
        def name(self,name):
            self._name = name   
    
    class C:
        pass
    
    
    a = A('孙悟空')
    b = B('猪八戒')
    c = C()

    定义一个函数
    对于say_hello()这个函数来说,只要对象中含有name属性,它就可以作为参数传递
    这个函数并不会考虑对象的类型,只要有name属性即可
    def say_hello(obj):
        print('你好 %s'%obj.name)
    在say_hello_2中我们做了一个类型检查,也就是只有obj是A类型的对象时,才可以正常使用,
    其他类型的对象都无法使用该函数,这个函数就违反了多态
    违反了多态的函数,只适用于一种类型的对象,无法处理其他类型对象,这样导致函数的适应性非常的差
    注意,向isinstance()这种函数,在开发中一般是不会使用的!
    def say_hello_2(obj):
        # 做类型检查
        if isinstance(obj , A):
            print('你好 %s'%obj.name)    
    # say_hello(b)    
    # say_hello_2(b)

    鸭子类型
    如果一个东西,走路像鸭子,叫声像鸭子,那么它就是鸭子

    len()
    之所以一个对象能通过len()来获取长度,是因为对象中具有一个特殊方法__len__
    换句话说,只要对象中具有__len__特殊方法,就可以通过len()来获取它的长度

    面向对象的三大特征:
    封装
    - 确保对象中的数据安全
    继承
    - 保证了对象的可扩展性
    多态
    - 保证了程序的灵活性
    属性和方法
    # 定义一个类
    class A(object):
    
        # 类属性
        # 实例属性
        # 类方法
        # 实例方法
        # 静态方法
    
        # 类属性,直接在类中定义的属性是类属性
        #   类属性可以通过类或类的实例访问到
        #   但是类属性只能通过类对象来修改,无法通过实例对象修改
        count = 0
    
        def __init__(self):
            # 实例属性,通过实例对象添加的属性属于实例属性
            #   实例属性只能通过实例对象来访问和修改,类对象无法访问修改
            self.name = '孙悟空'
    
        # 实例方法
        #   在类中定义,以self为第一个参数的方法都是实例方法
        #   实例方法在调用时,Python会将调用对象作为self传入  
        #   实例方法可以通过实例和类去调用
        #       当通过实例调用时,会自动将当前调用对象作为self传入
        #       当通过类调用时,不会自动传递self,此时我们必须手动传递self
        def test(self):
            print('这是test方法~~~ ' , self)    
    
        # 类方法    
        # 在类内部使用 @classmethod 来修饰的方法属于类方法
        # 类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
        #   类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
        #   类方法可以通过类去调用,也可以通过实例调用,没有区别
        @classmethod
        def test_2(cls):
            print('这是test_2方法,他是一个类方法~~~ ',cls)
            print(cls.count)
    
        # 静态方法
        # 在类中使用 @staticmethod 来修饰的方法属于静态方法  
        # 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用  
        # 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
        # 静态方法一般都是一些工具方法,和当前类无关
        @staticmethod
        def test_3():
            print('test_3执行了~~~')
    垃圾回收
    就像我们生活中会产生垃圾一样,程序在运行过程当中也会产生垃圾
    程序运行过程中产生的垃圾会影响到程序的运行的运行性能,所以这些垃圾必须被及时清理
    没用的东西就是垃圾
    在程序中没有被引用的对象就是垃圾,这种垃圾对象过多以后会影响到程序的运行的性能
    所以我们必须进行及时的垃圾回收,所谓的垃圾回收就是讲垃圾对象从内存中删除
    在Python中有自动的垃圾回收机制,它会自动将这些没有被引用的对象删除,
    所以我们不用手动处理垃圾回收
    class A:
        def __init__(self):
            self.name = 'A类'
    
        # del是一个特殊方法,它会在对象被垃圾回收前调用
        def __del__(self):
            print('A()对象被删除了~~~',self)
    
    a = A()
    b = a # 又使用一个变量b,来引用a对应的对象
    
    print(a.name)
    
    # a = None # 将a设置为了None,此时没有任何的变量对A()对象进行引用,它就是变成了垃圾
    # b = None
    # del a
    # del b
    特殊方法
    特殊方法,也称为魔术方法
    特殊方法都是使用__开头和结尾的
    特殊方法一般不需要我们手动调用,需要在一些特殊情况下自动执行
    # 定义一个Person类
    class Person(object):
        """人类"""
        def __init__(self, name , age):
            self.name = name
            self.age = age
    
        # __str__()这个特殊方法会在尝试将对象转换为字符串的时候调用
        # 它的作用可以用来指定对象转换为字符串的结果  (print函数)  
        def __str__(self):
            return 'Person [name=%s , age=%d]'%(self.name,self.age)        
    
        # __repr__()这个特殊方法会在对当前对象使用repr()函数时调用
        # 它的作用是指定对象在 ‘交互模式’中直接输出的效果    
        def __repr__(self):
            return 'Hello'        
    当我们打印一个对象时,实际上打印的是对象的中特殊方法 __str__()的返回值
    print(p1) # <__main__.Person object at 0x04E95090>
    print(p1)
    print(repr(p1))

    /******模块******/

    模块(module)
    模块化,模块化指将一个完整的程序分解为一个一个小的模块
    通过将模块组合,来搭建出一个完整的程序
    不采用模块化,统一将所有的代码编写到一个文件中
    采用模块化,将程序分别编写到多个文件中
    模块化的有点:
    ① 方便开发
    ② 方便维护
    ③ 模块可以复用!

    在Python中一个py文件就是一个模块,要想创建模块,实际上就是创建一个python文件
    注意:模块名要符号标识符的规范

    在一个模块中引入外部模块
    ① import 模块名 (模块名,就是python文件的名字,注意不要py)
    ② import 模块名 as 模块别名
    - 可以引入同一个模块多次,但是模块的实例只会创建一个
    - import可以在程序的任意位置调用,但是一般情况下,import语句都会统一写在程序的开头
    - 在每一个模块内部都有一个__name__属性,通过这个属性可以获取到模块的名字
    - __name__属性值为 __main__的模块是主模块,一个程序中只会有一个主模块
    主模块就是我们直接通过 python 执行的模块
    def foo():
        b = 'hello'
    
        def bar():  # Python中可以在函数内部再定义函数
            c = True
            print(a)
            print(b)
            print(c)
    
        bar()
        # print(c)  # NameError: name 'c' is not defined
    
    if __name__ == '__main__':
        a = 100
        # print(b)  # NameError: name 'b' is not defined
        foo()

    100
    hello
    True
    上面的代码能够顺利的执行并且打印出100和“hello”,但我们注意到了,在bar函数的内部并没有定义ab两个变量,那么ab是从哪里来的。
    我们在上面代码的if分支中定义了一个变量a,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中。
    在上面的foo函数中我们定义了变量b,这是一个定义在函数中的局部变量(local variable),属于局部作用域,在foo函数的外部并不能访问到它;但对于foo函数内部的bar函数来说,变量b属于嵌套作用域,在bar函数中我们是可以访问到它的。
    bar函数中的变量c属于局部作用域,在bar函数之外是无法访问的。事实上,Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索。
    前三者我们在上面的代码中已经看到了,所谓的“内置作用域”就是Python内置的那些隐含标识符minlen等都属于内置作用域)。
    
    
    包 Package
    包也是一个模块
    当我们模块中代码过多时,或者一个模块需要被分解为多个模块时,这时就需要使用到包
    普通的模块就是一个py文件,而包是一个文件夹
    包中必须要一个一个 __init__.py 这个文件,这个文件中可以包含有包中的主要内容
    __pycache__ 是模块的缓存文件
    py代码在执行前,需要被解析器先转换为机器码,然后再执行
    所以我们在使用模块(包)时,也需要将模块的代码先转换为机器码然后再交由计算机执行
    而为了提高程序运行的性能,python会在编译过一次以后,将代码保存到一个缓存文件中
    这样在下次加载这个模块(包)时,就可以不再重新编译而是直接加载缓存中编译好的代码即可
    python标准库
    开箱即用
    为了实现开箱即用的思想,Python中为我们提供了一个模块的标准库
    在这个标准库中,有很多很强大的模块我们可以直接使用,
    并且标准库会随Python的安装一同安装
    sys模块,它里面提供了一些变量和函数,使我们可以获取到Python解析器的信息
    或者通过函数来操作Python解析器
    引入sys模块
    import sys

    pprint 模块它给我们提供了一个方法 pprint() 该方法可以用来对打印的数据做简单的格式化
    import pprint

    sys.argv
    获取执行代码时,命令行中所包含的参数
    该属性是一个列表,列表中保存了当前命令的所有参数
    print(sys.argv)

    sys.modules
    获取当前程序中引入的所有模块
    modules是一个字典,字典的key是模块的名字,字典的value是模块对象
    pprint.pprint(sys.modules)

    sys.path
    他是一个列表,列表中保存的是模块的搜索路径
    ['C:\Users\lilichao\Desktop\resource\course\lesson_06\code',
    'C:\dev\python\python36\python36.zip',
    'C:\dev\python\python36\DLLs',
    'C:\dev\python\python36\lib',
    'C:\dev\python\python36',
    'C:\dev\python\python36\lib\site-packages']
    pprint.pprint(sys.path)

    sys.platform
    表示当前Python运行的平台
    print(sys.platform)

    sys.exit()
    函数用来退出程序
    sys.exit('程序出现异常,结束!')
    print('hello')

    os 模块让我们可以对操作系统进行访问
    import os

    os.environ
    通过这个属性可以获取到系统的环境变量
    pprint.pprint(os.environ['path'])

    os.system()
    可以用来执行操作系统的名字
    os.system('dir')
    os.system('notepad')


    /******异常处理******/

     

      程序在运行过程当中,不可避免的会出现一些错误,比如:
        使用了没有赋值过的变量
        使用了不存在的索引
        除0
        ...
      这些错误在程序中,我们称其为异常。
      程序运行过程中,一旦出现异常将会导致程序立即终止,异常以后的代码全部都不会执行!

     

    ## 处理异常
      程序运行时出现异常,目的并不是让我们的程序直接终止!
      Python是希望在出现异常时,我们可以编写代码来对异常进行处理!

    try语句
      try:
        代码块(可能出现错误的语句)
      except 异常类型 as 异常名:
        代码块(出现错误以后的处理方式)
      except 异常类型 as 异常名:
        代码块(出现错误以后的处理方式)
      except 异常类型 as 异常名:
        代码块(出现错误以后的处理方式)
      else:
        代码块(没出错时要执行的语句) 
      finally:
        代码块(该代码块总会执行)

      try是必须的 else语句有没有都行
      except和finally至少有一个

     

    可以将可能出错的代码放入到try语句,这样如果代码没有错误,则会正常执行,
      如果出现错误,则会执行expect子句中的代码,这样我们就可以通过代码来处理异常
      避免因为一个异常导致整个程序的终止

     

    ## 异常的传播(抛出异常)
      当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播,
        如果函数中没有对异常进行处理,则异常会继续向函数调用处传播,
        如果函数调用处处理了异常,则不再传播,如果没有处理则继续向调用处传播
        直到传递到全局作用域(主模块)如果依然没有处理,则程序终止,并且显示异常信息

     

    当程序运行过程中出现异常以后,所有的异常信息会被保存一个专门的异常对象中,
      而异常传播时,实际上就是异常对象抛给了调用处
      比如 : ZeroDivisionError类的对象专门用来表示除0的异常
         NameError类的对象专门用来处理变量错误的异常
          ....

     

    在Python为我们提供了多个异常对象

    如果except后不跟任何的内容,则此时它会捕获到所有的异常
    如果在except后跟着一个异常的类型,那么此时它只会捕获该类型的异常
    Exception 是所有异常类的父类,所以如果except后跟的是Exception,他也会捕获到所有的异常
    可以在异常类后边跟着一个 as xx 此时xx就是异常对象

     

     

    ## 抛出异常
      - 可以使用 raise 语句来抛出异常,
        raise语句后需要跟一个异常类 或 异常的实例

    # 也可以自定义异常类,只需要创建一个类继承Exception即可
    class MyError(Exception):
        pass
    
    def add(a,b):
        # 如果a和b中有负数,就向调用处抛出异常
        if a < 0 or b < 0:
            # raise用于向外部抛出异常,后边可以跟一个异常类,或异常类的实例
            # raise Exception    
            # 抛出异常的目的,告诉调用者这里调用时出现问题,希望你自己处理一下
            # raise Exception('两个参数中不能有负数!')  
            raise MyError('自定义的异常')
            
            # 也可以通过if else来代替异常的处理
            # return None

    /******文件******/

    - 通过Python程序来对计算机中的各种文件进行增删改查的操作
    - I/O(Input / Output)
    - 操作文件的步骤:
      ① 打开文件
      ② 对文件进行各种操作(读、写),然后保存
      ③ 关闭文件
    
    open(file, mode='r', buffering=-1, encoding_=None, errors=None, newline=None, closefd=True, opener=None)
    使用open函数来打开一个文件
    参数:
      file 要打开的文件的名字(路径)
    返回值:
      返回一个对象,这个对象就代表了当前打开的文件
    
    创建一个变量,来保存文件的名字
    如果目标文件和当前文件在同一级目录下,则直接使用文件名即可
    file_name = 'demo.txt'
    
    在windows系统使用路径时,可以使用/来代替 
    或者可以使用 \ 来代替 
    或者也可以使用原始字符串
    file_name = 'hello\demo.txt'
    file_name = r'hellodemo.txt'
    
    表示路径,可以使用..来返回一级目录
    file_name = '../hello/demo.txt'
    
    如果目标文件距离当前文件比较远,此时可以使用绝对路径
    绝对路径应该从磁盘的根目录开始书写
    file_name = r'C:UserslilichaoDesktophello.txt'
    调用open()来打开文件
    file_obj = open(file_name)
    当我们获取了文件对象以后,所有的对文件的操作都应该通过对象来进行
    读取文件中的内容
    read()方法,用来读取文件中的内容,它会将内容全部保存为一个字符串返回
    content = file_obj.read()
    
    print(content)
    关闭文件
    调用close()方法来关闭文件
    file_obj.close()
    with ... as 语句
    with open(file_name) as file_obj :
        # 在with语句中可以直接使用file_obj来做文件操作
        # 此时这个文件只能在with中使用,一旦with结束则文件会自动close()
        print(file_obj.read())
    
    try:
        with open(file_name) as file_obj :
            print(file_obj.read())
    except FileNotFoundError:
        print(f'{file_name} 文件不存在~~')
    
    
    file_name = 'demo2.txt'
    try:
        # 调用open()来打开一个文件,可以将文件分成两种类型
        # 一种,是纯文本文件(使用utf-8等编码编写的文本文件)
        # 一种,是二进制文件(图片、mp3、ppt等这些文件)
        # open()打开文件时,默认是以文本文件的形式打开的,但是open()默认的编码为None
        #   所以处理文本文件时,必须要指定文件的编码
        with open(file_name,encoding='utf-8') as file_obj:
            # 通过 read() 来读取文件中的内容
            # 如果直接调用read()它会将文本文件的所有内容全部都读取出来
            #   如果要读取的文件较大的话,会一次性将文件的内容加载到内存中,容易导致内存泄漏
            #   所以对于较大的文件,不要直接调用read()
            # help(file_obj.read)
            # read()可以接收一个size作为参数,该参数用来指定要读取的字符的数量
            #   默认值为-1,它会读取文件中的所有字符
            #   可以为size指定一个值,这样read()会读取指定数量的字符,
            #       每一次读取都是从上次读取到位置开始读取的
            #       如果字符的数量小于size,则会读取剩余所有的
            #       如果已经读取到了文件的最后了,则会返回''空串
            # content = file_obj.read(-1)
            # content = file_obj.read(6)
            # print(content)
            # print(len(content))
    except FileNotFoundError :
        print(f'{file_name} 这个文件不存在!')
    # 读取大文件的方式
    file_name = 'demo.txt'
    
    try:
        with open(file_name,encoding='utf-8') as file_obj:
            # 定义一个变量,来保存文件的内容
            file_content = ''
            # 定义一个变量,来指定每次读取的大小
            chunk = 100
            # 创建一个循环来读取文件内容
            while True:
                # 读取chunk大小的内容
                content = file_obj.read(chunk)
    
                # 检查是否读取到了内容
                if not content:
                    # 内容读取完毕,退出循环
                    break
    
                # 输出内容
                # print(content,end='')
                file_content += content
    
    except FileNotFoundError :
        print(f'{file_name} 这个文件不存在!')
    print(file_content)
    ---readline和readlines
    import pprint
    import os
    file_name = 'demo.txt'
    
    with open(file_name , encoding='utf-8') as file_obj:
        # readline()
        # 该方法可以用来读取一行内容
        # print(file_obj.readline(),end='')
        # print(file_obj.readline())
        # print(file_obj.readline())
    
        # readlines()
        # 该方法用于一行一行的读取内容,它会一次性将读取到的内容封装到一个列表中返回
        # r = file_obj.readlines()
        # pprint.pprint(r[0])
        # pprint.pprint(r[1])
        # pprint.pprint(r[2])
    
        for t in file_obj:
            print(t)
    ---文件写入
    file_name = 'demo5.txt'

    使用open()打开文件时必须要指定打开文件所要做的操作(读、写、追加)
    如果不指定操作类型,则默认是 读取文件 , 而读取文件时是不能向文件中写入的
    r 表示只读的
    w 表示是可写的,使用w来写入文件时,如果文件不存在会创建文件,如果文件存在则会截断文件
    截断文件指删除原来文件中的所有内容
    a 表示追加内容,如果文件不存在会创建文件,如果文件存在则会向文件中追加内容
    x 用来新建文件,如果文件不存在则创建,存在则报错
    + 为操作符增加功能
    r+ 即可读又可写,文件不存在会报错
    w+
    a+
    with open(file_name , 'w' , encoding='utf-8') as file_obj:
    with open(file_name , 'r+' , encoding='utf-8') as file_obj:
    with open(file_name , 'x' , encoding='utf-8') as file_obj:
        # write()来向文件中写入内容,
        # 如果操作的是一个文本文件的话,则write()需要传递一个字符串作为参数
        # 该方法会可以分多次向文件中写入内容
        # 写入完成以后,该方法会返回写入的字符的个数
        file_obj.write('aaa
    ')
        file_obj.write('bbb
    ')
        file_obj.write('ccc
    ')
        r = file_obj.write(str(123)+'123123
    ')
        r = file_obj.write('今天天气真不错')
        print(r)
    file_name = 'c:/Users/lilichao/Desktop/告白气球.flac'
    
    # 读取模式
    # t 读取文本文件(默认值)
    # b 读取二进制文件
    
    with open(file_name , 'rb') as file_obj:
        # 读取文本文件时,size是以字符为单位的
        # 读取二进制文件时,size是以字节为单位
        # print(file_obj.read(100))
    
        # 将读取到的内容写出来
        # 定义一个新的文件
        new_name = 'aa.flac'
    
        with open(new_name , 'wb') as new_obj:
    
            # 定义每次读取的大小
            chunk = 1024 * 100
    
            while True :
                # 从已有的对象中读取数据
                content = file_obj.read(chunk)
    
                # 内容读取完毕,终止循环
                if not content :
                    break
    
                # 将读取到的数据写入到新对象中
                new_obj.write(content)
    
    
    seek() 可以修改当前读取的位置
    file_obj.seek(80,0)
    file_obj.seek(70,1)
    file_obj.seek(-10,2)
    seek()需要两个参数
      第一个 是要切换到的位置
      第二个 计算位置方式
          可选值:
              0 从头计算,默认值
              1 从当前位置计算
              2 从最后位置开始计算
    tell() 方法用来查看当前读取的位置
    print('当前读取到了 -->',file_obj.tell())
    ---其他操作
    import os
    from pprint import pprint
    
    os.listdir() 获取指定目录的目录结构
    需要一个路径作为参数,会获取到该路径下的目录结构,默认路径为 . 当前目录
    该方法会返回一个列表,目录中的每一个文件(夹)的名字都是列表中的一个元素
    r = os.listdir()
    
    os.getcwd() 获取当前所在的目录
    r = os.getcwd()
    
    os.chdir() 切换当前所在的目录 作用相当于 cd
    os.chdir('c:/')
    创建目录
    os.mkdir("aaa") # 在当前目录下创建一个名字为 aaa 的目录
    删除目录
    os.rmdir('abc')
    
    open('aa.txt','w')
    删除文件
    os.remove('aa.txt')
    
    os.rename('旧名字','新名字') 可以对一个文件进行重命名,也可以用来移动一个文件
    os.rename('aa.txt','bb.txt')
    os.rename('bb.txt','c:/users/lilichao/desktop/bb.txt')
    
    pprint(r)
  • 相关阅读:
    yii2框架随笔9
    yii2源码学习笔记(五)
    yii2源码学习笔记(四)
    yii2源码学习笔记(三)
    yii2源码学习笔记(二)
    yii2源码学习笔记
    学习yii2.0框架阅读代码(一)
    (转)OAuth 2.0授权协议详解和流程
    (转)JavaScript 中对变量和函数声明的“提前(hoist)”
    JavaScript 中的算术运算
  • 原文地址:https://www.cnblogs.com/dalton/p/11794500.html
Copyright © 2020-2023  润新知