精华内容
下载资源
问答
  • Python知识点整理

    2020-03-12 22:10:54
    Python知识整理1.了解pythonpython工作方向Python语言特征环境部署基本知识:变量、语句、缩进、注释输入输出操作编程文件化及执行数据类型运算符 1.了解python python工作方向 web开发 自动化运维 数据分析 人工...

    1.了解python

    python工作方向

    web开发
    自动化运维
    数据分析
    人工智能

    Python语言特征

    python是一款动态解释性的强数据类型的编程语言

    环境部署

    可选择python3的最新版,其最低版本不小于3.5
    官网地址:https://www.python.org
    注:widows系统中,配置完成python3环境后,需要配置IDE的工具:vscode(微软官网下载,需要在线配置环境)、pycharm(官网下载社区版)

    基本知识:变量、语句、缩进、注释

    1.变量命名规则
    (1)标识符只能由 字母、数字或下划线组成
    (2)标识符的第一个字符不能是 数字
    (3)标识符区分大小写
    (4)关键字不能声明为标识符
    like:class、try、import、print、return、while
    命名规范:小写字母,单词之间用_分割,容易分辨。
    2.语句
    python语句组成一个python程序,一行编写一个语句。
    like:print(’Hello world‘)
    不需要像C/C#那样在语句后面必须又分号(;)表示结束。但是多个python语句在同一行时,必须使用分号(;)表示结束。
    like:print(’hello‘);print(’world‘);
    语句特征:执行修改操作
    表达式是用来计算的,语句是用来执行的
    3.缩进
    在代码前面添加空格,这样做可以使程序更有层次感、更有结构感、整洁明了、方便阅读。
    缩进不是随意的,平级之间的语句行的缩进必须相同。
    4.注释
    单行注释: #
    多行注释:三引号(三个单引号或者三个双引号)

    输入输出操作

    输入:python2.7
    name=raw_input(“What is your name?”)
    python3.X
    name=input(“What is your name?”)

    输出:加粗样式python2.7
    print name
    python3.X
    print(name)

    编程文件化及执行

    模块名命名规范:
    小写字母,单词之间用_分割
    变量命名规则:
    (1)标识符只能由字母、数字或下划线组成
    (2)标识符的第一个字符不能是数字
    (3)标识符区分大小写
    (4)关键字不能声明为标识符
    注释:
    单行注释: #
    多行注释:三引号(三个单引号或者三个双引号)

    数据类型

    六种标准数据类型:

    number(数字):int、float、bool
    str(字符串)
    list(列表)
    tuple(元组)
    set(集合)
    dict(字典)

    不可变数据类型:

    number、str、tuple

    可变数据类型:

    list、dict、set

    number(数字):

    支持int、float、bool、complex(复数)
    str(字符串):
    python中的字符串用单引号(’)或者双引号(“)括起来,同时使用反斜杠(\转移特殊字符)。

    set(集合):

    无序不重复元素的序列
    基本功能是进行成员关系测试和删除重复元素
    可以使用大括号{}或者set()函数创建集合,
    创建一个空集合必须用set()而不是{},因为{}是用来创建一个空字典。

    list(列表):

    列表元素 可重复,可修改
    列表是写在方括号([])之间、用逗号分隔开相邻的元素。
    索引值以0开始值-1为从末尾的开始位置。
    加号(+)列表连接运算符星号(*)重复操作。

    dict(字典):
    列表是有序的对象集合,字典是无序的对象集合。
    两者的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。
    字典是一种映射类型,用”{}“标识,他是一个无序的键(key):值(value)对集合
    键(key)必须使用不可变类型
    在同一字典中,键(key)必须是唯一的
    方法:
    内置的type()函数可用来查询对象类型
    isinstance来判断。

    运算符

    Python支持一下类型运算符:

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 成员运算符
    • 身份运算符
    算术运算符
    + 加,两个对象相加
    减,得到负数或是一个数减去另一个数
    * 乘,两个数相乘或是返回一个被重复若干次的字符串
    / 除,x除以y
    % 取模,返回除法的余数
    ** 幂,返回x的y次幂
    // 取整数,返回商的整数部分
    比较运算符
    == 等于,比较对象是否相等
    != 不等于,比较两个对象是否不相等
    > 大于,返回x是否大于y
    < 小于,返回x是否小于y。所有比较运算符返回1表示真;返回0表示假。分别于特殊的变量True和False等价
    >= 大于等于,返回x是否大于等于y
    <= 小于等于,返回x是否小于等于y
    赋值运算符
    = 简单的赋值运算符,c=a+b,将a+b的运算结果赋值为c
    += 加法赋值运算符,c+=a等效于c=c+a
    -= 减法赋值运算符,c-=a等效于c=c-a
    *= 乘法赋值运算符 ,c*=a等效于c=c*a
    /= 除法赋值运算符,c/=a等效于c=c/a
    %= 取模赋值运算符,c%=a等效于c=c%a
    **= 幂赋值运算符,c**=a等效于c=c**a
    //= 取整除赋值运算符,c//=a等效于c=c//a
    逻辑运算符
    and x and y ,布尔与,如果x为false,x and y返回false,否则返回y的计算值。
    or x or y ,布尔或,如果X为true,返回X的值,否则返回Y的值
    not not x ,
    成员运算符
    in 如果在指定的序列中找到值返回true,否则返回false。
    not in 如果在指定的序列中没有找到值返回true,否则返回false。
    身份运算符
    i s is是判断两个标识符是不是引用自一个对象,x is y,类似于id(x)=id(y),如果引用的是同一个对象则返回true,否则返回false。
    is not is not是判断两个标识符是不是引用自不同的对象,x is not y,类似于id(x)!=id(y),如果引用的不是同一个对象则返回结果true,否则返回false。
    运算符优先级
    ** 指数(最高优先级)
    ~ + - 按位翻转,一元加号和减号(+@,-@)
    * / % // 乘,除,取值和取整除
    + - 加法减法
    》《 右移左移运算符
    & 位**“AND”**
    ^ 和竖杠 位运算符
    <= < > >= 比较运算符
    < > == != 等于运算符
    = %= /= //= -= += *= **= 赋值运算符
    is is not 身份运算符
    in not in 成员运算符
    and or not 逻辑运算符

    1.python可以同时为多个变量赋值,如a,b=1,2。
    2. 一个变量可以通过赋值只想不同类型的对象。
    3. 整数的除法包含两个运算符: / 发挥一个浮点数,// 返回一个整数。
    4. 在混合算时,python会把整型转换为浮点数。

    条件语句

    python条件语句是通过一条或多条语句的执行结果(truth或者false),来决定执行的代码块。控制结构图
    if 嵌套

    循环语句

    python中的循环语句有 for 和 while。
    循环语句控制结构图
    一般形式
    break 语句作用 : 跳出本层循环体。
    如果你从for或while循环中终止,任何对应的循环else块将不执行。
    continue语句作用 : 跳过本次循环体剩余语句
    pass语句作用 : 保持程序结构的完整性

    在这里插入图片描述
    循环中的else子句仅在没有调用break时,执行。

    range()函数可创建一个整数列表,一般用在for循环中。
                      函数语法 :
                      range(start,stop[,step])
                      参数说明 :
                       start:计数从start开始。默认是从0开始。
                             例如;range(5)等价于range(0,5);
                       stop:计数到stop结束,但不包括 stop。
                             例如:range(0,5),是[0,1,2,3,4]没有5
                       step:步长,默认为1。
                            例如:range(0,5)等价于range(0,5,1)

    序列

    序列是最基本的数据结构。
    序列中的每个元素都分配一个数字——它的位置或索引,第一个索引是0,第二个索引是1,以此类推。

    常见序列有列表、元组、字符串。

    序列可以进行的操作,有索引,切片,加,乘,检查成员。
    (python中内置了确定序列长度,最大值元素和最小值元素的放法)

    列表是最常用的Python数据类型它可以作为一个方括号内的逗号分割值出现
    列表的数据项不需要具有相同的类型,可重复。
    例: list_1=[‘a’,1[1,2]]

    通用序列操作

    1. 索引
    2. 切片
    3. 序列相加
    4. 乘法
    5. 常用内置函数:长度len() 最小值min() 最大值max()

    列表

    列表元素特点可修改,可重复,可嵌套,支持多种数据类型

    1. 函数list()
    2. 基本列表操作:
      (1)创建:name=list()或[ ] 例如: [1,2],[1,[12]
      (2)增: append extend
      (3)删:del clear pop remoove
      (4)改:name[1]=2
      (5)查:count,index 内置函数:min,max,len,in,not in
    3. 其他操作
      (1)反转:reverse
      (2)排序:sort(数据类型必须相同)

    append()
    append()方法用于在列表末尾添加新的对象
    list.append(对象)
    该方法无返回值,但是会修改原来额列表
    count()
    count()方法用于统计某个元素在列表中出现的次数
    list.count(对象)
    返回元素在列表中出现的次数
    extend()
    extend()函数用于在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
    list.extend(序列)
    该方法没有返回值,但会在已存在的列表中添加新的列表内容
    index()
    index()函数用于在列表中找出某个值第一个匹配项的索引位置
    list.index(对象)
    该方法返回查找对象的索引位置,如果没有找到对象则抛出异常
    insert()
    insert()函数用于将指定的对象插入列表中的指定位置
    list.insert(索引位置,对象)
    该方法没有返回值,但会在列表指定位置插入对象
    pop()
    pop()函数用于移除列表中的一个元素(默认为最后一个元素),并且返回该元素的值
    list.pop([index=-1])
    ()内index为可选参数,要移除的列表中的元素的索引值,不能超过列表总长度,默认为index=-1,删除最后一个列表值
    该方法返回从列表中移除的元素对象
    remove()
    remove()函数用于移除列表中的某个值的第一个匹配项
    list.remove(对象)
    该方法没有返回值但是会移除列表中的某个值的第一个匹配项
    reverse()
    reverse()函数用于反向列表中的元素
    list.reverse()
    该方法没有返回值,但是会对列表中的元素进行反向排序
    sort()
    sort()函数用于对源列表进行排序,如果指定参数,则使用比较函数指定的比较函数
    list.sort(cmp=None,key=None,reverse=False)
    cmp—可选参数,如果指定了该参数会使用该参数的方法进行排序
    key—主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素进行排序
    reverse—排序规则,reverse=True降序,reverse=False升序(默认)
    该方法没有返回值,但是会对列表的对象进行排序

    元组

    元组类似于列表,不同之处在与元组的元素不能更改。
    元组使用小括号,列表使用方括号。
    元组创建很简单,只需要在小括号中添加元素,并使用逗号隔开即可。
    元组元素特点:可重复,不可修改,可嵌套,支持多种数据类型。

    列表形式及元素特点是什么?
    列表在方括号内,元素以逗号分隔
    列表元素特点:可重复,可修改,可嵌套,支持多种数据类型。
    元组形式级元素特点是什么?
    元组在小括号内,使用逗号隔开元素
    元组元素特点:可重复不可修改,可嵌套,支持多种数据类型
    列表和元组的区别是什么?
    列表和元组的不同之处在于一个在方括号内一个在小括号内,元组元素不能修改。

    展开全文
  • python知识点整理

    2020-01-17 16:38:45
    利用python的f.read(1024),一次读取1024字节到内存中,同时用yield返回读取的结果,这样当内存小于文件大小时,可以分批读取文件,并进行操作。 def bigFileRead(f, new_line): buf = "" while True: while ...

    一、大文件读取(生成器实现)

           利用python的f.read(1024),一次读取1024字节到内存中,同时用yield返回读取的结果,这样当内存小于文件大小时,可以分批读取文件,并进行操作。

    def bigFileRead(f, new_line):
        buf = ""
        while True:
            while new_line in buf:          # 如果一次性读取出的字符串包含多个分隔符,那么循环操作,查分分隔符,相当于做了split
                pos = buf.index(new_line)
                yield buf[:pos]
                buf = buf[pos+len(new_line):]
            chunk = f.read(5)
            if not chunk:                   # 读取结束
                yield buf
                break
            buf += chunk
    with open("input.txt", encoding="utf-8") as f:
        for line in bigFileRead(f, "{|}"):
            print(line)

    二、for else 用法

            for后面跟else,当正常遍历完for循环后,会接着执行else里面的代码。如果再执行for循环的时候,break了,那么就不会执行else了。

    for i in range(5):
        if i == 3:
            break
    else:               # 当for循环是break出来的时候,不会执行else,否则都会执行else里面的语句
        print("out")

    三、解开n层的list或tuple

            递归方法,解开n层的list或tuple。

    def splitList(lst):
        ret = []
        for x in lst:
            if (type(x)==list) or (type(x)==tuple):
                ret.extend(splitList(x))  # 处理外层
            else:
                ret.append(x)               # 处理内层
        return ret
    k = [2, ['23', ['234', (5, 6, 7)], ['9823', 9239]]]
    print(splitList(k))

    四、GIL(全局解释器锁)

            首先明确一点,每个CPU在同一时刻只能执行一个python的线程。在python多线程下,每个线程的执行方式:

    1. 获取GIL
    2. 执行代码直到sleep或等待网络I/O
    3. 释放GIL

            可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。

            在python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。 而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。 并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。

            而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。

            python对CPU密集型和IO密集型的多线程的友好性区别:

    1. CPU密集型代码(各种循环处理、计数等等),在这种情况下,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。
    2. IO密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。

            多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行。但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低

            python下想要充分利用多核CPU,就用多进程。这是因为每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。

    五、函数传参*、**的区别 | fun(*args,**kwargs)中的*args,**kwargs什么意思?

            *args是直接将value作为传入参数,**kwargs是将key和value一起作为传入参数。

    def args_test(x, y, *args):
        print(x, y, args)
    args_test(1,2,3,4,5)        # *args 用来将参数打包成tuple给函数体调用
    
    def kwargs_test(**kwargs):
        print(kwargs)
    kwargs_test(a=1,b=2,c=3)    # **kwargs 打包关键字参数成dict给函数体调用
    
    def param_test(arg, *args, **kwargs):
        print(arg, args, kwargs)
    param_test(1,3,5,a=6,b=9)   # 参数arg、*args、**kwargs三个参数的位置必须是一定的。必须是(arg,*args,**kwargs)这个顺序,否则程序会报错。
    
    def test(*args,**kwargs):
        print(args, kwargs)
    l = [1,2,3,4]
    d = {"a":1,"b":2}
    test(l, d)                  # l,d这2个变量都会传递给args这个形参,作为args变量的两个元素,kwargs是一个空的字典,没有任何参数传递一个它
    test(*l,**d)                # 这个变量就会被赋值给args,d这个变量就会被赋值给kwargs
    
    def foo(action=None, **kwargs):
        print("action",action,sep="=================>")
        print("kwargs", kwargs, sep="=================>")
    d = {"a":1,"b":2}
    foo(d)                      # d会被赋值给action参数
    foo(**d)                    # d会被赋值给kwargs参数
    d = {"action":"action","a":1}
    foo(**d)                    # action会被d里面的action覆盖

    六、装饰器作用、语法糖

            装饰器是给现有的模块增添新的小功能。

    import logging
    
    def use_logging(func):
        def wrapper():
            logging.info("%s is running" % func.__name__)
            return func()   # 把 foo 当做参数传递进来时,执行func()就相当于执行foo()
        return wrapper
    def foo():
        print('i am foo1')
    foo = use_logging(foo)  # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于  foo = wrapper
    foo()                   # 执行foo()就相当于执行 wrapper()
    
    # 等价于使用@语法糖
    def use_logging(func):
        def wrapper():
            logging.info("%s is running" % func.__name__)
            return func()
        return wrapper
    @use_logging
    def foo():
        print("i am foo2")
    foo()
    
    # 传参装饰器 - *args
    def use_logging(func):
        def wrapper(*args):
            logging.info("%s is running" % func.__name__)
            return func(*args)
        return wrapper
    @use_logging
    def foo(*args):
        print("~~~~", args)
    l = ["张三", 12]
    foo(l)
    
    # 传参装饰器 - **kwargs
    def use_logging(func):
        def wrapper(**kwargs):
            logging.info("%s is running" % func.__name__)
            return func(**kwargs)
        return wrapper
    @use_logging
    def foo(**kwargs):
        print("~~~~", kwargs)
    foo(name="张三", age=12)
    
    """
    @a
    @b
    @c
    def f ():   # 个函数还可以同时定义多个装饰器,它等效于 f=a(b(c(f)))
        pass
    """

    七、is 和 == 的区别

            python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。此外还有其他要素,比如计数字段,该字段便于后面垃圾回收机制做参考。

            is比较的是两个对象的id值是否相等,也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址。

            ==比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法。

    a = 256
    b = 256
    c = 1000
    d = 10*3
    e = 2000
    f = 2000
    print(a is b)   # True、出于对性能的考虑,Python内部会缓存一些整型对象,但凡是需要用些小整数时,就从这里面取,不再去临时创建新的对象。故而id相同
    print(c is d)   # False、1000的id 和 10*3的value一样,但是对应的id不同
    print(e is f)   # True、第一条

    八、python内存管理

            python中一切皆对象,对象又可以分为可变对象和不可变对象。如果修改后地址不变,则是可变对象,否则为不可变对象。

            (1)内存池

            python有内存池机制,pymalloc机制。 当创建大量消耗小内存的对象时,c语言中频繁调用new/malloc会导致大量的内存碎片,致使效率降低。 在python中使用内存池,内存池的概念就是预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够了之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。 查看源码,可以看到pymalloc对于小的对象,pymalloc会在内存池中申请空间,一般是少于256kb,如果是大的对象,则直接调用new/malloc来申请新的内存空间。

            (2-1)垃圾回收机制

            python采用GC作为自动内存管理机制,GC要做的有2件事,一是找到内存中无用的垃圾对象资源,二是清除找到的这些垃圾对象,释放内存给其他对象使用。

            优点: 简单 实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。

            缺点: 需要额外的空间维护引用计数。 不能解决对象的循环引用。(主要缺点) 。对象循环参考下面的代码

            (2-2)标记清除

            标记清除主要是解决循环引用问题。 对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。 从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。 非活动对象会被GC回收。 详情可参考:https://juejin.im/post/5ca2471df265da307b2d45a3

            (2-3)分代回收

            python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代。 python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。 新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉。 而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。

    a = {}      # 对象a的引用计数为 1
    b = {}      # 对象b的引用计数为 1
    a['b']=b    # b的引用计数增1
    b['a']=a    # a的引用计数增1
    del a       # a的引用减 1,最后a对象的引用为 1
    del b       # b的引用减 1, 最后b对象的引用为 1
    # 循环引用
    # 执行del后,a、b对象已经没有任何引用指向这两个对象,需要被回收,但是a、b的计数器并不为0,故而无法通过计数法对两个对象回收,它们会一直驻留在内存中,就会造成了内存泄漏。
    

    九、python魔法函数

    1. 魔法函数以__开始、__结束,是python内置的函数
    2. 自定义的类中使用魔法函数,该类就具有了该魔法函数的功能,比如使用iter魔法函数,该类就具有迭代功能。
    3. 魔法函数不要自己构建,而是使用python提供的魔法函数
    class Company(object):
        def __init__(self, employee_list):
            self.employee = employee_list
        def __getitem__(self, item):
            return self.employee[item]
        def __len__(self):
            return 2
    company = Company(["tom", "bob", "jane"])
    # 实例化对象
    employe = company.employee
    for em in employe:
        print(em)
    print()
    # 魔法函数,自动调用__getitem__
    for em in company:
        print(em)
    print()
    # 魔法函数,调用__len__,全局关键字len的优先级小于class里面的魔法函数
    print(len(company))

    十、理解__new__和__init__的区别

            __init__方法为初始化方法、__new__方法才是真正的构造函数。

    1. 默认Foo类继承object类,而object类里面包含__new__方法,故而调用Foo类时,会自动调用object里面的__new__构造方法
    2. __new__ 方法创建实例对象供__init__ 方法使用,__init__方法定制实例对象。__new__方法必须返回值
    3. __init__方法为初始化方法,为类的实例提供一些属性或完成一些动作。__init__方法不需要返回值
    4. 如果在自定义的类中重构了__new__方法,那么调用该类时,就会使用重构的__new__方法
    class Foo(object):
        def __new__(cls, *agrs, **kwds):
            inst = object.__new__(cls, *agrs, **kwds)
            print("重构的__new__:", inst)
            return inst
        def __init__(self, price=50):
            self.price = price
        def how_much_of_book(self, n):
            print("初始化__init__:", self)
            return self.price * n
    foo = Foo()    # 当class中重构了__new__函数后,会优先调用重构的__new__函数
    print(foo.how_much_of_book(8))

    十一、三元表达式

            为真时的结果 if 判定条件 else 为假时的结果

    print(100 if 77>66 else 99)

    十二、lambda表达式

            lamba的代码简洁、返回结果也很简洁,但是lamba并不会带来程序运行效率的提高。

    g = lambda x:x+2                    # lamba传一个参数
    info = [g(x) for x in range(10)]
    print(info)
    
    m = lambda x,y,z:(x-y)*z            # lamba传多个参数
    print(m(3,1,2))
    
    dict1 = {"a":4, "b":3, "c":9}
    dict2 = sorted(dict1.items(), key=lambda item:item[1], reverse=True)
    print(dict2)

    十三、assert断言

            检查条件,不符合就报错终止程序。

    a = -1
    # assert a>0  # 报错
    assert a<0
    print(a)

    十四、连接字符串用join还是+

            join的性能明显好于+。这是因为,当用操作符+连接字符串的时候,每执行一次+都会申请一块新的内存,然后复制上一个+操作的结果和本次操作的右操作符到这块内存空间,因此用+连接字符串的时候会涉及好几次内存申请和复制。而join在连接字符串的时候,会先计算需要多大的内存存放结果,然后一次性申请所需内存并将字符串复制过去,这是为什么join的性能优于+的原因。

    str1 = " ".join(["hello", "world"])
    str2 = "hello " + "world"
    print(str1)
    print(str2)

    十五、深拷贝和浅拷贝

            python对象分为容器对象和非容器对象

            容器对象:

                    列表、元组、字典和集合

            非容器对象(对于非容器类型的对象没有拷贝一说):

                    数字、字符串和其他'原子'类型的对象

        浅拷贝:

    1. 是将一个对象的地址赋值给一个变量,让新变量指向该地址
    2. 新的对象改变,旧的对象也会跟着改变

        深拷贝:

    1. 在另一块内存地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的
    2. 新的对象改变,旧的对象不会跟着改变
    from copy import deepcopy
    
    # 浅拷贝,
    a = ['hello',[1,2,3]]
    b1 = a[:]
    b2 = a
    print([id(x) for x in a])   # 只是将新变量指向旧的变量地址
    print([id(x) for x in b1])
    print([id(x) for x in b2])
    b2[0] = "p"
    print(a)                    # 新的变量做了更改,旧的变量也会跟着改变
    
    # 深拷贝
    a = ['hello',[1,2,3]]
    b = deepcopy(a)
    print([id(x) for x in a])   # 地址不同
    print([id(x) for x in b])
    b[0] = "p"
    print(a)                    # 新的对象改变,旧的对象不会跟着改变

    十六、数组、链表、队列、堆栈等容器对象的区别

        数组:

    • python中没有数组的概念,这里把数组单独拿出来,是为了后面与列表做比较
    • 在c语言中,定义一个数组,必须指定数组的长度和数据类型,例如 int a[4]={10,12,16,33}
    • 相当于在内存中开辟长度为20的连续空间(注意是连续空间),a就指向该数组第一个元素的指针,每个内存空间真真实实的存储着10/12/16/33的二进制

        列表:

    • python中的列表可以混合存储任意数据类型,这是因为列表中存的是元素的内存地址,而不是元素的值。
    • 对元素取值时,先在列表中找到元素的内存地址,再通过内存地址找到元素。因此,比起数组,对列表元素的取值要慢一些。
    • 列表没有长度限制,当我们通过append方法向列表中添加元素时,如果列表满了,那么新申请2倍于原来列表的内存地址,并将原列表的值拷贝到新的内存地址中。
    • 列表的删除,插入的时间复杂度是O(N)

        栈:

    • 栈是一个数据集合,遵循先进后出的特性
    • 进栈:append
    • 出栈:pop
    • 查看栈顶元素:li[-1]
    • 在python中通常用list来实现栈

        堆栈溢出:

    • 计算机中的内存可以分为堆存储、栈存储。其中程序中的变量保存在堆中,栈用来保存函数调用上下文等。一般程序嵌套层不会太多,因此堆的比例远大于栈的比例
    • 我们通常说的内存溢出,就是堆溢出
    • 在python中,函数调用是通过栈这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出

        队列:

    • 队列是一个数据集合,遵循先进先出的特性
    • 在python中通常用deque来实现队列

        链表

    • 链表中每一个元素都是一个对象,每个对象称为一个节点,包含有数据域key和指向下一个节点的指针next。通过各个节点之间的相互连接,最终串联成一个链表
    • 数组和列表都是连续存储的,删除和插入元素,其它元素需要补过去或者后移
    • 不过数组删除插入要耗时一些,时间复杂度是O(n),而链表则不会这样,它的时间复杂度是O(1)
    • 在python中通常定义class来实现链表

        集合与字典

    • 哈希表:是一种线性的存储结构(类似于数组),根据关键码的值(key, value)直接进行访问。将对象的Key通过一个哈希函数hash(key)换成一个整型数字,然后将该数字对数组长度进行取余,取余结果就当作数组的下标,将对象的值value存储在以该数字为下标的数组空间里。
    • 例如:数据集合{1,7},假设存在哈希函数hash(x)使得hash(1)=0,hash(7)=4,那么这个哈希表被存储为[1,None, None, None, 7]。当我们查找7所在的位置时,通过哈希函数获得该元素在哈希表中的下标(hash(7)=4),然后在下标为4的位置取出该元素。哈希表查询速度非常的快,几乎是O(1)的时间复杂度。
    • 哈希冲突:由于哈希表的下标范围是有限的,而元素关键字的值是接近无限的,因此可能会出现h(102)=56, h(2003)=56这种情况。此时,两个元素映射到同一个下标处,造成哈希冲突。
    • 字典:python中的字典就是使用哈希表存储的。通过哈希函数将字典中元素的key映射为数组/列表下标。假如hash(‘name’)=3, hash(‘age’)=1, h(‘hobby’)=4, 则哈希表存储为[None, 18, None, 'Lena', '瓜子']。在字典键值对数量不多的情况下,几乎不会发生哈希冲突,此时查找一个元素的时间复杂度为O(1)。
    # 16-1、括号匹配问题
    def check(exp):
        list1 = []
        for char in exp:
            if char in ["(", "[", "{"]:
                list1.append(char)
            elif char == ")":
                if list1[-1]=="(":
                    list1.pop()
            elif char == "]":
                if list1[-1]=="[":
                    list1.pop()
            elif char == "}":
                if list1[-1]=="{":
                    list1.pop()
        if len(list1) == 0:
            return True
        else:
            return False
    print(check("({[()]})"))
    # 16-2、迷宫问题,深度遍历,栈实现
    maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
            [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
            [1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
            [1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
            [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
            [1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
            [1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
            [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
    # 约定按四个方向探寻:
    dirs = [lambda x, y: (x-1, y),  # 左
            lambda x, y: (x+1, y),  # 右
            lambda x, y: (x, y-1),  # 上
            lambda x, y: (x, y+1)]  # 下
    def mazePath(x1, y1, x2, y2):
        stack = []
        stack.append((x1, y1))                          # 起点位置入栈
        while stack:                                    # 栈不为空时循环;栈空代表死迷宫(找遍所有的路,直到回退到起点位置,继续回退(出栈),栈空)
            curNode = stack[-1]                         # 栈顶,表示当前位置
            if (curNode[0]==x2) and (curNode[1]==y2):   # 到达终点,栈中的元素就是最终路径
                for node in stack:                      # 打印路径
                    print(node)
                return True
            for dir in dirs:                            # 依次进行4个方向的探寻
                nextNode = dir(curNode[0], curNode[1])  # 下一个位置坐标
                if maze[nextNode[0]][nextNode[1]]==0:   # 0代表通道,说明找到下一个方块
                    stack.append(nextNode)              # 添加到栈顶,更新当前位置
                    maze[nextNode[0]][nextNode[1]]=-1   # 将0赋值为-1,标记为已经走过,防止死循环
                    break                               # 退出
            else:                                       # 四个方向都找完,没有通道,那么开始回退
                maze[curNode[0]][curNode[1]] = -1       # 标记死路
                stack.pop()                             # 弹出栈顶,回退
        print("死迷宫")
        return False  # 死迷宫
    mazePath(1, 1, 8, 8)
    # 16-3、迷宫问题,广度遍历,队列实现
    from collections import deque
    
    maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
            [1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
            [1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
            [1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
            [1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
            [1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
            [1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
            [1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
    dirs = [lambda x, y: (x-1, y),  # 左
            lambda x, y: (x+1, y),  # 右
            lambda x, y: (x, y-1),  # 上
            lambda x, y: (x, y+1)]  # 下
    def mazePath2(x1, y1, x2, y2):
        queue = deque()
        path = []                                       # 存放走过的节点
        queue.append((x1, y1, -1))                      # queue中元素:(1, 1, -1),前两位是坐标,第三位是其上一步在path中的索引。这里用-1标记起点。queue出了包含正确的路径,也会包含一些噪音节点。
        maze[x1][y1] = -1
        while queue:
            curNode = queue.popleft()
            path.append(curNode)
            if curNode[0]==x2 and curNode[1]==y2:
                printPath(path)
                return True
            for dir in dirs:
                nextNode = dir(curNode[0], curNode[1])
                if maze[nextNode[0]][nextNode[1]]==0:   # 0代表通道,说明找到下一个方块,因为是广度遍历,故而上下左右都会尝试走一下,不会break
                    queue.append((nextNode[0], nextNode[1], len(path)-1))  # len(path)-1)记录来源索引,也就是记录当前节点的前一个节点路径
                    maze[nextNode[0]][nextNode[1]]=-1   # 标记该节点已经走过,防止死循环
        return False
    def printPath(path):
        curNode = path[-1]
        realpath = []                                   # 记录新的路径
        while curNode[2] != -1:                         # 反溯到起始节点时,break
            realpath.append(curNode[0:2])
            curNode = path[curNode[2]]                  # 通过索引找到上一步节点
        realpath.append(curNode[:2])
        realpath.reverse()                              # list取反
        print(realpath)
    print(mazePath2(1, 1, 8, 8))

    十七、提高python运行效率

            JIT:是just in time的缩写, 也就是即时编译编译器。通常python的执行过程是,将.py的文件编译成.pyc的字节码,之后PVM将字节码编译成对应的机器指令执行。在循环语句中,编译器也会一条一条的将代码编译成字节码之后再翻译为机器指令,这非常耗时。而JIT就是解决这个问题的,针对那些频繁运行的程序,JIT会将这部分程序直接编译为机器指令并缓存起来,之后在运行时直接调用缓存的机器指令即可,大大缩减了程序运行的时间

            CPython:一般说Python都是指CPython解释器

            Cython: Cython将Python代码编译成C源码,再把C源码转换成Python扩展模块

            Pypy:   Pypy最重要的一点就是Pypy集成了JIT。Pypy的优点是对纯Python项目兼容性极好,缺点是对很多C语言库支持性不好,不能导入一些库

            Numba:  Numba是一个库,是一个用于编译python数组和数值计算函数的编译器,可以在运行时将Python代码编译为本地机器指令,而不会强制大幅度的改变普通的Python代码

            Cython、Pypy、Numba的区别:

    1. 通用性:在三个方案中,Cython和Numba的兼容性都非常好,而Pypy对于部分库的支持较差(如Numpy,Scipy)
    2. 速度:  这三种方案的速度相差不大,通常来说Cython要快于Pypy,尤其是对于部分C扩展。Pypy要快于Numba,但针对于纯数值计算的工作,Numba甚至还要快于Cython。(通常用numba对numpy进行加速)
    3. 易用性:易用性最好的无疑是Pypy,Pypy是Python的解释器,我们针对纯Python使用Pypy,除了Pypy不支持的部分库外,不需要进行任何改动。然后是Numba,Numba的基本使用方法就是给函数加一个装饰器,易用性也很高,最后是Cython,因为Cython中需要使用Python+C混合编码,如果需要移植,代价会很大
    import time
    from numba import jit
    
    # pypy 可以参考,https://zhuanlan.zhihu.com/p/28018577
    time_start = time.time()
    for i in range(100):
        for j in range(i):
            pass
    time_end = time.time()
    print("pypy执行耗时:", (time_end-time_start))   # python运行耗时3.5s、pypy运行耗时0.059s
    
    # cPython代码可读性较差,一般很少用,故而不做详细介绍
    
    # numba使用LLVM编译器架构将纯Python代码生成优化过的机器码,可以将面向数组和使用大量数学的python代码优化到与c,c++和Fortran类似的性能,而无需改变Python的解释器。
    # python加速numpy等计算时,优先考虑numba
    @jit
    def f(x, y):
        # A somewhat trivial example
        return x + y
    print("A = ", f(1,2))
    
    # numba编译的函数可以调用其他编译函数
    @jit
    def square(x):
        return x ** 2
    @jit
    def hypot(x, y):
        return math.sqrt(square(x) + square(y))
    print("B = ", hypot(1,2))
    
    # numba有两种编译模式:nopython模式和object模式。前者能够生成更快的代码,但是有一些限制可能迫使numba退为后者。想要避免退为后者,而且抛出异常,可以传递nopython=True.
    @jit(nopython=True)
    def f(x, y):
        return x + y
    print("C = ", f(1,2))

     

    展开全文
  • python知识点整理.pdf

    2021-01-06 13:00:19
    python知识点整理.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,343
精华内容 937
关键字:

python知识点整理

python 订阅