Python后端技术栈(一)

每日分享

Happiness is a way of travel. Not a destination.

幸福是一种旅行方式。 不是目的地。

小闫语录

刻意的寻找,幸福无影无踪;简单的感受,幸福如影随形。幸福是什么?爱着,被爱着。

1.导航

1.1Python 语言基础

1.语言特点

2.语法基础

3.高级特性

1.2算法与数据结构

1.常用算法和数据结构

2.分析时间和空间复杂度

3.实现常见数据结构和算法

1.3编程范式

1.面向对象编程

2.常用设计模式

3.函数式编程

1.4操作系统

1.常用 Linux

2.进程和线程

3.内存管理

1.5网络编程

1.常用协议 TCP、IP、HTTP

2.Socket 编程基础

3.Python 并发库

1.6数据库

1.MySQL 数据库、索引优化

2.关系型和 NoSQL 的使用场景

3.Redis 缓存(常用的数据类型以及使用场景,底层实现了解会更好)

1.7Python Web框架

1.常用框架的对比,使用 Restful

2.WSGI 原理

3.Web 安全的问题

1.8系统设计

1.设计原则,如何分析

2.后端系统常用的组件(缓存、数据库、消息队列等等)

3.技术选型和实现(短网址服务、Feed 流系统)

1.9技术之外的软实力

1.学习能力

2.业务理解能力,沟通交流能力

3.心态

2.0小扩展-STAR模型

在生活中描述一件事情或者是在面试中描述项目经验,如何让我们的语言更加有条理,逻辑性?可以采用如下的模型进行梳理。

情境(situation)

什么情况下发生的

任务(task)

你是如何明确你的任务的

行动(action)

采取了什么样的行动

结果(result)

结果怎么样?学到了什么?

2.技术栈详解

2.1 Python 语言基础

2.1.1 Python 语言特性

Python 是动态强类型语言,很多人都误认为是弱类型语言,其实是错误的。

动态是指在运行期确定类型(静态则是在编译期确定类型)。强类型指的是在没有强制类型转化前,不允许两种不同类型的变量相互操作(也就是不会发生隐式类型转换)。

2.1.2 Python 作为后端语言的优缺点

为什么使用 Python ?

答:它是一门胶水语言,轮子多,应用广泛;语言灵活,生产力高,是一些创业公司以及外包项目节省时间的首选语言。但是在使用的时候,需要考虑到性能的问题,代码维护问题,以及2和3版本的兼容问题。

2.1.3 Python 其他的重要知识点

鸭子类型简单介绍

曾有一个生动的例子来描述鸭子类型:当看到一只鸟,如果它走起来像鸭子、叫起来像鸭子,游泳的时候也像鸭子,那么我们就称这只鸟是鸭子。它的关注点在对象的行为,而不是类型。比如一些常用对象 file、StringIO、socket 都支持 read/write 方法,我们可以看做类似的对象 file like object;再举个例子,在 Python 中实现了 __iter__ 魔法方法的对象都可以用 for 迭代。

什么是 monkey patch ?

答:所谓的 monkey patch 就是运行时的属性替换。比如我们常用的一个并发库 gevent ,需要将内置的 socket 修改为非阻塞。我们在使用的时候,用到如下的代码:

from gevent import monkey # 打补丁,让gevent框架识别耗时操作,比如:time.sleep,网络请求延时 monkey.patch_all()

什么是自省?

答:自省就是在运行时判断一个对象的类型的能力。我们可以通过 type、id和 isinstance 等方法获取对象类型的信息。Inspect 模块提供了更多获取对象信息的函数。

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

列表生成式:

my_list = [i for i in range(10) if i % 2 == 0]

字典推导式:

dict1 = {k: v for k,v in zip(a,b)}

特殊的情况:将列表推导式的方括号修改为小括号的时候,会返回一个生成器。

2.1.4 Python 之禅

The Zen of Python 便是著名的Python 之禅,它是由 Tim Peters 编写的关于 Python 编程的准则,我们可以使用下面的代码进行查看:

import this

我们在编程的过程中,如果拿不准的时候可以进行一个参考。

2.2 Python2 和 Python3 的差异

2.2.1 Python2/3 差异

Python3 中做了一些改进,我们需要了解。比如 print 成为了函数;还有编码的问题,Python3 中不再有 Unicode 对象,默认 str 就是 Unicode;除法也有所变化,比如 Python3 除法返回的是浮点数。

Python2 里面是没有类型申明的,Python3 中我们可以添加一个类型注解(type hint),帮助 IDE 实现类型提示以及类型检查(mypy)。Python3 中优化的 super() 方便直接调用父类函数。Python3 中还有一些高级的解包操作,如下面示例:

a,b,*rest = range(10)

上面会将0赋值给 a,将1赋值给 b,然后将剩下的赋值给 rest。

类型检查示例:

In [2]: def fuction(name: str): return ‘hello‘ + name In [3]: fuction(‘Ethan‘) Out[3]: ‘helloEthan‘

Python3 限定关键字参数。也就是函数在传参的时候,我们可以通过关键字参数方式,指定参数名传参,避免参数太多时候搞混。

Python3 中重新抛出异常不会丢失栈信息,方便我们去排错(在 Python2 中如果在一个异常中 raise 一个异常,原来的异常就会丢失,Python3 中支持 raise from,保留异常栈信息)。

Python3 中一切都是返回迭代器,比如 range/zip/map/dict.value/etc. are all iterators。

2.2.2 Python3 新增

1.yield from 链接子生成器

2.asyncio 内置库, async、await 原生协程支持异步编程

3.新的内置库 enum(枚举),mock(单测时用),asyncio, ipaddress (处理ip地址)等等。

2.2.3 Python3 改进

1.生成的 pyc 文件统一放到 __pycache__文件夹下。

2.一些内置库的修改。如 urllib,selector(支持select、epoll等Linux底层的一些封装,方便我们统一做一些接口,实现异步IO) 等。

3.一些性能的优化,比如 dict。

2.2.4一些兼容2、3的工具

1. six 模块。

2. 2to3 等工具转换代码。(脚本工具,将 Python2 转换为 Python3 代码)

3. __future__模块。在 Python2 中使用 Python3 的函数功能可参照如下代码:

from __future__ import print_fuction

2.3 Python 函数

2.3.1 Python 如何传递参数?

答:Python 其实不是引用传递也不是值传递,而是共享传参(函数形参获得实参中各个引用的副本)。简单的理解一下:

我们在每一次传递参数的时候,形参和实参都指向同一个对象,这样就叫做对象传递,既不是拷贝了一个值,也不是直接去操作这块内存,但是它的结果有两个。对于可变对象来说,我们直接去修改它,对于不可变对象来说,表现就好像 copy 了一个值,然后去修改新的值。

不可变对象好像是传值,可变对象好像是传引用。但是实际是不同的。

2.3.2 Python 可变/不可变对象

1.可变对象:bool、int、float、tuple、str

2.不可变对象:list、set、dict

可变对象作为默认参数的时候,注意默认参数只计算一次。

2.3.3 Python 中 *args**kwargs

函数传递中,他们处理可变参数。如果使用 *args那么会将所有的参数打包成一个 tuple 对象。 **kwargs 则是将所有的关键字参数打包成一个 dict 对象。

2.4 Python 异常机制

2.4.1什么是 Python 的异常?

答:异常就是一种错误处理机制。所有的异常都继承自 BaseException 。举几个和系统相关的异常:SystemExit、KeyboardInterrupt、GeneratorExit(生成器退出的异常)。还有一个异常的基类就是 Exception。

2.4.2使用异常的常见场景

答:网络请求(超时、连接错误);资源访问(权限问题、资源不存在);代码逻辑(越界访问、KeyError等)。

2.4.3处理异常

try: # 可能会抛出异常的代码 except (Exception1,Exception2) as e: # 异常处理代码 else: # 异常没有发生时代码逻辑 finally: # 无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放。

2.4.4如何自定义异常

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

可以通过查看异常的等级信息,发现如果继承自顶级父类,那么一些常用的异常也没有了,自己需要定义的异常就太多太多,耗费时间。

2.可以给异常加上一些附加信息。

3.通常都是处理一些和业务相关的特定异常(raise MyException)

2.5 Python 性能分析与优化

Python 作为一门脚本语言来说,它的性能一直被诟病。并且由于存在一个臭名昭著的 GIL 导致没有办法充分利用多核,这都限制了 Python 的性能。

2.5.1什么是 CPython GIL?

GIL (Global Interpreter Lock)

1.CPython 解释器的内存管理并不是线程安全的,存在多个线程时,有可能会出现同时修改同一对象,这样容易出现问题。

2.为了保护多线程情况下对 Python 对象的访问,CPython 使用了简单的锁机制避免多个线程同时执行字节码。

缺陷便是没有办法同时利用 CPU 的多核,只有一个线程执行字节码。对于 CPU 密集型的程序来说,影响会比较大。

2.5.2 GIL 的影响

限制了程序的多核执行。

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

2.CPU 密集型程序难以利用多核优势。

3.IO 期间会释放 GIL ,对 IO 密集型程序影响不大。

2.5.3如何规避 GIL 的影响

1.CPU 密集型可以使用多进程 + 进程池的方式充分的利用多核。

2.IO 密集型可以使用多线程或者是协程。

3.使用 cython 扩展(将 Python 程序转化成 C 代码的一个扩展)。

2.5.4 GIL 的实现

CPython 中才会有 GIL ,其他的解释器是没有的。底层的代码逻辑是设置一个ticker,每执行多少个字节码的时候,去检查当前是否有全局解释器锁,如果有那么执行函数释放,让其他的线程去执行;如果没有,就重新获取锁。通俗一点就是每隔一段时间,就会尝试去释放当前线程的锁,让其他线程获取锁并去执行。

2.5.5为什么有了 GIL 之后,还要关注线程安全?

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

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

2.原子的是可以保证线程安全的,非原子操作不是线程安全的。

3.使用 dis 操作来分析字节码。

import dis

def update_list(l): l[0] = 1 dis.dis(update_list)

2.5.6如何剖析程序性能

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

1.遵循二八定律,其实大部分的时间耗时在少量的代码上。

2.通过内置的 profile 和 cprofile 等工具衡量程序的运行时间。

3.对于 web 应用来说,使用 pyflame(uber开源) 的火焰图工具分析产品的性能。

2.5.7服务端性能优化措施

web应用一般语言不会成为瓶颈。可以采用如下的一些优化措施:

1.数据结构和算法优化。

2.数据库层:索引优化、慢查询消除、批量操作减少IO、NoSQL的使用。

3.网络IO:批量操作, pipline 操作减少 IO。

4.缓存:使用内存数据库 redis、memcached 等。以此抗一些并发比较高的请求。

5.使用异步的框架或者库如 asyncio 和 celery。

6.对于并发相关的一些请求使用 gevent 协程或者多线程。

2.6 Python 生成器与协程

2.6.1什么是生成器

Generator

1.生成器就是可以生成值的函数。

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

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

2.6.2基于生成器的协程

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

1.pep 342(Coroutines via Enhanced Generators)增强生成器功能。

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

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

示例:

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

2.6.3协程的注意点

1.协程需要使用 send(None) 或者 next(coroutine) 来 『预激』(prime) 才能启动。

2.在 yield 处协程会暂停执行。

3.单独的 yield value 会产出值给对方调用

4.可以通过 coroutine.send(value) 来给协程发送值,发送的值会赋值给 yield 表达式左边的变量 value=yield

5.协程执行完成之后(没有遇到下一个 yield 语句)会抛出 StopIteration 异常。

2.6.4协程装饰器

避免每次都要用 send 预激它。

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

2.6.5Python3 原生协程

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

2.7单元测试

2.7.1什么是单元测试

Unit Testing

1.针对程序模块进行正确性检验。

2.一个函数,一个类进行验证。

3.自底向上保证程序正确性。

2.7.2为什么写单元测试

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

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

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

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

2.7.3单元测试相关的库

1.nose/pytest 较为常用

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

3.coverage 统计测试覆盖率

如何设计测试用例:(等价类划分): 1.正常值功能测试。 2.边界值(比如最大最小,最左最右值) 3.异常值(比如None,空值,非法值)

2.8重点知识

2.8.1 Python 深拷贝与浅拷贝

浅拷贝:对于不可变对象相当于引用赋值;浅拷贝对于可变对象拷贝时只拷贝第一层引用。

深拷贝:对于不可变对象同样相当于引用赋值;对于可变对象会逐层进行拷贝。

Python 中浅拷贝的方式:copy 模块的 copy 方法;对象本身的 copy 方法;工厂方法;切片(只对列表有效)。

工厂方法就是直接使用 list 等方法进行修改。 Python 中默认使用的就是浅拷贝方式。

2.8.2小结

1.不可变对象在赋值时会开辟新空间

2.可变对象在赋值时,修改一个引用的值,另一个引用也会发生改变。

3.深浅拷贝对不可变对象拷贝时,不开辟新的空间,相当于赋值操作。

4.浅拷贝在拷贝时,只拷贝顶层中的引用,如果元素是可变对象,并且被修改,那么拷贝的对象也会发生变化。

5.深拷贝在拷贝时,会逐层进行拷贝,直到所有的引用都是不可变对象为止。

6.Python 中有多种方式实现浅拷贝,copy 模块的 copy 函数,对象的 copy 函数,工厂方法,切片等。

7.大多数情况下,编写程序时,都是使用浅拷贝,除非有特定的需求。

8.浅拷贝的优点:拷贝速度快,占用空间少,拷贝效率高。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Python中的函数(二) 在上一篇文章中提到了Python中函数的定义和使用,在这篇文章里我们来讨论下关于函数的一些更深的话题。在学习C语言函数的时候,遇到的问题主要有形参实参的区别、参数的传递和改变、变量的作用域。同样在Python中,关于对函数的理解和使用也存在这些问题。下面来逐一讲解。一.函
Python中的字符串 可能大多数人在学习C语言的时候,最先接触的数据类型就是字符串,因为大多教程都是以"Hello world"这个程序作为入门程序,这个程序中要打印的"Hello world"就是字符串。如果你做过自然语言处理方面的研究,并且用Python
Python 面向对象编程(一) 虽然Python是解释性语言,但是它是面向对象的,能够进行对象编程。下面就来了解一下如何在Python中进行对象编程。一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法。 类是对现实世界中一些事物的封装,
Python面向对象编程(二) 在前面一篇文章中谈到了类的基本定义和使用方法,这只体现了面向对象编程的三大特点之一:封装。下面就来了解一下另外两大特征:继承和多态。 在Python中,如果需要的话,可以让一个类去继承一个类,被继承的类称为父类或者超类、也可以称作基类,继承的类称为子类。并且Pytho
Python中的函数(一) 接触过C语言的朋友对函数这个词肯定非常熟悉,无论在哪门编程语言当中,函数(当然在某些语言里称作方法,意义是相同的)都扮演着至关重要的角色。今天就来了解一下Python中的函数用法。一.函数的定义 在某些编程语言当中,函数声明和函数定义是区分开的(在这些编程语言当中函数声明
在windows下如何快速搭建web.py开发框架 用Python进行web开发的话有很多框架供选择,比如最出名的Django,tornado等,除了这些框架之外,有一个轻量级的框架使用起来也是非常方便和顺手,就是web.py。它由一名黑客所创建,但是不幸的是这位创建者于2013年自杀了。据说现在由
将Sublime Text 2搭建成一个好用的IDE 说起编辑器,可能大部分人要推荐的是Vim和Emacs,本人用过Vim,功能确实强大,但是不是很习惯,之前一直有朋友推荐SUblime Text 2这款编辑器,然后这段时间就试了一下,就深深地喜欢上这款编辑器了...
Python中的模块 有过C语言编程经验的朋友都知道在C语言中如果要引用sqrt这个函数,必须用语句"#include<math.h>"引入math.h这个头文件,否则是无法正常进行调用的。那么在Python中,如果要引用一些内置的函数,该怎么处理呢?在Python中
Python的基础语法 在对Python有了基础的认识之后,下面来了解一下Python的基础语法,看看它和C语言、java之间的基础语法差异。一.变量、表达式和语句 Python中的语句也称作命令,比如print "hello python"这就是一条语句。 表达式,顾名思义,是
Eclipse+PyDevʽjango+Mysql搭建Python web开发环境 Python的web框架有很多,目前主流的有Django、Tornado、Web.py等,最流行的要属Django了,也是被大家最看好的框架之一。下面就来讲讲如何搭建Django的开发环境。一.准备工作 需要下载的
在windows下安装配置Ulipad 今天推荐一款轻便的文本编辑器Ulipad,用来写一些小的Python脚本非常方便。 Ulipad下载地址: https://github.com/limodou/ulipad http://files.cnblogs.com/dolphin0520/u...
Python中的函数(三) 在前面两篇文章中已经探讨了函数的一些相关用法,下面一起来了解一下函数参数类型的问题。在C语言中,调用函数时必须依照函数定义时的参数个数以及类型来传递参数,否则将会发生错误,这个是严格进行规定的。然而在Python中函数参数定义和传递的方式相比而言就灵活多了。一.函数参数的
在Notepad++中搭配Python开发环境 Python在最近几年一度成为最流行的语言之一,不仅仅是因为它简洁明了,更在于它的功能之强大。它不仅能够完成一般脚本语言所能做的事情,还能很方便快捷地进行大规模的项目开发。在学习Python之前我们来看一下Python的历史由来,"Pytho
Python中的条件选择和循环语句 同C语言、Java一样,Python中也存在条件选择和循环语句,其风格和C语言、java的很类似,但是在写法和用法上还是有一些区别。今天就让我们一起来了解一下。一.条件选择语句 Python中条件选择语句的关键字为:if 、elif 、else这三个。其基本形式如
关于raw_input( )和sys.stdin.readline( )的区别 之前一直认为用raw_input( )和sys.stdin.readline( )来获取输入的效果完全相同,但是最近在写程序时有类似这样一段代码:import sysline = sys.stdin.readline()
初识Python 跟学习所有的编程语言一样,首先得了解这门语言的编程风格和最基础的语法。下面就让我们一起来了解一下Python的编程风格。1.逻辑行与物理行 在Python中有逻辑行和物理行这个概念,物理行是指在编辑器中实际看到的一行,逻辑行是指一条Python语句。在Python中提倡一个物理行只
当我们的代码是有访问网络相关的操作时,比如http请求或者访问远程数据库,经常可能会发生一些错误,有些错误可能重新去发送请求就会成功,本文分析常见可能需要重试的场景,并最后给出python代码实现。
1.经典迭代器 2.将Sentence中的__iter__改成生成器函数 改成生成器后用法不变,但更加简洁。 3.惰性实现 当列表比较大,占内存较大时,我们可以采用惰性实现,每次只读取一个元素到内存。 或者使用更简洁的生成器表达式 4.yield from itertools模块含有大量生成器函数可
本文介绍简单介绍socket的常用函数,并以python-kafka中的源码socketpair为例,来讲解python socket的运用
python实践中经常出现编码相关的异常,大多网上找资料而没有理解原理,导致一次次重复错误。本文对常用Unicode、UTF-8、GB2312编码的原理进行介绍,接着介绍了python字符类型unicode和str以及常见编解码错误UnicodeEncodeError和UnicodeDEcodeEr