Python学习笔记

2020年6月10号晚 阴 微风

今晚出去庆祝同事进公司一周年 回来后发现自己也即将写了三年PHP了~~也感觉自己好久没有学习了

所以从今晚开始 学习学习Python 主要也是想通过Python也将自己 丢掉已久的数学给捡回来

为什么是Python而不是Java、Go或者C#之类的呢 因为我始终感觉自己对Java喜爱不起来 从我大学开始学编程便是如此 所以不想再因为兴趣问题半途而废 Go的话其实还是蛮有兴趣 等我学完Python还有精力学习的话应该会考虑学习学习Go(毕竟下载的B站后台源码除了一句 fuck chanpin 其他的还没看懂哈哈哈哈)

好了 废话不多说 下面就开撸吧~~~

#这是一行注释 (学一门编程语言 从学习怎么写注释开始~~~~)
'''
这是一段注释
'''

# python 数据类型
    # 整数
    # 浮点数
    # 字符串
    # 布尔值
    # 空值 None None不能理解为0,因为0是有意义的,None是没意义的
    # 除以上还有列表字典等多种数据类型 还允许自定义数据类型
# 占位符
    # %d 表示整数占位符
    # %f 表示浮点数占位符
    # %s 表示字符串占位符
    # %x 表示十六进制整数占位符
    # 如果不确定应该用什么,用%s就可以了,因为它会将任何数据类型转成字符串类型
    str = '整数占位符 %d ,字符串占位符 %s, 浮点数占位符 %f' %(1000,'字符串',3.25)
    print(str)
# 定义变量 (与php相同,只是不用写$符哈哈哈)
    a = 10
    b = 3.25
    c = '哈哈哈哈'

# 数据类型 list list是一种有序的集合 可以随时添加或删除其中的元素 list元素的类型可以不一样 然后list元素也可以是一个list(与php数组相同)
    classmates = ['小红','小明','小白']
    # 获取list的长度可以用len()函数 len(classmates) 获取字符串的长度也可以用len()函数
    listLength = len(classmates)
    # 可以通过索引来获取list内的元素(与php相同) 但超出索引范围会报错 最后一个元素的索引是 len(classmates)-1 list长度减一
    # 要获取最后一个元素还可以通过-1做索引 list[-1]就获取到最后一个元素,以此类推-2,-3分别获取倒数第二、倒数第三个元素 同样越界之后会报错
    stuOne = classmates[0] #第一个学生
    stuTwo = classmates[1] #第二个学生
    lastStu = classmates[len(classmates)-1] #最后一个学生
    # 往list后面添加元素 list.append('小花')
    classmates.append('小花')
    # 把元素插到指定位置 list.insert(0,'小彭')
    classmates.insert(0,'小彭') #这样小彭就变成了班里001号同学
    # 删除list末尾的元素 list.pop() 
    classmates.pop() #将小花从班里名单里移除(调到其他班去了)
    # 删除指定位置的元素 list.pop(i) i是索引 
    classmates.pop(1) #将小红从班里名单移除(辍学了)

# 数据类型tuple tuple是有序列表元组 tuple与list很像 但是tuple一旦初始化就不能再改了 因此没有append() insert()这些方法
    # 不可变的tuple有什么意义? 因为tuple不可变 所以代码更安全 能用tuple代替list就尽量使用tuple 
    otherClassmates = ('小黄','小饭','大华')
    # tuple的坑 当你的tuple只有一个元素时 
    tupleA = (1) #此时定义的不是tuple 而是数字1 因为()即可以表示tuple 又可以表示数学公式中的小括号 为了避免这种问题 只有一个元素的tuple定义时必须加一个逗号来消除歧义
    tupleB = (2,) #这样定义的才是tuple
    #定义一个可变tuple
    canChangeTuple = ('小红','小白',['小黄','小花'])
    canChangeTuple[2][0] = '小彭' 
    '''
        此时这个的tuple的元素就变成了 ('小红','小白',['小彭','小花']) 表面上看起来tuple的元素的确变了
        但这里要理解tuple的不变到底是什么含义 tuple所谓的“不变” 指的是 指向不变 即指向 '小红' 就不能改为指向 '小黑', 指向一个list 就不能改为指向其他对象 
        但指向的这个list本身是可变的 
        所以要创建一个不变的tuple要怎么做? 那就要保证tuple 的每一个元素都不可变 
    '''
# list和tuple是Python内置的有序集合,一个可变,一个不可变。根据需要来选择使用它们。

'''
  
    2020-6-11 00:33 今晚就先学到这里吧~~~ 加油

'''

# --------------------------- 时间分割线 -------------------------------------

# 条件判断 if: elif: else: 
    age = 20
    if age >=18:
        print('成年人') #这里一定要缩进 不然程序执行的时候会报错
    elif age >= 60 :
        print('退休了')
    else :
        print('未成年')
    # 再看input()函数
    # 直接用input返回的值作为数字去判断会报错 因为input函数返回的是字符串 应该要用int()转一下 
    # 但是int函数如果你的参数是类似于abc这种字符串 int函数又会报错
        age = int(input())
        if age >= 10:   
            print('够10岁了')
        else :
            print('不够10岁')

# 循环 有2中循环 一种是 for x in ... 一种是while循环
    # for x in ...循环 依次把list或tuple中的每一个元素迭代出来 如:
        names = ['小红','小彭','小花']
        for name in names: #别忘记冒号
            print(name)

        # 计算1到100的和 range()函数可以生成一个整数序列 再通过list()函数将这个序列转成list
        numbers = list(range(101))   
        sum = 0
        for number in numbers:
            sum += number
        print(sum)

    # while 循环 以及 break continue都与php相同 这里不再累赘

# 数据类型 dict(字典) 在其他语言中叫map 一种键值对的结构 也就是php的键值对数组
    scores = [
        '小红':98,
        '小彭':100,
        '小花':60
    ]
    print(scores['小彭']) # 输出100 如果key不存在 就会报错
    #要避免key不存在报错 有2种方法
        # 通过 in 判断 
        if '小彭' in scores:
            print('成绩有效')
        else :
            print('查无此人')

        #用get() 如果key不存在可以返回None 或自己定义的value
        score = scores.get('小白',60)#找不到小白的成绩就返回60分
    '''
        和list相比dict有以下几个特点:
            1、查找和插入的速度极快 不会随着key的增加而变慢
            2、需要占用大量的内存 内存浪费多
        而list与dict相反
            1、查找与插入的速度会随着元素的增多而变慢
            2、占用空间小 浪费内存很少
        所以dict是用空间来换时间的一种方法
    '''
# 数据类型set set与dict类型 也是一组key的集合 但set不存储value 在set中 不存在重复的key
    # 创建一个set 需要提供一个list作为输入集合
        setA = set([1,2,3])
        print(setA) # 输出{1,2,3} set是无序的 所以输出123并不表示它内部元素的顺序就是123
    # 重复的元素在set中会被自动过滤掉
        setB = set([1,1,2,3])
        print(setB)  # 输出{2,3,1}
    # 通过add(key)的方式可以添加元素到set中
        setB.add(5)
    #可以通过remove(key)的方式删除元素
        setB.remove(5)
    # set可以看做数学上的无序以及无重复元素的集合 所以它有一些集合的特性 可以求交集与并集
        print(setA&setB)
        print(setA | setB)

# 函数
    # 内置函数 如:abs() print()等等 可以通过help(abs)查看abs函数的帮助信息 
    # 可以访问http://docs.python.org/3/library/functions.html#abs 查看Python内置函数

    # 自定义函数 使用def语句 依次写出函数名 括号中的参数以及冒号:
        def my_add(a,b):
            return a + b
        print(my_add(10,20))
    # 函数结果通过return返回 如果没有return 函数执行完毕也会返回结果 返回结果为None return None 可以简写为return 
    # 如果想定义一个空函数 或者函数的内部逻辑你还没确定
        def nop():
            pass
    # 调用函数时 如果参数个数不对 Python解释器可以帮我们检查出来 并抛出typeError 但是如果参数的类型不对 解释器就不能帮我们检查出来了
    # 数据类型检查我们可以通过 isinstance()实现
        a = 10
        if not isinstance(a,(int,float)):
            print('参数错误')
    # 返回多个值 (在php中函数要返回多个值一般是将多个返回值放到一个数组中一起返回 )
        # 输入x和y坐标 返回真实坐标函数
            def new_local(x,y):
                newX = x+10
                newY = y+5
                return newX,newY
        newX,newY = new_local(5,50) #这样我们就同时获取到2个函数返回值
        # 但其实这是一种假象 python函数返回的仍然是单一值 可以通过一个变量去接收返回值看出
        newLocal = new_local(5,10)
        print(newLocal) # 输出(15,10) 原来多个返回值时是将所有的返回值放到一个tuple中 多个变量同时接收一个tuple时 按位置赋值
    
    # 函数的参数 pyton函数定义非常简单 但灵活度却很高 除了正常定义的参数外 还可以使用默认参数、可变参数和关键字参数
        #必选参数以及默认参数比较简单 这里不再展开叙述 但必选参数在前 默认参数在后 不然python会报错  而且默认参数必须指向一个不变的对象
        #可变参数 在php中要实现可变参数一般是将参数都扔到数组里面 在python中也可以将参数都传到list或tuple中
            def cala(numbers):
                sum = 0
                for number in numbers:
                    sum = sum + number
                return sum
            sum = cala([1,2,3]) #像这样 要先构建一个list
        # 但是如果在numbers参数见前面加个* 就不需要提前构建list了
            def calas(*numbers):
                sum = 0
                for number in numbers:
                    sum = sum + number
                return sum
            sum = cala(1,2,3) #像这样 在函数内部 参数numbers接收到的是一个tuple 
        # 如果已经有一个list或tuple了
            numbersNew = [1,2,3]
            calas(*numbersNew) # 就可以这样去调用函数

    # 关键字参数
        # 可变参数允许你传入0个或任意个参数 这些可变参数在函数 调用时会组成一个tuple  而关键字参数允许你传入0个或任意个含参数名的参数 这些关键字参数在函数内部会组成一个dict
            def person(name, age ,**kw):
                print('name:',name,'age:',age,'other:',kw)
        # 函数person除了可以接收必选参数name与age之外还可以接收关键字参数kw,在调用函数 可以只传入必传参数
            person('小彭',18)
        # 也可以传入一些关键字参数
            person('小彭',18,city='东莞')
            person('小彭',18,city='东莞',job = '文案')
        # 和可变参数一样 如果已经有了一个dict
            personDict = {
                'city':'东莞',
                'job':'文案'
            }
            person('小彭',18,**personDict) 
            # **personDict表示把personDict这个dict的所以key-value用关键字参数传入到函数的**kw参数,kw将获得一份dict,但这是personDict的拷贝,修改kw并不会影响到原来的personDict

        # 命名关键字参数
            # 对于关键字参数 调用者可以随便传 至于传了什么 要在函数内部用in去判断 命名关键字参数 直接限制了只接收指定的关键字参数
                def personA(name, age, *, city, job): #这样就表示关键字参数只接收city与job
                    print(name, age, city, job)

            # 如果函数定义中已经有一个可变参数 那后面的命名关键字参数就不用加*了
                def personB(name, age, *arges, city, job):
                    print(name, age, args, city, job)

            # 命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错
        
        '''
            小结:
                1、*args 是可变参数 arges接收的是一个tuple
                2、可变参数既可以直接传入fun(1,2,3) 也可以先定义一个tuple或list再fun(*tuple)调用
                3、**kw 是关键字参数 kw接收的是一个dict
                3、关键字参数既可以直接传入fun(a=1,b=2) 也可以先定义一个dict再fun(**dict)调用
        '''
    '''
         
        2020-6-11 23:23 今晚任务结束~~~~~

    '''
# --------------------------- 时间分割线 -------------------------------------

# 切片(slice) 可以用来截取list、tuple以及字符串等 作用可以相当于php的array_slice、sub_str等函数
    aList = ['小红','小明','小黄','小花'] # 定义一个list 用切片获取除第一个同学外的同学名单
    bList = aList[1:3] # 切片操作符 : 前面参数指的是开始截取的元素索引(不输入的话默认0) 后面参数指的是截取长度(不输入的话默认到最后)其实就是mysql的start与limit 但是切片可以从后面往前面截取 最后一个元素的索引是-1 记住这些就可以了 

# 迭代(作用类似于php数组的foreach) 在Python中 迭代是通过for in来完成的 Python中for循环的抽象程度比较高 因为Python的for循环不仅仅用在list或者tuple上 还可以用在其他的可迭代对象上
    aDict = {'a':1,'b':2,'c':3} #定义一个dict
    #输出key
    for key in aDict:
        print(key) #输出'a','b','c'
    #输出value
    for value in aDict.values():
        print(value) #输出1,2,3
    #同时输出key与value (很常用)
    for key,value in aDcit.items():
        print(key,'=',value) #输出 a=1,b=2,c=3
    # 字符串也是可以作为迭代对象的
    aStr = 'abcd'
    for value in aStr:
        print(value) # 输出a,b,c,d
    # 当我们使用for循环时 只要作用于一个可迭代对象for循环就能正常运行 不太关心这个对象是list还是字符串或其他 那么如何判断一个对象是否可以迭代呢? 用collections模块的Iterable类型判断
    from  collections import Iterable
    print(isinstance('abc',Iterable)) #输出True
    print(isinstance(123,Iterable)) #输出False
    
    # 最后一个问题 如果list要实现下标与值的循环要怎么办 Python内置的emumerate函数可以将list变成索引-元素对
    aList = ['a','b','v']
    for key,value in enumerate(aList): #注意这里不需要.items()
        print(key,'-',value)
    
# 列表生成式 Python内置的非常简单却强大的可以用来创建list的生成式
    aList = list(range(1,6)) #生成1到10的list[1,2,3,4,5]
    # 如果要生成 [1*1,2*2,3*3]的怎么办 先想到的是循环
    aList = []
    for i in list(range(1,6)):
        aList.append(i*i)
    # 这种方式太繁琐 通过列表生成式一句代码就能生成 
    aList = [x*x for x in range(1,6)]
    print(aList) #输出[1,4,9,16,25]
    # 还可以在for循环后面加判断
    aList = [a*a for a in range(1,6) if a%2==0]
    print(aList) #输出[4,16]
    # 还可以通过2层循环生成2个集合的全排列
    aList = [a+b for a in 'abc' for b in 'xyz' if b=='x']
    print(aList) #输出['ax','bx','cx']
    # 三层以上的很少用但原理都是一样的
    '''
        再说下列表生成式中if else的用法 
        [x for x in range(1, 11) if x % 2 == 0] 这个能正常筛选出偶数
        [x for x in range(1, 11) if x % 2 == 0 else 0] 这个会报错 
        因为for后面的if是一个筛选条件 不能带上else 我们可以尝试将if else写在for前面
        [x if x % 2 == 0 else 0 for x in range(1, 11)]
    '''
    aList = [a if a%2==0 else 0 for a in range(1,6)]
    print(aList) # 输出[0,2,0,4,0]

# 生成器 通过列表生成式我们可以快速生成一个list 但是由于内存限制 列表时的容量也是有限的 而且当创建一个百万级元素的list时 如果只用到前面几个元素 这么大的空间就白白地浪费了 所以如果列表的元素可以通过某种固定的算法计算出来 那么我们是否可以在循环的过程中不断的推算后续的元素 这样就不必创建完成的list 从而节省空间 在Python中 一边循环一边计算的的机制 叫生成器 (generator)
    # 要创建一个generator 有很多种方式 最简单的一种是将列表生成式的[] 改为() 就创建了一个generator
    aGenerator = (x for x in range(1,6)) #这样就生成了一个generator 但list可以直接打印 generator要这么打印呢?如果要一个个打印出来可以通过next(aGenerator)打印 但这种方式打印到最后没有更多的元素时会报错 正确的打印方式是通过for循环 因为generator也是一个可迭代对象啊
    for value in aGenerator:
        print(value) #输出1,2,3,4,5 这样也不用关心到了最后一个会报错的问题
    # 但并不是所有的生成算法都那么简单 当一个list的生成算法比较复杂不能通过列表生成式去生成list的时候 我们还可以通过函数去生成
    # 比如比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
    # 1, 1, 2, 3, 5, 8, 13, 21, 34, ... 这种列表生成式处理不了 但通过函数我们却能简单的实现它
    def flb(max):
        n,a,b = 0,0,1
        while n < max: 
            print(b)
            a,b = b,a+b
            n = n + 1
        return 'done'
    flb(5) #输出1,1,2,3,5 
    # 仔细观察其实flb函数中是定义了斐波拉契数列的推算规则 这种逻辑就很像generator 要将flb函数变成generator 只需要将print(b)改为yield b 就可以了
    # 这就是定义generator的另一种方式了
    def flb(max):
        n,a,b = 0,0,1
        while n < max:
            yield b
            a,b = b,a+b
            n = n + 1
        return 'done'
    # 如果一个函数定义中包含了yield关键字 那么它就不是一个简单的函数了 而是一个generator
    # 同样我们使用for循环来调用generator 但这里我们发现拿不到generator的return语句返回值 如果想要拿到return语句的返回值 必须捕获 StopIteration异常 返回值包含在StopIteration的value中
    g = flb(6)
    while True:
        try:
            x = next(g)
            print('g:',x)
        except StopIteration as e:
           print('Generator return value:', e.value)
           break
    '''

        2020-6-14 1:17  结束了一场在台风天由于吃饱了睡不着而跨天的学习 睡觉~~
    
    '''

# 迭代器 
    '''
        可以作用于for循环的数据类型有以下几种
            一类是数据集合类型:list、tuple、dict、set、str等
            一类是generator
        这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
        可以使用isinstance()判断一个对象是否为可迭代对象
        而生成器不但可以作用于for循环 还能被next()函数不断调用并返回下一个值 知道最后抛异常表示没有后面的值了
        可以被next()函数不断调用并返回下一个值的对象成为迭代器:Iterator
        同样也可以通过isinstance()函数来判断一个对象是否为Iterator对象
        生成器都是Iterator对象 但list、tuple、dict、str虽然是Iterable 但不是Iterator 但可以通过iter()函数将Iterable转为Iterator
    '''

# 函数式编程 
    # 高阶函数 
        # map/reduce Python内建了map与reduce函数
            # map() 函数接收2个参数 一个是函数 一个Iterable map将传入的函数依次作用到序列的每个元素 并把结果作为新的Iterator返回 例:
                def f(x):
                    return x*x
                aList = map(f,list(range(1,11)))
                aList = list(aList)
                print(aList) # 输出 [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
                # 再例如 将list里面的所有元素转成str
                aStr = list(map(str,list(range(1,11))))
                print(aStr) #输出 ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
            # reduce() 同样接收2个参数 reduce把结果和继续和下一个元素做积累计算 其效果就是
            # reduce(f,[x1,x2,x3,x4]) = f(f(f(x1,x2),x3),x4) 例如将一个序列[1,2,3,4,5]转成整数12345
                from functools import reduce
                def f1(x,y):
                    return x*10+y
                aReduce = reduce(f1,list(range(1,6)))
                print(aReduce) #输出 12345
            
        # filter filter()函数用来过滤序列 filter函数也接收一个函数与一个序列 filter把传入的函数依次作用于序列 但根据函数返回值是True还是False俩决定保留还是遗弃该元素
            # 例:保留一个list中的偶数 
            def is_o(x):
                return x%2 == 0
            aList = list(filter(is_o,list(range(1,11)))) #要注意filter函数返回的是一个Iterator,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list
            print(aList) #输出[2, 4, 6, 8, 10]
        
        # sorted 排序算法 无论是冒泡还是快速排序 排序的核心都是比较两个元素的大小 
            # 通过sorted对list排序 
            aList = sorted([10,8,97,-5,56,99])
            print(aList) #输出 [-5, 8, 10, 56, 97, 99]
            # 还可以传入一个key函数来实现自定义的排序
            aList = sorted([10,8,97,-5,56,-99],key=abs)
            print(aList) #输出[-5, 8, 10, 56, 97, -99]
        
    # 返回函数(闭包) 告诫函数除了可以接收h函数作为参数外 还能将函数作为返回值
        # 实现一个可变参数的求和 
        def my_sum(*args):
            sum = 0
            for x in args:
                sum = sum + x
            return sum
        # 但是如果不需要立即求和 而是在后面的代码中根据需求再计算 
        def lazy_sum(*args):
            def my_sum():
                total = 0
                for x in args:
                    total = total + x
                return total
            return my_sum
        f = lazy_sum(1,2,3,4,5)
        print(f) # 输出.my_sum at 0x000000000269ACA0>
        print(f()) # 输出15

        # 返回一个函数时 牢记该函数未被执行 所以返回函数中不要引用任何可能会变化的变量
    
    # 匿名函数 当我们将函数作为参数去传值时 正常是先定义函数 但直接传入匿名函数会更方便 
        # 关键字lambda表示匿名函数  例如
        f = lambda x:x*x 
        print(f(2))  #输出4 由此可以看出 冒号前面的x表示函数的参数 冒号后面的x*x表示函数内部计算逻辑 匿名函数不需要写return
    

    '''

        2020-6-14 23:43  一个没有台风的台风天

    '''
#-------------------------时间分割线-------------------------------------------------

# 模块 
    # 作用域 在一个模块中我们可能会定义很多的函数与变量 有一些是可以给外部调用 但有一些我们不希望外部调用 Python通过 _ 前缀来实现变量与函数的私有化

# 面向对象编程   封装、继承与多态是 面向对象的三大特点

    # 类和实例 面对对象最重要的就是类(class) 与 实例 (Instance) 必须牢记类是抽象的模板 而实例是根据类创建出来的的一个个具体的对象 每个对象拥有相同的方法 但数据可能会不一样 在Python中 定义类 是通过class关键字
        class Student(object):
            pass
        # class 后面紧跟着类名 即Student 类名通常是大写开头的单词 紧接着是(object) 表示该类从哪个类继承下来 通常如果没有合适的继承类就使用object 因为这是所有类最终都会继承的类
        # 定义好类之后就可以通过该类创建出实例 创建实例是通过类名+()实现 
        studentOne = Student()
        print(studentOne) # 输出 <__main__.Student object at 0x00000000021E97C0>
        # 可以看到变量studentOne指向的就是一个Student实例 后面的0x00000000021E97C0是内存地址
        # 然后可以给实例对象绑定属性 例如 我们给这个一号学生绑定一个name
        studentOne.name = '小明'
        print(studentOne.name) # 输出小明
        # 由于类起到了一个模板的作用 因此我们可以在创建实例的时候 把一些我们认为必须绑定的属性强制写进去 通过定义一个__init__方法 
        class Student(object):
            def __init__(self,name,age,score):
                self.name = name
                self.age = age
                self.score = score
        # __init__方法前后都有2个下划线 第一个参数一定是self 表示实例本身 有了__init__方法 创建实例的时候就不能传入空的参数 但self参数不用传

    #  数据封装  面对对象编程的一个重要特点就是封装 其实就是将内部方法封装起来 外部调用这个方法时不需要知道方法的内部实现逻辑 只需要知道传什么参数 会得到什么结果就行了
    
    # 访问限制 在类中 如果希望内部属性或方法不被外部直接调用 那么定义属性或方法时请在前面加上 _ 下划线 在Python中 实例的变量名如果以下划线_开头 就变成了一个私有变量 只有内部可以使用 外部不能直接调用
        class Student(object):
            def __init__(self,name,score):
                self._name = name
                self._score = score
            
            def print_score(self):
                print('%s:%s' %(self._name,self._score))
        studentOne = Student('小明',99)
        studentOne.print_score() # 输出 小明:99
    
    # 继承与多态 
        # 继承 当我们定义一个class的时候 可以从某个现有的class继承 新的class称为 子类 而被继承的class 称为 父类、基类或超类
        # 定义一个叫 human 的类 有一个run()方法可以打印
        class Human(object):
            def run(self):
                print('人类在奔跑......')
        # 当我们需要编写 Boy或Girl类的时候 就可以从Human类继承出来
        class Boy(Human):
            pass
        class Gril(Human):
            pass
        # 继承有什么好处 当然是获得父类的全部财产(功能)啊!!! 
        boy = Boy()
        boy.run() # 输出 人类在奔跑......

        girl = Girl()
        girl.run() # 输出 人类在奔跑......
        # 如果父类的方法满足不了子类 子类完全可以重写父类的方法 比如
        class Boy(Human):
            def run(self):
                print('男孩在奔跑......')

        class Girl(Human):
            def run(self):
                print('女孩在奔跑......')
        boy = Boy()
        boy.run() # 输出 男孩在奔跑

        # 当子类与父类都存在相同的run方法时 我们就说 子类的run()覆盖了父类的run() 在代码运行时 总会调用子类的run() 这样 我们就获得了继承的另一个好处 多态
        # 多态 要理解什么是多态 我们首先还要对数据类型进行一个说明 当我们定义class的时候 实际上就定义了一种数据类型 和自带的list、tuple、dict没什么2样
        # 判断一个变量是什么类型我们可以通过isinstance()函数
        isinstance(boy, Boy) # 输出True
        isinstance(boy, Human) # 输出True boy既是男孩也是人类
        # 所以 在继承关系中 如果一个实例的数据类型是某个子类 那它的数据类型也可以看做是父类 反过来就不行 男孩可以被看作人类 但人类不能被看作男孩
        # 要理解多态的好处 我们再定义一个函数 该函数接收一个Human类型的变量
        def run_twice(Human):
            Human.run()
            Human.run()
        # 当我们传入Human实例时
        run_twice(Human()) 
        # 输出 人类在奔跑...... 人类在奔跑......
        # 当我们传入Boy实例时
        run_twice(Boy()) # 输出 男孩在奔跑...... 男孩在奔跑......

        # 整理一下以上的代码 就是: 
        class Human(object):
            def run(self):
                print('人类在奔跑')
        class Boy(Human):
            def run(self):
                print('男孩在奔跑')
        class Girl(Human):
            def run(self):
                print('女孩在奔跑')
        class Stone(object):
            pass
        def run_twice(Human):
            Human.run()
            Human.run()
        run_twice(Human()) #人类跑2次
        run_twice(Boy()) # 男孩跑2次
        run_twice(Girl()) # 女孩跑2次
        run_twice(Stone()) # 石头没有继承人类也没有run()方法 所以它不会跑(报错)
        '''
            所以 多态的意思就是:对于一个变量 我们只需要知道它是Human类型 无需知道它的子类 就可以放心的调用run() 而具体的 run()方法是作用在Human、Boy是Girl对象上 由运行时该对象的确切类型决定 这就是多态的威力 调用方只管调用 不管细节 而我们再新增一个Human的子类时 只要确保run()方法编写正确 不用管原来的代码是怎么调用的
        '''
        '''

            2020-6-15 23:30 万周开头难 周一即将结束

        '''
#-----------------------时间分割线------------------------------------------------

# 面对对象高级编程
    # 使用__slots__  
        # 正常情况下 当我们定义了一个class 创建了一个class实例之后我们可以动态的给这个实例绑定任何的属性与方法
        class Student(object):
            pass
        # 给绑定一个属性
        s = Student()
        s.name = '小明'
        # 绑定一个方法 
        def set_age(self,age): # 先定义一个函数
            self.age = age
        from types import MethodType
        s.set_age = MethodType(set_age,s)
        s.set_age(25)
        print(s.age) # 输出25
        # 但是你给这个实例绑定的方法 对其他的实例是不起作用的
        # 但是 如果我们要限制实例的属性怎么办 只允许对Student实例添加name与age属性
        # 为了达到限制的目的 Python允许在定义class 的时候定义一个特殊的__slots__变量 来限制该class实例能添加的属性
        class Student(object):
            __slots__ = ('name','age') # 用tuple类型定义允许绑定的属性名称
        # 使用__slots__要注意 __slots__定义的属性只对当前类实例起作用 对继承的子类是不起作用的
    
    # 使用@property 
        # 在绑定属性时 如果我们直接把属性暴露出去 虽然写起来简单 但是没办法校验参数 导致属性值可以随便修改
        s = Student()
        s.score = 9999  # 这显然不符合逻辑 为了限制score 可以通过一个set_score()方法来设置成绩 再通过一个get_score()方法来获取成绩 这样在set_score()方法里就能校验参数

        class Student(object):
            def get_score(self):
                return self.score
            
            def set_score(self,score):
                if not isinstance(score,int):
                    raise ValueError('成绩应该是整数')
                if score < 0 or score > 100:
                    raise ValueError('成绩应该在0-100')
                self.score = score
        # 但是 上面的方法调用又显得复杂 没有直接用属性那么简单 有没又能检查参数 又能通过属性这样简单的方法来访问变量呢 
        # Python内置的@property装饰器就是负责把一个方法变成属性调用
        class Student(object):
            @property
            def score(self):
                return self._score # 这里的_score表示score是私有变量
            @score.setter
            def score(self, value):
                if not isinstance(value,int):
                    raise ValueError('成绩应该为整数')
                if value < 0 or value > 100 :
                    raise ValueError('成绩应该在0-100之间')
                self._score = value
        s = Student()
        s.score = 60
        print(s.score) # 输出60
        s.score = 9999 # 输出提示成绩应该在0-100之间
        # 还可以定义只读属性 不设置setter就是只读属性
    
    # 多重继承 简单点就是可以继承多个类 而不是像php或java这样只能继承一个类

    '''

        2020-6-16 23:32 今晚微风 效率低下

    '''

#----------------------------时间分割线-----------------------------------------------

# IO编程
    # 文件读写 读写文件是最常见的IO操作 在读写文件前 我们必须了解 在磁盘上读写文件的功能都是操作系统提供的 现代操作系统不允许普通的程序直接操作磁盘  所以 读写文件就是请求操作系统打开一个文件对象(通常描述为文件操作符) 然后通过操作系统提供的接口从这个文件对象中读取数据 或者将数据写到这个文件对象中

        # 读文件 使用Python内置的open()函数 传入文件名与标识符
            f = open('/haha.txt','r') # r表示读 如果该文件不存在 就会报一个IOError的错误 并且给出错误码和详细的信息告诉你这个文件不存在
            # 如果文件打开成功 调用read()方法可以一次性读取文件的所有内容 Python将内容读到内存中 用一个str对象保存
            content = f.read()
            print(content)  # 输出 这个一个哈哈哈文件
            # 最后是调用close()方法将文件关闭 文件使用完之后必须关闭 因为文件对象会占用操作系统的资源 并且操作系统同一时间能打开的文件数量也是有限的
            f.close()
            # 由于文件读写随时都有可能产生IOError 一旦出错 后面的f.close()就不会被调用 为了保证无论是否出错都能正确的关闭文件 我们可以用try...finally来实现
            try:
                f = open('haha.txt','r')
                print(f.read())
            except expression as identifier:
                pass
            finally:
                f.close()
            # 但每次都这么写太繁琐了 所以Python引入了with语句来帮我们自动调用close()
            with open('haha.txt','r') as f:
                print(f.read())
            # 这个效果与前面的try 是一样的 但代码更简洁并且不用调用close()方法
            # 调用read()会一次性读取文件内容 但如果文件有个10G大小 内存就爆了 为了保险起见 不确定大小的文件 可以反复调用read(size)方法 限制每次最多读取size字节的内容 另外 调用readline()可以每次读一行内容 调用readlines()可以一次性读取所以内容然后以list方式返回 因此要根据需求决定怎么用
            # 如果能确定文件大小 直接用read() 不确定文件大小就用 read(size) 如果是配置文件用readlines()最佳
            with open('haha.txt','a') as f:
                content = f.readlines()
                for text in content:
                    print(text) # b'\xff\xd8\xff\xe0\x00\x10JF......' 十六进制表示的字节
            
            # file-like Object 
                # 像open()函数返回的这个有个read()方法的对象 在Python中被称为file-like Object。除了file外 还可以是内存的字节流 网络流和自定义流等 file-like Object不要求从特定类继承 只要有个read()方法就行
                # StringIO 就是在内存中创建的file-like Object 常用于临时缓冲
            # 二进制文件 
                # 前面将的都是读取文本文件 并且是utf-8编码的文本文件 要读取二进制文件 比如图片、视频等 用'rb'模式打开文件即可
                with open('3.jpg','rb') as image:
                    print(image.read())
            
            # 字符编码 
                # 要读取非UTF-8编码的的文本文件 需要给open()函数传入encoding参数 例如读取GBK编码的文件
                with open('haha.txt','r',encoding='gbk') as f:
                    print(f.read())
                # 遇到一些编码不规范的文件 你还可能会遇到UnicodeDecodeError 因为在文本中可能掺夹了一些非法的字符 遇到这种情况 open()函数还接收一个errors参数 表示遇到编码错误之后怎么处理 最简单的方式就是直接忽略 
                with open('haha.txt','r', errors='ignore') as target:
                    print(target.read())
        
        # 写文件 写文件与读文件的操作是一样的 唯一区别是操作open函数时 传入标志符'w'或者'wb'表示写文本文件或写二进制文件
        with open('haha.txt','w') as p:
            p.write('嘿嘿嘿我是后面加入的') # 要写入特定编码的文本文件 请给open()函数传入encoding参数 将字符串转成特定的编码
        # 以w或者wb模式写入文件时 如果文件已存在 会直接覆盖 相当于将之前的内容删掉重新写入 如果希望追加到文件末尾可以通过'a'(append)模式
        # 所有模式的定义与含义 https://docs.python.org/3/library/functions.html#open
    
    # StringIO和BytesIO 
        # StringIO 很多时候 数据读写不一定是文件 也可以在内存中读写 StringIO顾名思义就是在内存中读写str
            # 要将str写入StringIO 我们需求先创建一个StringIO 然后像文件一个写入即可
            from io import StringIO
            f = StringIO()
            f.write('hello StringIO')
            print(f.getvalue()) # getValue()方法用于获取写入后的str
            # 要读取StringIO 可以用一个str初始化StringIO 然后像读文件一样企业读取就好了
            f = StringIO('hello\n StringIO')
            print(f.read())
        
        # BytesIO 
            # StringIO只能操作字符串 如果要操作二进制文件 就需要用到BytesIO
            # BytesIO实现了在内存中读写bytes 我们创建一个BytesIO 然后写入一些bytes
            from io import BytesIO
            f = BytesIO()
            f.write('中文'.encode('utf-8'))
            print(f.getvalue()) # 输出 b'\xe4\xb8\xad\xe6\x96\x87'
            # 和StringIO类似 可以用一个bytes初始化BytesIO 然后像读文件一个去读
            f = BytesIO(b'\xe4\xb8\xad\xe6\x96\x87')
            print(f.read())

    # 操作文件与目录
        # Python内置的os模块可以直接调用操作系统提供的接口函数
        import os
        print(os.name) # 输出操作系统名称 如果是posix,说明系统是Linux、Unix或Mac OS X,如果是nt,就是Windows系统。
        print(os.uname()) # 报错 因为在windows上不提供这个函数
        print(os.environ) # 打印全部环境变量
        # 获取某个环境变量的值 可以直接os.environ.get('key)
        print(os.environ.get('APPCAN_PATH','没有找到适合返回的默认值'))

        # 操作文件与目录的函数一部分放在 os模块中 一部分放在os.path模块中 查看、创建与删除目录可以这么调用:
        path= os.path.abspath('.') # 查看当前目录的绝对路径
        # 在某个目录下创建一个新目录 首先将新目录的完整路径展示出来
        newPath = os.path.join(path,'newDir')
        os.mkdir(newPath)
        # 删掉一个目录
        os.rmdir(newPath) 
        # 将两个路径合成一个时 不要直接拼接字符串 而要通过os.path.join()函数 这样可以正确处理不同操作系统的路径分隔符
        # 在Linux/Unix/Mac下,os.path.join()返回这样的字符串:
        # part-1/part-2
        # 在windows下返回这样的字符串:
        # part-1\part-2
        # 同样的道理 要拆分路径时 也不要直接去拆字符串 而要通过os.path.split()函数 后一部分总是最后级别的目录或文件名
        # os.path.splitext() 可以直接获取到文件扩展名

    # 序列化
        # json
        import json
        d = dict(name = 'jon',age=18,score=80)
        print(json.dumps(d)) # 输出{"name": "ddd", "age": 18, "score": 88}
        # json.dumps()方法返回一个str
        # 将json反序列为Python对象
        json_str = '{"name": "ddd", "age": 18, "score": 88}'
        print(json.loads(json_str))

    '''

        2020-6-18 17:45 学习了一天 充实 准备下班~~
    
    '''
#---------------------时间分割线-----------------------------------

# 进程与线程
    # 操作系统相关知识:Unix/Linux操作系统提供一个fork()系统调用 普通的函数 调用一次 返回一次 但是fork()调用一次返回2次 因为操作系统自动把当前进程(成为父进程) 复制了一份(成为子进程) 然后 分别在父进程与子进程中返回 子进程永远返回0 而父进程返回子进程的ID 因为一个父进程有很多子进程 所以 父进程要记下所有子进程的ID 而子进程字需要调用getppid()就能拿到父进程的ID
    # 多进程
        # Python 的os模块封装了常见的系统调用 其中fork()就包含在内 
            # 下面这段代码只能在Unix或linux下执行 因为windows系统没有fork调用
            import os
            print('Process (%s) start' % os.getpid())
            pid = os.fork()
            if pid == 0:
                # 子进程 
                print('我的子进程%s 我的父进程是 %s' % os.getpid(),os.getppid())
            else :
                print('我是父进程%s,我创建了一个子进程%s' %os.getpid(),pid)
        # multiprocessing
            # Python的multiprocessing模块提供了一个Process类来代表一个进程对象 下面的例子演示了启动一个子进程并等待其结束
            from multiprocessing import Process
            import os
            # 子进程要执行的代码
            def run_proc(name):
                print('运行子进程%s(%s)' %(name,os.getpid()))
            if __name__ == '__main__':
                print('父进程%s' %os.getpid())
                p = Process(target=run_proc,args=('test',))
                print('子进程即将启动')
                p.start()
                p.join()
                print('子进程结束')
            ''' 输出
                    父进程14944
                    子进程即将启动
                    运行子进程test(14448)
                    子进程结束
            '''
            # 创建子进程时 只需要传入要执行的函数以及函数的参数 创建一个Process实例 用start()方法启动
            # join()可以等待子进程结束后再继续往下运行 通常用于进程间的同步

            # Pool 如果需要创建大量的子进程 可以用进程池的方式批量创建子进程
            from multiprocessing import Pool
            import os,time,random
            
            def long_time_task(name):
                print('运行任务%s(%s)' %(name,os.getpid()))
                start = time.time()
                time.sleep(random.random()*3)
                end = time.time()
                print('任务%s运行时间为:%0.2f' %(name,(end-start)))
            
            if __name__ == '__main__':
                print('父进程为%s' % os.getpid())
                p = Pool(4)
                for i in range(5):
                    p.apply_async(long_time_task,args=(i,))
                print('等待全部子进程结束')
                p.close()
                p.join()
                print('全部子进程运行结束')
            ''' 输出
                    父进程为7224
                    等待全部子进程结束
                    运行任务0(2252)
                    运行任务1(6488)
                    运行任务2(4504)
                    运行任务3(9572)
                    任务3运行时间为:0.08
                    运行任务4(9572)
                    任务1运行时间为:0.51
                    任务2运行时间为:1.48
                    任务4运行时间为:1.62
                    任务0运行时间为:2.11
                    全部子进程运行结束
            '''
            # 对Pool对象调用join()方法会等待所有的子进程执行完毕 调用join()之前必须调用close() 调用close()之后就不能继续添加Process了
            # 请注意输出的结果 task0,1,2,3是立刻执行的 而task4是等到task3运行完了之后才执行的 这是因为Pool的默认大小是4 因为最多只能同时执行4个子进程 如果p = Pool(5) 就能同时执行5个子进程

        # 子进程
            # 很多时候 子进程并不是自身 二是一个外部进程 我们创建了子进程后 还要控制子进程的输入与输出
            # subprocess模块可以让我们很方便的启动一个子进程 然后控制其输入输出
            import subprocess
            print('$ nslookup www.python.org')
            r = subprocess.call(['nslookup','www.python.org'])
            '''
                输出
                    $ nslookup www.python.org
                    服务器:  public1.114dns.com
                    Address:  114.114.114.114

                    非权威应答:
                    名称:    dualstack.python.map.fastly.net
                    Addresses:  2a04:4e42:36::223
                            151.101.228.223
                    Aliases:  www.python.org
            '''
        # 进程间通信
            # 进程之间肯定是要通信的 Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据
            # 以Queue为例 在父进程中创建2个子进程 一个往Queue里写数据 一个往Queue里读数据
            from multiprocessing import Process,Queue
            import os, time, random
            # 写数据
            def write(q):
                print('写进程%s' % os.getpid())
                for value in ['a','b','c']:
                    print('往queue里写入%s' % value)
                    q.put(value)
                    time.sleep(random.random())

            # 读数据
            def read(q):
                print('读进程%s' % os.getpid())
                while True:
                    value = q.get(True)
                    print('从Queue中取出的值%s' % value)

            if __name__ == '__main__':
                # 父进程创建queue 并传给各个子进程
                q = Queue()
                pw = Process(target=write,args=(q,))
                pr = Process(target=read,args=(q,))
                # 启动子进程pw 开始写入数据
                pw.start()
                # 启动子进程pr 开始读数据
                pr.start()
                # 等待pw结束
                pw.join()
                # 子进程pr里面是一个死循环 无法等待其结束 只能强制终止
                pr.terminate()
                '''
                    输出
                    写进程8576
                    往queue里写入a
                    读进程11572
                    从Queue中取出的值a
                    往queue里写入b
                    从Queue中取出的值b
                    往queue里写入c
                    从Queue中取出的值c
        

                '''
# 关于进程与requests的一个小demo
# 采用多进程从本地另外一个项目拿数据回来 用一个list接收 然后统一写到txt文件里面
from multiprocessing import Pool
import requests,json,time

def get_company_data(companyId):
    url = 'http://zaa.com/index.php?s=/Index/test&company_id='+ str(companyId)
    ret = requests.get(url)
    ret = json.loads(ret.text)
    return ret['data']

if __name__ == '__main__':
    start = time.time()
    companyIds = [441,531,581,605,620,645,647,669]
    #companyIds = [441, 531, 581, 605]
    p = Pool(4)
    results = []
    for companyId in companyIds:
        results.append(p.apply_async(get_company_data, args=(companyId,)))
    print('任务进行中......')
    p.close()
    p.join()
    # 将读回来的数据写到一个txt文件中
    txtFile = open('客户欠款文件.txt','a',encoding='utf-8')
    dataList = []
    for res in results:
        companyCustomInfo = res.get()
        for cf in companyCustomInfo:
            string = '企业id:%s,企业名:%s,客户名称:%s,交易额:%s,欠、还时间差:%s,欠款次数:%s,回款周期:%s' % (
                cf['company_id'],cf['name'],cf['custom_name'],cf['amount_receivable'],cf['day'],cf['num'],cf['avg'])+'\n'
            txtFile.write(string)
    txtFile.close()
    end = time.time()
    print('任务完成!总耗时:',(end-start))

    # 多线程
        # 多任务可以由多进程完成 也可以由一个进程内的多线程完成 一个进程至少有一个线程
        # Python的标准库提供了2个模块: _thread 和 threading, _thread是低级模块 threading是高级模块 对_thread进行了封装 大多数情况下我们都使用threading
        # 启动一个线程就是把一个函数传入并创建Thread实例 然后调用start()开始执行
        import time,threading
        def loop():
            print('%s 正在运行...' % threading.current_thread().name)
            n = 0
            while n < 5:
                n = n + 1
                print('%s 输出 %s' % (threading.current_thread().name, n))
                time.sleep(1)
            print('%s 结束' % threading.current_thread().name)
        print('主线程%s 正在运行' % threading.current_thread().name)
        t = threading.Thread(target=loop, name='子线程')
        t.start()
        t.join()
        print('主线程%s结束' % threading.current_thread().name)
        '''
            主线程MainThread 正在运行
            子线程 正在运行...
            子线程 输出 1
            子线程 输出 2
            子线程 输出 3
            子线程 输出 4
            子线程 输出 5
            子线程 结束
            主线程MainThread结束
        '''
        # 由于任何进程会启动一个线程 我们把该线程成为主线程 主线程又可以启动新的子线程 threading模块有一个current_thread()函数 它可以返回当前线程实例 

        # lock 锁
            # 多进程与多线程最大的区别是 多进程中 同一个变量 各自有一份拷贝存在每个进程中 互不影响 而多线程中 所有变量由所有线程共享 所以 任何一个变量都可能被任何一个程序修改 因此 线程之间共享数据最大的危险在于多个线程将同一个变量修改 就给改乱了
            # 先来一个例子
            import time,threading
            money = 0
            def change_it(n):
                global money
                # 先加后减 最后money的结果应该为0
                money = money + n
                money = money - n 
            
            def run_thread(n):
                for i in range(100000):
                    change_it(n)

            t1 = threading.Thread(target=run_thread,args=(5,))
            t2 = threading.Thread(target=run_thread,args=(8,))
            t1.start()
            t2.start()
            t1.join()
            t2.join()
            print(money)
            # 多次运行之后 该程序会偶尔出现money值不为0的情况 由于线程的调度是由操作系统决定的 当t1 t2交替执行时 只要循环的次数够多 money最后的结果就不一定是0了 因为在高级语言中 一条语句在执行的过程中可能会被分为多条语句 例如:
            money = money + n # 会被分为2步 1、计算money+n 然后存到一个临时变量中 2、将临时变量的值赋给money
            # 当t1 t2交替执行时
            '''
            初始值 balance = 0

            t1: x1 = balance + 5  # x1 = 0 + 5 = 5

            t2: x2 = balance + 8  # x2 = 0 + 8 = 8
            t2: balance = x2      # balance = 8

            t1: balance = x1      # balance = 5
            t1: x1 = balance - 5  # x1 = 5 - 5 = 0
            t1: balance = x1      # balance = 0

            t2: x2 = balance - 8  # x2 = 0 - 8 = -8
            t2: balance = x2   # balance = -8

            结果 balance = -8
            '''
        # 为了确保money是正确的 就要给change_it上锁 当某个线程开始执行change_it()时其他线程不能同时执行change_it()只能等待锁被释放之后才能获得该锁然后去修改 无论多少线程 只有一个锁 所以不会造成修改的冲突 
        # 创建一个锁通过threading.lock()来实现
        # 下面通过上锁优化上面的代码 确保money最后一定是等于0
        import threading
        money = 0
        lock = threading.Lock()

        def change_it(n):
            global money
            money  = money + n
            money = money - n

        def run_thread(n):
            for i in range(1000000):
                lock.acquire() # 先获得锁
                try:
                    change_it(n)
                finally:
                    lock.release() # 释放锁
        t1 = threading.Thread(target=run_thread, args=(10,))
        t2 = threading.Thread(target=run_thread, args=(50,))
        t1.start()
        t2.start()
        t1.join()
        t2.join()
        print(money) # 无论运行多少次 循环多少次 money输出结果都是0
        # 锁的好处就是可以确保某段代码只能由一个线程从头到尾的执行 坏处当然也多 首先是阻止多线程的并发运行 包含锁的某段代码只能单线程的运行
    
    # ThreadLocal 快速获取当前线程的局部变量
    import threading
    local_student = threading.local()
    def process_student():
        # 获取当前线程关联的student:
        std = local_school.student
        print('Hello, %s (in %s)' % (std, threading.current_thread().name))

    def process_thread(name):
        # 绑定ThreadLocal的student:
        local_school.student = name
        process_student()

    t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
    t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
    t1.start()
    t2.start()
    t1.join()
    t2.join()


# 常用内建模块
    # datetime datetime是Python处理日期与时间的标准库
        # 获取当前日期与时间
            from datetime import datetime
            now =  datetime.now()
            print(now) # 输出2020-06-22 21:06:30.563283
        # 获取指定日期与时间
            dt = datetime(2020,6,22,19,7,59)
            print(dt) # 输出2020-06-22 19:07:59
        # datetime转成timestamp 用datestamp()方法
        # timestamp转成datetime 用fromtimestamp()方法
    
    # collections cellections是Python内建的一个集合模块 提供了许多有用的集合类
        # namedtuple
            # 我们知道tuple可以用来表示一个不变集合 例如一个点的二位坐标可以表示为
            p = (1,2)
            # 但是这看起来不像一个坐标 并且tuple要通过索引来获取元素 很麻烦 可以用namedtuple来试试
            from collections import namedtuple
            Point = namedtuple('Point',['x','y'])
            p = Point(1,2)
            print(p.x) # 输出1
            print(p.y) # 输出2
            # namedtuple是一个函数 我们用它来创建一个自定义的tuple对象 并且可以通过属性的方式而不是索引的方式去引用tuple的某个元素
            # 创建的Point对象tuple的子类

        # deque 
            # 使用list存储数据时 按索引访问元素很快 但是插入和删除元素就很慢了 因为list是线性存储 数据量大的时候 插入与删除效率都很低 
            # deque是为了高效实现插入与删除操作的双向列表 适用于队列与栈 
            from collections import deque
            q = deque(['a','b','c'])
            q.append('v')
            q.appeenleft('y')
            print(q) # 输出 deque(['y', 'a', 'b', 'c', 'v'])
            # deque除了实现list的append与pop之外 还实现了appendleft 与popleft 这样就可以很高效的往头部添加与删除元素了
        
        # defaultdict 
            # 使用dict时 如果一个引用的key不存在 就会报错 希望key不存在时 返回一个默认值就用defaultdict
            from collections import defaultdict
            dd = defaultdict(lambda: '不存在的')
            dd['key1'] = '手动赋值'
            print(dd['key1']) # 输出手动赋值
            print(dd['key2']) # 输出 不存在的

        # orderdict 
            # dict的key是无序的 对dict做迭代时 我们无法确认key的顺序 如果要保持key的顺序 可以用orderdict
            from collections import OrderedDict
            d = OrderedDict([('a',1),('b',2]) # Orderdict的key是按照插入的顺序排序 而不是key之间的排序
            # Orderdict可以实现一个先进先出的dict 当超出容量时 删除最早添加的key
        
        # ChainMap 

        # Counter Counter是一个简单的计数器 例如 统计字符出现的个数 
            from collections import Counter
            c = Counter()
            for ch in 'hello':
                c[ch] = c[ch] + 1
            print(c) # 输出 Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
            c.update('hi')
            print(c) # 输出 Counter({'h': 2, 'l': 2, 'e': 1, 'o': 1, 'i': 1})
        
    # hashlib Python的hashlib提供了常用的摘要算法 如果md5 sha1等 
        import hashlib
        md5 = hashlib.md5()
        pwd = '123456'
        md5.update(pwd.encode('utf-8'))
        hashPwd = md5.hexdigest()
        print(hashPwd) # 输出 e10adc3949ba59abbe56e057f20f883e
        sha1 = hashlib.sha1()
        sha1.update(pwd.encode('utf-8'))
        newPwd = sha1.hexdigest()
        print(newPwd) # 输出 7c4a8d09ca3762af61e59520943dc26494f8941b
    
    # urllib urllib提供了一系列用于操作url的功能
        # Get  urllib的request模块可以很方便的抓取url内容 也就是发生一个get请求到具体的页面 返回http的响应
        from urllib import request

        url = 'https://recsidebar.csdn.net/getSideBarRecommend.html'
        with request.urlopen(url) as f:
            data = f.read()
            print(f.status,f.reason)
            for k,v in f.getheaders():
                print('%s:%s' %(k,v))
            print(data.decode('utf-8'))
        
        # 模拟浏览器发生get请求 
        from urllib import request

        req = request.Request(url)
        req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
        with request.urlopen(req) as f:
            print('Status:', f.status, f.reason)
            for k, v in f.getheaders():
                print('%s: %s' % (k, v))
            print('Data:', f.read().decode('utf-8'))

        # POST 
        from urllib import request,parse
        post_data = parse.urlencode([
            ('name','12345'),
            ('pwd','12456')
        ])
        req = request.Request(url)
        req.add_header('Origin', 'https://passport.weibo.cn')
        req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25')
        req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F')

        with request.urlopen(req,data=post_data.encode('utf-8')) as f:
            print('status',f.status)

# 常用第三方模块 
    # Pillow 
    from PIL import Image
    im = Image.open('test.jpg')
    w,h = im.size
    print('原画尺寸为%s %s'%(w,h)) # 输出 原画尺寸为500 300
    # 缩小一半
    im.thumbnail((w//2,h//2))
    print('Resize image to: %sx%s' % (w//2, h//2))
    # PIL的ImageDraw提供了一系列的绘图方法 让我们可以直接绘图

    '''
    
        2020年6-22 23:03 很多知识都要写了实际的案例才能真正体会其用处、以及踩它的坑

    '''
        
    

 

你可能感兴趣的:(Python学习笔记)