Python基础 知识点总结

Python是静态还是动态类型?是强类型还是弱类型?

  • Python是动态强类型语言
  • 动态还是静态类型是看运行时还是编译期确定类型
  • 强类型指的是不会发生隐式类型转换;弱类型比如javascript,1 + ‘1’ = ‘11’,整数1会自动转换为字符1

Python作为后端语言的优缺点

  • 胶水语言,轮子多,应用广泛
  • 语言灵活,生产力高
  • 性能问题,代码维护问题,Python2/3的兼容问题

什么是鸭子类型?

  • “当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子时,那么这只鸟就可以称做鸭子”
  • 也就是说关注点在对象的行为,而不是类型
  • 比如file,StringIO,socket,对象都支持read/write方法(file like object)
  • 再比如定义了__iter__ 魔法方法的对象都可以用for迭代

什么是monkey patch?

  • monkey patch其实就是运行时替换
  • 比如gevent需要修改内置的socket;time.time() 替换成自己自定义的函数;
    from gevent import monkey
    monkey.patch_socket()
    
    import time
    print(time.time())
    def _time():
        return '321'
    time.time = _time
    print(time.time())
    View Code 

什么是自省?

运行时判断一个对象的类型的能力

  • Python一切皆对象,可以用type,id,isinstance来获取对象类型信息
  • ll = [1, 2, 3, 4]
    d = dict(a=1)
    
    print(type(ll))
    print(isinstance(d, dict))
    
    def add(a, b):
        if isinstance(a, int):
            return a + b
        elif isinstance(a, str):
            return a.upper() + b
    
    print(add(1, 2))
    print(add('head', 'tail'))
    
    print(id(ll) is id(d))
    View Code

什么是列表和字典推导式?

  • 比如[for i in range(10) if i % 2 ==0]
  • 一种快速生成list/dict/set的方式,用来替代map/filter等
  • a = ['a', 'b', 'c']
    b = [1, 2, 3]
    
    print({k:v for k, v in zip(a, b)})
    View Code 

Python中的深拷贝与浅拷贝

  • 什么是赋值操作?

    • 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址 。
    • 修改不可变对象(strtuple)需要开辟新的空间,修改可变对象(list等)不需要开辟新的空间
    • 其实就是对象的引用(别名)
  • 什么是浅拷贝?什么是深拷贝?

    • 浅拷贝就是创建一个新的对象,其内容是原对象中元素的引用
    • 深拷贝是指创建一个新的对象,然后递归的拷贝原对象所包含的子对象。深拷贝出来的对象与原对象没有任何关联。  
  • Python中如何实现浅拷贝和深拷贝?

    • 浅拷贝
      • 使用切片[:]操作
      • 使用工厂函数(如list/dir/set)
      • 使用对象的copy(),copy模块中的copy()函数
      • a = [1, 2, 3]
        b = list(a)
        print(id(a), id(b))  # a和b身份不同
        # 4534852616 4399036872
        for x, y in zip(a, b):  # 但它们包含的子对象身份相同
            print(id(x), id(y))
            # 4395255744 4395255744
            # 4395255776 4395255776
            # 4395255808 4395255808
            
        # 从上面可以明显的看出来,a 浅拷贝得到 b,a 和 b 指向内存中不同的 list 对象,
        # 但它们的元素却指向相同的 int 对象。这就是浅拷贝!
        View Code
    • 深拷贝
      • 使用copy模块中的deepcopy()函数   

Python函数相关

  • 参数是如何传递的?可变对象和不可变对象

    • 既不是传递引用也不是值,唯一支持的参数传递是共享传参(call by object, call by sharing);
    • 共享传参是指函数的各个形式参数获得实参中各个引用的副本;也就是说,函数内部的形参是实参的别名
    • 不可变对象:bool/str/int/float/tuple/frozenset; 可变对象:list/set/dict
    • 传递参数其实是通过对象引用的方式,实参和形参都指向同一个对象,但是Python中有可变对象和不可变对象;

      对于不可变对象,每次赋值都会创建新的对象,并且原来的形参指向新的对象。

      View Code

  • Python中*args, **kwarg含义是什么?

    • 主要用来处理可变参数
    • *args被打包成tuple
    • *kwargs被打包成dict  
    • def print_all(a, *args, **kwargs):
          print(a)
          if args:
              print(args)
          if kwargs:
              print(kwargs)
      
      print_all('hello', 'world', name='Sky')
      View Code

Python的异常机制

  • 什么是异常?

  异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。

  异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。

  • 使用异常的常见场景

    • 网络请求(超时,连接错误等)
    • 资源访问(权限问题,资源不存在)
    • 代码逻辑(越界访问,KeyError等)
      try:
          f = open("testfile", "w")    #可能出现异常的代码
          f.write("这是一个测试文件,用于测试异常!!")
      except IOError:  #可以捕获多个代码并处理
          print("Error: 没有找到或创建文件失败或读取文件失败") # 处理异常的代码
      else:
          print("内容写入文件成功") #异常没有发生执行的代码
      finally:            # 不管异常有没有发生都会执行的代码,一般用于关闭资源和释放
          f.close()
      View Code
  • 如何自定义自己的异常?为什么需要自定义自己的异常?    

    • 继承Exception类实现自定义异常
    • 给异常加一些附加信息
    • 处理一些业务相关的特定异常(raise MyException)
    • class MyException(Exception):
          pass
      
      try:
          raise MyException('my exception')
      except MyException as e:
          print(e)
      View Code

Python性能分析及优化;GIL(全局解释器锁)

  • 什么是Cpython GIL

    • Cpython解释器的内存管理不是线程安全的
    • 保护多线程情况下对Python对象的访问
    • Cpython使用简单的锁机制避免多个线程同时执行字节码
  • GIL的影响

    • 限制了程序的多核执行
    • 同一个时间只有一个线程执行字节码
    • CPU密集型程序难以利用多核优势
    • IO期间会释放GIL,对IO密集程序影响不大(请求网页、读写文件等)
  • 如何规避GIL影响

    • cpu密集可以使用多进程+进程池

    • IO密集可以使用多线程/协程

  • 为什么有了GIL还要关注线程安全

    • 什么操作是原子的?一步到位执行完?

      • 一个操作如果是一个字节码指令可以完成的就是原子的(计数不是原子的, list, set, dict 在python里是原子操作。)

      • 原子的可以保证线程是安全的

      • 用dis操作来分析字节码 

      • 对非原子操作(计数等),进行加锁操作, 保证线程是安全的。
  • 如何剖析程序性能

    • 二八定律,大部分时间耗时在少量代码上

    • 内置的profile/cprofile等工具

    • 使用pyflame(uber开源)的火焰图工具 (web应用) 
  • 服务端性能优化

    • 数据结构和算法优化

    • 数据库层:索引优化,慢查询消除,批量操作减少IO,NoSQL

    • 网络IO:批量操作,pipeline操作减少IO 

    • 缓存:使用缓存数据库redis/memached

    • 异步:ayncio, celery

    • 并发:gevnet/多线程                     


生成器与协程

  • 什么是生成器?

    • 生成器就是可以生成值的函数
    • 当一个函数有了yield 关键字就成了生成器
    • 生成器可以挂起执行并且保持当前执行的状态
  • 基于生成器的协程

    • 生成器可以通过yield暂停执行和产出数据

    • 同时支持send()向生成器发送数据和throw()向生成器抛异常  

      def coro():
          hello = yield 'hello' # yield关键字在=右边作为表达式,可以被send值
          yield hello
      
      c = coro()
      # 输出hello,这里调用next产出一个值'hello',之后函数暂停
      print(next(c))
      # 再次调用send发送值,此时hello变量赋值为'world',然后yield产出hello变量的值'world'
      print(c.send('world'))
      
      #之后协程结束,后续再next(c)会抛异常StopIteartion
      next(c)
      View Code          
  • 原生协程

    • import asyncio
      
      async def compute(x, y):
          print("Compute %s + %s ..." % (x, y))
          await asyncio.sleep(1.0)
          return x + y
      
      async def print_sum(x, y):
          result = await compute(x, y)
          print("%s + %s = %s" % (x, y, result))
      
      loop = asyncio.get_event_loop()
      loop.run_until_complete(print_sum(1, 2))
      loop.close()
      View Code              

Python单元测试

  • 什么是单元测试

    • 针对程序模块进行正确性校验
    • 一个函数,一个类进行验证
    • 自底向上保证程序正确性
  • 为什么要写单元测试

    • 保证代码逻辑的正确性

    • 单测影响设计,易测的代码往往是高内聚低耦合的

    • 回归测试,防止改一处整个服务不可用

  • 单元测试相关的库

    • pytest/nose 较为常用                           

    • mock模块用来模拟替换网络请求等  

 

posted @ 2020-05-10 07:17  Ming|Zhao  阅读(212)  评论(0)    收藏  举报