第03章 Python语言基础

2021-02-19 06:18

阅读:647

标签:分析   none   线程安全   开源   unit test   test   多进程   web应用   prim   

Python语言特性

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

◆动态强类型语言(不少人误以为是弱类型)

◆动态还是静态指的是编译期还是运行期确定类型

◆强类型指的是不会发生隐式类型转换

Python作为后端语言优缺点

为什么要用 Python?

◆胶水语言,轮子多,应用广泛

◆语言灵活,生产力高

◆性能问题、代码维护问题、 python2/3兼容问题

动态语言一时爽,代码重构火葬场。

什么是鸭子类型

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

◆关注点在对象的行为,而不是类型(duck typing)

◆比如file, StringIO, socket 对象都支持read/write方法(file like object)

◆再比如定义了__iter__魔术方法的对象可以用for迭代

鸭子类型更关注接口而非类型

class Duck:
    def quack(self):
        print("gua gua")

class Person:
    def quack(self):
        print("我是人类")

def in_the_forest(duck):
    duck.quack()

def game():
    donald = Duck()
    john = Person()
    in_the_forest(donald)
    in_the_forest(john)
    print(type(donald))
    print(type(john))


if __name__ == ‘__main__‘:
    game()
什么是 monkey patch

什么是 monkey patch?哪些地方用到了?自己如何实现?

◆所谓的 monkey patch就是运行时替换

◆比如 gevent库需要修改内置的 socket

from gevent import monkey;monkey.patch_socket()

import socket

print(socket.socket)

print("After monkey patch")

from gevent import monkey

monkey.patch_socket()
print(socket.socket)

import select

print(select.select)
monkey.patch_select()
print("after monkey patch")
print(select.select)

#
# After monkey patch
#
#
# after monkey patch
#

import time

print(time.time()) # 1613097577.3560631


def _time():
return 1234


time.time = _time
print(time.time()) # 1234

什么是自省?

Introspection

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

◆ Python-切皆对象,用type,id, isinstance 获取对象类型信息

◆ Inspect 模块提供了更多获取对象信息的函数

ll = [1, 2, 3]
d = dict(a=1)
print(type(ll))
print(type(d))

print(isinstance(ll, list))
print(isinstance(d, dict))

= 判断两个变量的值是不是相同

is 判断内存地址是否相同

is None

None 本身是单例

技术图片

什么是列表和字典推导

List Comprehension

◆ 比如[i for i in range(10) if i%2 == 0]

◆ 一种快速生成 list/dict/set的方式。用来替代map/filter等

◆ (i for i in range(10)if i%2 == 0)    返回生成器

技术图片

技术图片

知道 Python之禅吗

The Zen of Python

◆ Tim Peters 编写的关于 Python编程的准则

◆ import this

◆编程拿不准的时候可以参考

Python2/3差异常考题

使用 pyenv安装

多版本 Python

Python3 改进

◆ print成为函数

◆ 编码问题。 Python3不再有 Unicode对象,默认str就是 unicode

◆ 除法变化。 Python3除号返回浮点数

技术图片

技术图片

技术图片

◆类型注解(type hint)。帮助IDE实现类型检查

◆优化的 super0方便直接调用父类函数。

◆高级解包操作。a,b,*rest= range(10)

Python3 改进

◆类型注解(type hint)。帮助IDE实现类型检查

◆优化的 super() 方便直接调用父类函数。

◆高级解包操作。a,b,*rest= range(10)

技术图片

技术图片

技术图片

技术图片

◆ Keyword only arguments。限定关键字参数

◆ Chained exceptions. Python3 重新抛出异常不会丟失栈信息

◆ 一切返回迭代器 range,zip,map, dict values, etc. are all iterators

技术图片

技术图片

技术图片

技术图片

技术图片

技术图片

Python3 新增

◆ yield from 链接子生成器

◆ asyncio 内置库, async/await  原生协程支持异步编程

◆ 新的内置库enum, mock, asyncio, ipaddress, concurrent.futures 等

Python3 改进

◆生成的pyc文件统一放到__pycache__

◆一些内置库的修改。 urlib,selector 等

◆性能优化等。。。

Python2/3工具

熟悉一些兼容2/3的工具

◆ six    模块

◆ 2to3等工具转换代码

◆ __future__

Python函数常考题

常考点:

  • 参数传递
  • (不)可变对象
  • 可变参数

技术图片

技术图片

技术图片

技术图片

Python 如何传递参数

一个容易混淆的问题

传递值还是引用呢?都不是。唯一支持的参数传递是共享传参

Call by Object( Call by Object Reference or Call by Sharing

Call by sharing(共享传参)。函数形参获得实参中各个引用的副本

技术图片

技术图片

技术图片

s 创建一个新的对象 hehe

指向的同一个对象

技术图片

一切是对象

对象引用的方式

实参和形参指向同一个对象

不可变对象看起来 像是拷贝了一份  实则不是 是创建了一份

创建一个新的对象,让形参指向新的

技术图片

技术图片

技术图片

技术图片

技术图片

技术图片

技术图片

Python 异常机制常考题

什么是 Python的异常?

Python 使用异常处理错误(有些语言使用错误码)

◆ BaseException

◆ SystemExit/KeyboardInterrupt/GeneratorExit

◆ Exception

技术图片

使用异常的常见场景

什么时候需要捕获处理异常呢?看 Python 内置异常的类型

◆ 网络请求 (超时、连接错误等)

◆ 资源访问 (权限问题、资源不存在)

◆ 代码逻辑 (越界访问、 KeyError等)

如何处理 Python 异常
try:
    # func # 可能会抛出异常的代码
except (Exception1, Exception2) as e: # 可以捕获多个异常并处理
    # 异常处理的代码
else:
    # pass  # 异常没有发生的时候代码逻辑
finally:
    pass  # 无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放
如何自定义异常

如何自定义自己的异常?为什么需要定义自己的异常?

◆ 继承 Exception 实现自定义异常 (想想为什么不是 BaseException)

◆ 给异常加上一些附加信息

◆ 处理一些业务相关的特定异常(raise MyException)

class MyException(Exception):
    pass

try:
    raise MyException(‘my exception‘)
except MyException as e:
    print(e)

CtrlC 也是异常

捕捉不到

Python性能分析与优化GIL常考题

什么是 Cpython GIL

GIL, Global Interpreter Lock

◆ Cpython解释器的内存管理并不是线程安全的

◆ 保护多线程情况下对 Python 对象的访问

◆ Cpython 使用简单的锁机制避免多个线程同时执行字节码

GIL的影响

限制了程序的多核执行

◆ 同一个时间只能有一个线程执行字节码

◆ CPU 密集程序难以利用多核优势

◆ IO 期间会释放GIL,对IO密集程序影响不大

技术图片

如何规避GIL影响

区分CPU和IO密集程序

◆ CPU密集可以使用多进程+进程池

◆ IO密集使用多线程/协程

◆ cython扩展

GIL的实现

技术图片

问题:请问这段代码输出?

import threading

n = [0]


def foo():
    n[0] = n[0] + 1
    n[0] = n[0] + 1


threads = []
for i in range(5000):
    t = threading.Thread(target=foo)
    threads.append(t)

for t in threads:
    t.start()
print(n)

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

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

◆ 一个操作如果是一个字节码指令可以完成就是原子的

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

◆ 使用dis操作来分析字节码

原子操作

import dis

def update_list(l):
    l[0] = 1  # 原子操作,不用担心线程安全问题

dis.dis(update_list)
"""
  5           0 LOAD_CONST               1 (1)
              2 LOAD_FAST                0 (l)
              4 LOAD_CONST               2 (0)
              6 STORE_SUBSCR                #单字节码操作,线程安全
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
"""

非原子操作不是线程安全的

import dis


def update_list(l):
    l[0] += 1


dis.dis(update_list)
"""
 5           0 LOAD_FAST                0 (l)
              2 LOAD_CONST               1 (0)
              4 DUP_TOP_TWO
              6 BINARY_SUBSCR
              8 LOAD_CONST               2 (1)
             10 INPLACE_ADD  #需要多个字节码操作,有可能在线程执行过程中切到其他线程
             12 ROT_THREE
             14 STORE_SUBSCR
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
"""

如何剖析程序性能

使用各种 profile 工具(内置或第三方)

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

◆内置的 profile/cprofile 等工具

◆使用 pyflame(uber开源)的火焰图工具

服务端性能优化措施

Web应用一般语言不会成为瓶颈

◆数据结构与算法优化

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

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

◆缓存:使用内存数据库 redis/memcached

◆异步:asyncIO, celery

◆并发:gevent/多线程

什么是生成器

Generator

◆生成器就是可以生成值的函数

◆当一个函数里有了 yield 关键字就成了生成器

◆生成器可以挂起执行并且保持当前执行的状态

def simple_gen():
    yield ‘hello‘
    yield ‘world‘


gen = simple_gen()
print(type(gen))  # 
print(next(gen))  # hello
print(next(gen))  # world

基于生成器的协程

Python3 之前没有原生协程,只有基于生成器的协程

◆pep 342( Coroutines via Enhanced generators)增强生成器功能

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

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

Generator Based Coroutine示例

def coro():
    hello = yield ‘hello‘  # yield 关键宇在右边作为表达式,可以被send值
    yield hello


c = coro()
print(next(c))  # 输出 ‘hello‘,这里调用next 产出第一个值‘hello‘,之后函数暂停
print(c.send(‘world‘))  # 再次调用send发送值,此时he1lo变量值为‘ world‘,然后yie1d产出hell0变量的值‘world‘

# 之后协程结束,后续再send值会抛异常 StopIteration


#hello
#world
协程的注意点
  • 协程需要使用 send(None)或者 next(coroutine)来预激prime才能启动
  • 在yield 处协程会暂停执行
  • 单独的 yield value 会产出值给调用方
  • 可以通过 coroutine.send(value)  来给协程发送值,发送的值会赋值给 yield 表达式左边的变量 value=yield
  • 协程执行完成后(没有遇到下一个yiled 语句)会抛出 StopIteration 异常
协程装饰器

避免每次都要用send预激它

python2 yield 的协程

流畅的python

from functools import wraps


def coroutine(func):  # 这样就不用每次都用 send(None)启动了
    """ 装饰器:向前执行到第一个`yield` 表达式,预激 `func` """
    @wraps(func)
    def primer(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen

    return primer
Python3 原生协程

Python3.5 引入 async/await 支持原生协程 (native coroutine)

import asyncio
import datetime
import random


async def display_date(num, loop):
    end_time = loop.time() + 50.0
    while True:
        print(‘Loop: {} Time: {}‘.format(num, datetime.datetime.now()))
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(random.randint(0, 5))


loop = asyncio.get_event_loop()
asyncio.ensure_future(display_date(1, loop))
asyncio.ensure_future(display_date(2, loop))
loop.run_forever()

Python单元测试

什么是单元测试

Unit Testing

◆针对程序模块进行正确性检验

◆一个函数,一个类进行验证

◆自底向上保证程序正确性

为什么要写单元测试

三无代码不可取(无文档、无注释、无单测)

◆保证代码逻辑的正确性(甚至有些采用测试驱动开发(TDD))

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

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

单元测试相关的库

单元测试相关库

◆ nose/pytest 较为常用

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

◆ coverage 统计测试覆盖率

技术图片

技术图片

技术图片

技术图片

技术图片

Python深拷贝与浅拷贝

深拷贝与浅拷贝的区别

◆ 什么是深拷贝?什么是浅拷贝?

◆ Python中如何实现深拷贝?

◆ 思考:Python中如何正确初始化一个二维数组?

第03章 Python语言基础

标签:分析   none   线程安全   开源   unit test   test   多进程   web应用   prim   

原文地址:https://www.cnblogs.com/wenyule/p/14408004.html


评论


亲,登录后才可以留言!