一.Python基础

  1.Python语言特性:

    动态型(运行期确定类型,静态型是编译型确定类型),强类型(不发生隐式转换,弱类型,如PHP,JavaScript就会发生隐患式转换

  2.Python作为后端语言的优缺点:

    优点:

      胶水语言,轮子多,应用广泛;语言灵活,生产力高

    缺点:

      性能问题,代码维护问题,python2/3不兼容问题

  3.鸭子类型:

    “当一只鸟走起来像鸭子,游泳像鸭子,叫起来像鸭子,那么这只鸟就能被称为鸭子”

    关注点在对象的行为,而不是类型;

    如file,StringIO,socket都支持read/write方法(file类型)

    定义了__iter__就可以用for迭代

  4.monkey patch(猴子补丁):

    就是运行时替换

    4.1比如gevent需要修改内置的socket和select:

    4.2自己写的猴子补丁:

      

   5.is和==:

    is是判断是否为同一个对象(id内存地址是否相同),==是判断值是否相同

  6.列表,生成器,字典推导:

  7.Python之禅:

    Tim Peters编写的关于Python编程的规则;

    import this查看,编程拿不准可以查看:

                

优美胜于丑陋(Python 以编写优美的代码为目标)

明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)

简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)

复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)

扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)

间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)

可读性很重要(优美的代码是可读的)

即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)

不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)

当存在多种可能,不要尝试去猜测

而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)

虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )

做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)

如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)

命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

  8.Python2/3的差异:

    Python3改进:

      print改为函数,可以指定sep参数;

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

      除法变化,Python3除号(/)返回浮点数,Python2向下取整,Python3向下取整为(//)

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

主要是提示作用,检查可以用mypy包

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

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

      限定关键字参数(Keyword only arguments),防止把数据搞混

      Chained exceptions。Python3重新抛出异常不会丢失栈信息(有利于排错),raise from保留栈信息(raise NotImplementedError("haha") from OSError)

      一切返回迭代器range,zip,map,dict.values,etc(节省内存)

      生成的pyc文件统一放到__pyache__;

        一些内置库的修改,如urllib,selector等;

      性能优化等

    Python新增:

      yield from链接子生成器;

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

      新的内置库enum(枚举),mock(单测),asyncio(异步),ipaddress(处理ip地址),concurent.futures等

    Python/2/3兼容工具:

      six模块;2to3等工具转换代码;__future__

  9. Python函数常考题:

    Python如何传递参数:

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

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

      共享传参,函数形参获得实参种各个引用的副本      

     

ll指向[1,2,3],l也指向[1,2,3],后又指向[],所以ll仍为[1,2,3],l为[]

默认参数只计算一次

    Python可变,不可变对象:

      不可变对象:bool/int/float.str/frozenset;

      可变对象:list/set/dict

    Python *args,**kwargs:

      用来处理可变参数,*args会被打包成tuple,**kwargs会打包成dict

  10.Python异常处理机制:

    什么是Python的异常:

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

      BaseException(最低层异常)

      SystemExit,Timeout(超时),KeyboradInterrupt(ctrl+c),GeneratorExit(生成器退出异常),继承Exception(继承BaseException)

    使用异常常见场景:

      网络超时(超时,资源错误);

      资源问题(权限问题,资源不存在)

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

    如何处理:

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

     如何自定义异常,作用:

      通过继承Exception实现定义异常(为什么不是BaseException,ctrl+c不能结束,BaseException是所有异常的基类,KeyboradInterrupt和SystemExit,GeneratorExit,Exception都继承于它,如果继承BaseException,可能捕获不到一些异常。)

      给异常加一些附加信息;

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

  10.Python性能分析与优化,GIL常考题:

    GIL(Global Interpreter Lock):全局解释器锁

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

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

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

    GIL的影响:

      限制了程序的多核执行:

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

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

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

    如何规避GIL影响:

      CPU密集可以用多进程+进程池;

      IO密集使用多线程/协程

      cython扩展

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

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

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

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

    如何剖析程序性能:

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

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

        内置的profile/cprofile等工具;

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

    服务端性能优化措施:

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

          数据结构与算法优化;

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

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

        缓存:使用内存数据库 redis。memcached

        异步:asyncio,celery

        并发:gevent/多线程

  11.生成器和协程:

    生成器:

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

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

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

    基于生成器的协程:

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

        pep342增强生成器功能;

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

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

    

    协程注意点:

      协程使用send(None)或者next(coroutine)来预激(prime)才能启动;

      在yield出协程会暂停执行;

      单独的yield value会产出值给调用方;

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

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

      避免每次都使用send预激它(from functool import wraps)@wraps装饰器向前执行一个yield表达式,预激

      Python3.5引入async/await支持原生协程

  12.Python单元测试:

    什么是单元测试:

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

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

      自底向上保证程序正确性

    为什么要写单元测试:

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

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

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

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

    单元测试库:

      nose/pytest较为常用;

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

      coverage统计测试覆盖率

    Python深拷贝和浅拷贝:

      深拷贝,包含对象里面的自对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变。

      浅拷贝相当于拷贝了对象的引用(在python中,对象赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用)

    Python创建二维数组:

      https://www.cnblogs.com/woshare/p/5823303.html

二.算法和数据结构:  

  1.常用的内置算法结构:

    sorted;

    dict/list/set/tuple.... 

    collections中常用模块:

      namedtuple:让tuple属性可读(赋予名字)

      deque:双端队列(可以在队列前后操作,方便实现queue/statck)

      Counter:实现计数的功能

      OrderDict:可以记住key第一次进入的位置(有序的)

      defaultdict():键不存在时不会报错(带有默认值的字典)

      dict低层使用的hash表:

        为了支持快速查找使用了哈希表作为低层结构;

        哈希表平均查找复杂度为O(1);

        CPython解释器使用二次探查解决哈希冲突问题(如何解决哈希冲突,哈希扩容)

      list vs tuple:

        都是线性结构,支持下标访问;

        list是可变对象,tuple是保存的引用不可变(指的是没法替换掉这个对象,但是如果对像本身是一个可变对象,是可以修改这个引用指向的可变对象的);  

       

        list没法作为字典的key,tuple可以(可变对象不可hash)

      LRUCache:

        Least-Recently-Used替换最少使用的对象

        缓存剔除策略,当缓存空间不够用的时候需要一种方式剔除key

        常见的有LRU,LFU

        LRU通过使用一个循环双端队列不断把最新访问的key放到表头实现

        实现:

          字典利用缓存,循环双端链表用来记录访问顺序:

            利用Python内置的dict+collections.OrderdDict实现;

            dict用来当作k/v键值对的缓存;

            OrderedDict用来实现最近访问的key

        实现代码:     

 from collections import OrderedDict

 class LRUCathe:
def __init__(self,capacity=120):
self.od=OrderedDict()
self.capacity=capacity def get(self,key):
if key in self.od:
val=self.od[key]
self.od.move_to_end(key)
return val
else:
return -1
# 更新k/v
def put(self,key,value):
if key in self.od:
del self.od[key]
#更新key到表头
self.od[key]=value
else:
self.od[key]=value
#判断当前容量是否满了
if len(self.od)>self.capacity:
self.od.popitem(last=False)

    2.算法常考题:

      排序,查找等。。。。

      排序的稳定性:

        相同大小的元素排序之后保持相对的位置不变,就是稳定的。稳定性对于排序一个复杂的结构,并且保持原有排序才有意义。

      快排:(分治法)

        选择基准分割数组为两个字数组,小于基准和大于基准的;

        对两个子数组分别快排;

        合并结果。

 '''快速排序:
时间平均复杂度:O(nlog2n)
最好情况:O(nlog2n)
最坏:O(n^2)
空间复杂度:O(nlog2n)
排序方式:内部排序
稳定性:不稳定
'''
def quicksort(arr):
if len(arr)<2:
return arr pivot_index=0
pivot=arr[pivot_index]
less_pivot=[i for i in arr[pivot_index+1:] if i <=pivot]
great_pivot=[i for i in arr[pivot_index+1:] if i > pivot]
return quicksort(less_pivot)+[pivot]+quicksort(great_pivot)
print(quicksort([3,4,5,6]))

      归并排序:

 '''归并排序:
时间平均复杂度:O(nlog2n)
最好情况:O(nlog2n)
最坏:O(nlog2n)
空间复杂度:O(n)
排序方式:外部排序
稳定性:稳定
''' def mergeSort(arr):
import math
if len(arr)<2:
return arr
middle=math.floor(len(arr)/2)
left,right=arr[:middle],arr[middle:]
return merge(mergeSort(left),mergeSort(right)) def merge(left,right):
result=[]
while left and right:
if left[0]<=right[0]:
result.append(left.pop(0))
else:
result.append(right.pop(0))
while left:
result.append(left.pop(0))
while right:
result.append(right.pop(0))
return result
if __name__=='__main__':
print(mergeSort([3,2,1,5,6,8,10,44,51,31,0,49]))

      二分查找:

 def binary_search(arr,val):
if not arr:
return -1
beg=0
end=len(arr)-1
while beg<=end:
mid=int((beg+end)/2)
if arr[mid]==val:
return mid
elif arr[mid]>val:
end=mid-1
else:
beg=mid+1
return -1
print(binary_search([1,3,5,7,0,12],12))

  3.数据结构:   

    常考的数据结构:

      数据结构链表,队列,栈,二叉树,堆

      使用内置结构实现数据结构,比如内置的list/deque实现栈     

      leetcode或者《剑指offer》常见题

    常见数据结构之链表:

      链表有单链表,双链表,循环双端链表:

        如何使用Python来表示链表结构;

        实现链表常见操作,比如插入节点,反转链表,合并多个链表;

        Leetcode练习常见链表题目

      反转链表:(合并链表,求链表公共值)

 # Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None class Solution(object):
def reverseList(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
pre=None
cur=head
while cur:
nextnode=cur.next
cur.next=pre
pre=cur
cur=nextnode
return pre

      队列是先进先出结构:

          如何实现队列;实现队列的append和pop操作,如何做到先进先出;使用Python的list或者collections.deque实现队列          

       栈是后进先出: 

 from collections import deque

 class Stack(object):
def __init__(self):
self.item=deque() def push(self,value):
self.item.append(value) def pop(self):
return self.item.pop() s=Stack()
s.push(1)
s.push(2)
print(s.pop())
print(s.pop())

     Python的dict和set低层实现都是hash表:

        hash表的低层实现原理其实就是数组;

        根据哈希表可以很快定位一个元素,平均查找为O(1);

        不断加入元素会引起hash表从新开辟新空间,拷贝之前的元素到新空间

      解决哈希冲突:

        链接法:

          元素key冲突之后使用一个链表填充相同key的元素;

        开放寻址法:

          冲突之后根据一定方式(二次探查)寻找下一个可用的槽

      二叉树:

         先序(根->左->右),中序(左->根->右),后序(左->右->根)    

      堆:

        最大堆:对于每个非叶子节点V,V的值都比它的两个孩子大;

        最大堆支持每次pop操作获取最大的元素,最小堆获取最小的元素;

        常见问题:用堆来完成topk问题,从海量数据中寻找最大的k个  

      堆解决topk的问题:

        获取大量元素,固定内存:

          思路:1.先放入元素前k个建立一个最小堆;2.迭代剩余元素:如果当前元素小于栈顶元素,跳过该元素(肯定不是前k大),否则替换栈顶元素为当前元素,并从新调整堆    

 import heapq

 class TopK:
def __init__(self,iterable,k):
self.minheap=[]
self.capacity=k
self.iterable=iterable def push(self,val):
if len(self.minheap)>=self.capacity:
min_val=self.minheap[0]
if val<min_val:
pass
else:
#返回并替换pop堆顶最小值,插入新的值并调整堆
heapq.heapreplace(self.minheap,val)
else:
#前面k个元素直接放入minheap
heapq.heappush(self.minheap,val)
def get_topk(self):
for val in self.iterable:
self.push(val)
return self.minheap tk=TopK([1,3,5,6,0,3,44,11,42,0,1,9999],4)
print(tk.get_topk())

    链表:

      删除节点:

 # Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None class Solution(object):
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
nextnode=node.next
after_node=node.next.next
node.val=nextnode.val
node.next=after_node

      合并链表:

 # Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None class Solution(object):
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
root=ListNode(None)
cur=root
while l1 and l2:
if l1.val<l2.val:
node=ListNode(l1.val)
l1=l1.next
else:
node=ListNode(l2.val)
l2=l2.next
cur.next=node
cur=node
#l1和l2可能还有剩余值
# if l1 is None:
# cur.next=l2
# else:
# cur.next=l1
cur.next = l1 or l2
return root.next

  

     二叉树:

      二叉树的镜像(反转):

 # Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if root==None:
return root
else:
self.invertTree(root.left)
self.invertTree(root.right)
root.left,root.right = root.right,root.left
return root

        层序遍历二叉树(广度优先):  

 # Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
if root:
res=[]
cur_nodes=[root]
next_nodes=[]
res.append([i.val for i in cur_nodes])
while cur_nodes or next_nodes:
for node in cur_nodes:
if node.left:
next_nodes.append(node.left)
if node.right:
next_nodes.append(node.right)
if next_nodes:
res.append([i.val for i in next_nodes])
cur_nodes=next_nodes
next_nodes=[]
return res
else:
return []

    栈和队列:

      栈实现队列:

 from collections import deque
class Stack(object):
def __init__(self):
self.item=deque() def pop(self):
return self.item.pop()
def push(self,val):
self.item.append(val)
def empty(self):
return len(self.item)==0 def top(self):
return self.item[-1] class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.s1=Stack()
self.s2=Stack() def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
self.s1.push(x) def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if not self.s2.empty():
return self.s2.pop()
while not self.s1.empty():
val=self.s1.pop()
self.s2.push(val)
return self.s2.pop() def peek(self) -> int:
"""
Get the front element.
"""
if not self.s2.empty():
return self.s2.top()
while not self.s1.empty():
val = self.s1.pop()
self.s2.push(val)
return self.s2.top() def empty(self) -> bool:
"""
:return:
"""
return self.s1.empty() and self.s2.empty()

    堆:

      合并多个有序数组;TopK问题。

      堆是完全二叉树,有最大堆和最小堆。

      使用heapq实现堆的操作;

      合并k个有序链表

 # Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
from heapq import heapify,heappop class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
h=[]
for node in lists:
while node:
h.append(node.val)
node=node.next
if not h:
return None
#构建一个最小堆
heapify(h)
#构建链表
root=ListNode(heappop(h))
curnode=root
while h:
nextnode=ListNode(heappop(h))
curnode.next=nextnode
curnode=nextnode
return root

    

    字符串:

      翻转字符串: 

 class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
beg=0
end=len(s)-1
while beg<end:
s[beg],s[end]=s[end],s[beg]
beg+=1

      判断一个数字是否是回文数:

class Solution:
def isPalindrome(self, x: int) -> bool:
if x<0:
return False
s=str(x)
beg,end=0,len(s)-1
while beg<end:
if s[beg]==s[end]:
beg+=1
end-=1
else:
return False
return True

三.面向对象编程和设计模式

  1.什么是面向对象编程:

    把对象作为基本单元,把对象抽象成类,包括成员和方法;

    数据封装,继承,多态;

    Python中使用类来实现。过程式编程(函数),OOP(类)

   2.组合和继承:

    组合是使用其他类的实例作为自己的一个属性;

    子类继承父类的属性和方法(Is a关系);

    优先使用组合保持代码简单。

  3.类变量和实例变量的区别:

    类变量是所有实例共享;

    实例变量由实例单独享有,不同实例之间不影响;

    当我们需要在一个类的不同实例之间共享变量的时候使用类变量。

 class Person:
Coutry='China'
def __init__(self,name):
self.name=name
def print_name(self):
print(self.name)
p=Person('laoliu')
p2=Person('laowang')
p.print_name()
p2.print_name()

  4.classmethod和staicmethod的区别:

    都可以通过Class.method()的方式使用;

    classmethod第一个参数是cls,可以引用类变量

    staticmethod使用起来和普通函数一样,只不过放在类里去组织

    注:classmethod是为了使用类变量,staticmethod是代码组织的需要,完全可以放在类之外

 class Person:
Coutry='China'
def __init__(self,name):
self.name=name
@classmethod
def print_country(cls):
print(cls.Coutry)
@staticmethod
def join_name(first_name,last_name):
return last_name+first_name

  5.元类:

    元类是创建类的类。(元类允许我们控制类的生成,比如修改类的属性等);

    使用type来定义类;

    元类最常见的一个使用场景就是ORM框架

  type:

 #参数:类名,基类,属性
class Base():
pass
class Child(Base):
pass
#等价定义,注意是tuple
SameChild=type('Child',(Base,),{})
#加上方法
class ChildWithMethod(Base):
bar=True def hello(self):
print('hello')
#等价
ChiildWithMethod=type('ChiildWithMethod',(Base,),{'bar':True,'hello':hello})

  元类的使用:

 #元类继承于type
class LowercaseMeta(type):
'''
修改类的属性名称为小写的元类
'''
def __new__(mcs,name,bases,attrs):
lower_attrs={}
for k,v in attrs.items():
if not k.startswith('__'):
lower_attrs[k.lower()]=v
else:
lower_attrs[k]=v
return type.__new__(mcs,name,bases,lower_attrs)
class LowerCaseClass(metaclass=LowercaseMeta):
BAR=True
def HELLO(self):
print('hello')
print(dir(LowerCaseClass))

  6.装饰器:

    Python中一切皆对象,函数也可以当作参数来传递;

    装饰器是接收函数作为参数,添加功能后返回一个新函数的函数(类);

    Python中通过@使用装饰器(@是语法糖)

  编写一个记录函数耗时的装饰器

 #编写一个记录函数耗时的装饰器
import time
def log_time(func):
def _log(*args,**kwargs):
beg=time.time()
res=func(*args,**kwargs)
print('use time:{}'.format(time.time()-beg))
return res
return _log
@log_time
def mysleep():
time.sleep(1)
#相同
#mysleep=log_time(mysleep)
mysleep()

  使用类编写装饰器:

 class LogTime:
def __call__(self, func):
def _log(*args,**kwargs):
beg=time.time()
res=func(*args,**kwargs)
print('use time:{}'.format(time.time()-beg))
return res
return _log
@LogTime()
def mysleep2():
time.sleep(1)

   使用类砖石砌比较方便实现装饰器参数:

 class LogTime:
def __init__(self,user_int=False):
self.user_int=user_int
def __call__(self, func):
def _log(*args,**kwargs):
beg=time.time()
res=func(*args,**kwargs)
if self.user_int:
print('use time:{}'.format(time.time()-beg))
else:
print('use time:{}'.format(int(time.time() - beg)))
return res
return _log
@LogTime(True)
def mysleep2():
time.sleep(1)

  7.设计模式:

    创建型:

      工厂模式:解决对象创建问题;

      构造模式:控制复杂对象的创建;

      原型模式:通过原型的克隆创建新的实例;

      单例模式:一个类只能创建同一个对象;

      对象池模式:预先分配同一个类型的一组实例;

      惰性计算模式:延迟计算

      

      工厂模式:

        解决对象创建问题;

        解决对象的创建和使用;

        包括工厂方法和抽象工厂

 class DogToy:
def speak(self):
print('wang wang') class CatToy:
def speak(self):
print('miao miao')
def toy_factory(toy_type):
if toy_type=='dog':
return DogToy()
elif toy_type=='cat':
return CatToy()

      构造模式:

        用来控制复杂对象的构造;

        创建和表示分离。比如你要买电脑,工厂模式直接给你需要的电脑,但是构造模式允许你自己定义电脑的配置,组装完成后给你

      原型模式:

        通过克隆原型来创建新的实例;

        可以使用相同的类型,通过修改部分属性来创建新的实例;

        用途:对于一些创建实例开销比较高的地方可以使用原型模式 

      单例模式:

        一个类创建出来的对象都是同一个;

        Python'的模块其实就是单例的,只会导入一次;

        使用共享同一个实例的方式来创建单例模式 

   构建型:

      桥代理组合适配器,享元回家装饰外观。

      装饰器模式:

        无需子类化就可以扩展对象功能;

      代理模式:

        把一个对象的操作代理到另一个对象;

        前面实现的Stack/Queue,把操作代理到deque;

        has-a组合关系;

        还有就是实现一些安全相关的东西,比如一个比较裸露的接口,在校验层可以做一些校验。

      适配器模式:

        通过一个间接层适配统一接口;

        相当于多功能冲电头,可以给多个不同手机冲电;     

 from abc import abstractmethod, ABCMeta

 class Payment(metaclass=ABCMeta):
@abstractmethod
def pay(self, money):
raise NotImplementedError class Alipay(Payment):
def pay(self, money):
print("支付宝支付%s元"%money) class ApplePay(Payment):
def pay(self, money):
print("苹果支付%s元"%money) #------待适配的类-----
class WeChatPay:
def fuqian(self,money):
print('微信支付%s元'%money) #------类适配器------
class RealWeChatPay(Payment,WeChatPay):
def pay(self, money):
return self.fuqian(money) #-----对象适配器-----
class PayAdapter(Payment):
def __init__(self,payment):
self.payment=payment
def pay(self, money):
return self.payment.fuqian(money) #RealWeChatPay().pay(100) p=PayAdapter(WeChatPay())
p.pay(200)

      外观模式:

        简化复杂对象的访问问题;

      享元模式:

        通过对象复用(池)改善资源利用,比如连接池;

      MVC模式:

        解耦展示逻辑和业务逻辑;

    行为型:

      访问者写好策略备忘录,观察模板迭代状态,命令中介解释责任链。

      迭代器模式:通过统一的接口迭代对象

        Python内置对迭代器模式的支持;

        比如我们可以用for遍历各种Iterable的数据类型;

        实现__next__和__iter__实现迭代器

 class LinkedList:
class Node:
def __init__(self,item=None):
self.item=item
self.next=None
class LinkedListIterator:
def __init__(self,node):
self.node = node
#实现next方法,返回下一个元素
def __next__(self):
if self.node:
cur_node = self.node
self.node = cur_node.next
return cur_node.item def __iter__(self):
return self
def __init__(self,iterable=None):
self.head = LinkedList.Node(0)
self.tail = self.head
self.extend(iterable) #链表尾部追加元素
def append(self,obj):
s = LinkedList.Node(obj)
self.tail.next = s
self.tail = s
#链表自动增加长度
def extend(self,iterable):
for obj in iterable:
self.append(obj)
self.head.item += len(iterable) def __iter__(self):
return self.LinkedListIterator(self.head.next) def __len__(self):
return self.head.item def __str__(self):
return '<<'+', '.join(map(str,self)) + '>>' li = [i for i in range(100)]
lk = LinkedList(li)
print(lk)

      观察者模式:对象发生改变的时候,观察者执行相应的操作

        发布订阅是一种最常用的实现方式;

        发布订阅用于解耦逻辑;

        可以通过回调方式实现,当发生事件时,调用相应的回调函数

 from abc import ABCMeta, abstractmethod

 #抽象主题
class Oberserver(metaclass=ABCMeta):
@abstractmethod
def update(self):
pass #具体主题
class Notice:
def __init__(self):
self.observers = [] def attach(self,obs):
self.observers.append(obs) def detach(self,obs):
self.observers.remove(obs) def notify(self):
for obj in self.observers:
obj.update(self) #抽象观察者
class ManagerNotice(Notice):
def __init__(self,company_info=None):
super().__init__()
self.__company_info = company_info @property
def company_info(self):
return self.__company_info @company_info.setter
def company_info(self,info):
self.__company_info = info
self.notify() #具体观察者
class Manager(Oberserver):
def __init__(self):
self.company_info = None
def update(self,noti):
self.company_info = noti.company_info #消息订阅-发送
notice = ManagerNotice() alex=Manager()
tony=Manager() notice.attach(alex)
notice.attach(tony)
notice.company_info="公司运行良好"
print(alex.company_info)
print(tony.company_info) notice.company_info="公司将要上市"
print(alex.company_info)
print(tony.company_info) notice.detach(tony)
notice.company_info="公司要破产了,赶快跑路"
print(alex.company_info)
print(tony.company_info)

      策略模式:针对不同规模使用不同的策略

        根据不同的输入采用不同的策略;

        比如买东西超过10个打八折,超过20个打七折;

        对外暴露统一的接口,内部采用不同的策略计算

  8.函数式编程(推荐使用生成式):

    把电脑运算视作数学上的函数计算(lambda计算)

    高阶函数:map/reduce/filter;

    无副作用,相同的参数调用始终产生同样的结果

print(list(map(lambda x:x*2,range(10))))
#求1到9和
reduce(lambda x,y:x+y,range(10))
#求10内偶数
filter(lambda x:x%2==0,range(10))

  9.闭包:引用了外部自由变量的函数;自由变量:不在当前函数定义的变量;特性:自由变量会和闭包函数同时存在

    绑定了外部作用域的变量的函数;

    即使程序离开外部作用域,如果闭包仍然可见,绑定变量不会销毁;

    每次运行外部函数都会重新组建闭包

 from functools import wraps

 def catche(func):   #装饰器
store={}
@wraps(func)
def _(n): #闭包函数
if n in store:
return store[n]
else:
res=func(n)
srore[n]=res
return res
return _ @catche
def f(n):
if n<=1:
return 1
else:
return f(n-1)+f(n)

  10.Linux常考命令:

    man:查看某个命令怎末用;

    tldr:更简洁(pip install tldr)

    文件:chown/chmod/chgrp

    ls/rm/mv/touch/rename/ln(软硬连接)

    locate/find/grep

    编辑器 vi/nano

    cat/head/tail查看文件

    more/less交互式查看文件

    ps查看进程,kill杀死,top/htop监控进程

    free查看可用内存;

    了解每一列含义,排查泄露

    ipconfig/lsof/netstat/ssh/scp。tcpdump

    useradd/usermod/grepadd/grepmod

  11.线程和进程的区别:

    进程是对运行程序的封装,是系统资源调度和分配的基本单位;

    线程是进程的子任务,cou调度和分配的基本单位,实现进程内并发;

    一个进程可以包含多个线程,线程依赖进程存在,并共享进程内存

  12.线程安全:

    Python中哪些是线程安全的:

      一个操作可以在多线程环境中安全使用,获取正确的结果;

      线程安全的操作好比线程是顺序执行而不是并发执行的;

      一般如果涉及到写操作需要考虑如何让多个县城安全访问数据

    线程同步的方式:

      互斥量(锁):通过互斥机制防止多个线程同时访问公共资源;

      信号量:控制同一时刻多个线程同时访问同一个资源的线程数

      事件(信号):通过通知的方式保持多个线程同步

    进程间通信:

      管道/匿名管道/有名管道(pipe)

      信号:比如用户ctrl+c产生SININT程序终止信号

      消息队列

      共享内存;

      信号量;

      套接字:最常用的

  13.操作系统内存管理机制:

    分页机制:操作系统为了高效管理内存,减少碎片

    把逻辑地址和物理地址分离的内存分配管理方案:

         程序的逻辑地址划分为固定大小的页;

         物理地址划分为同样大小的帧;

         通过页表对应逻辑地址和物理地址

    分段机制:分段是为了满足代码的一些逻辑需求

      数据共享,数据保护,动态链接等

      通过段表实现逻辑地址和物理地址的映射关系;

      每个段内部是内存分配,段和段之间是离散分配的

    分页和分段的区别:

      页是出于内存利用率的角度提出的离散分配机制;

      段是出于用户角度,用于数据保护,数据隔离等用途的管理机制;

      页的大小是固定的,操作系统决定,;段的大小不确定,用户程序决定

    什么是虚拟内存:

      通过把一部分暂时不用的内存信息放到硬盘上:

        局部性原理,程序运行时候只有部分必要的信息装入内存;

        内存中暂时不需要的内容放到硬盘上;

        系统似乎提供了比实际内存大得多的容量,称之为虚拟内存

    什么是内存抖动(颠簸):

      频繁的页调度,进程中不断产生缺页中断;

      置换一个页,又不断再次需要这个页;

      运行程序太多了;页面替换策略不好。终止进程或者增加物理内存;

    Python垃圾回收机制:https://blog.csdn.net/csucsgoat/article/details/81510313

      引用计数为主(缺点:循环引用无法解决【两个对象互相引用之后引用计数无法归零】,del减少引用计数);

      引用标记清除和分代回收解决引用计数的问题;

      引用计数为主+标记清除和分代回收为辅

    什么时候引用计数增加:

      对象创建 a=1;

      对象被引用:b=a;

      对象作为参数传递func(a);

      对象存储在容器中 l=[a];

    什么时候引用计数会减少:

      显示使用del a;

      引用指向了别的对象 b=None;

      离开了对象的作用域(比如函数执行结束);

      从一个容器移除对象或者销毁容器

      循环引用:

#a和b的引用计数都为1
a=[1]
b=[1]
#a和b的引用计数都为2
a.append(b)
b.append(a)
#a和b都销毁 但是还是1
del a
del b

      标记清除:

        从根访问,如果不可达的点(独立的)就标记

      分代回收:

        分三代(0 1 2),每隔一定时间对每一代实行标记回收

四.网络协议TCP/UDP/HTTP

  浏览器输入一个url中间经历的过程:

    DNS查询->TCP握手->HTTP请求->反向代理nginx->uwsgi/gunicom->wep app响应->TCP挥手

  TCP三次握手,四次挥手,状态转换:https://blog.csdn.net/ZWE7616175/article/details/80432486

    TCP和UDP的区别:https://www.cnblogs.com/xiaomayizoe/p/5258754.html

    TCP是面向连接的,可靠的,基于字节流的;

    UDP是无连接,不可靠,面向报文的;

  HTTP协议:

    HTTP请求的组成:

      状态行;

      请求头;

      消息主体(可能为空)

    

    HTTP响应组成:

      状态行;响应头;向应正文

    HTTP请求头:https://www.cnblogs.com/honghong87/articles/6941436.html

    HTTP常见状态码:https://blog.csdn.net/laishaohe/article/details/79052085

    HTTP GET/POST的区别:https://www.cnblogs.com/logsharing/p/8448446.html

      Restful语义上一个是获取,一个是创建;

      Get是幂等的,POST是不幂等的;

      GET请求参数放到url(明文),长度限制;POST放在请求体,更安全

    什么是幂等的(指多次请求结果和请求一次结果一致):https://blog.csdn.net/qq_15037231/article/details/78051806

      幂等方法是无论调用多少次得到相同结果的HTTP方法;

      例如:a=4是幂等的,a+=4就是非幂等的;

      幂等的方法客户端可以安全的从发请求

      幂等:OPTIONS/GET/HEAD?PUT?DELETE;非幂等:POST/PATCH 

      安全的(是否会修改数据):OPTIONS/HEAD/GET;不安全的:PUT/POST/DELETE/PATCH 

    HTTP长连接:https://www.cnblogs.com/cswuyg/p/3653263.html 

      短连接:建立连接。。。数据传输。。关闭连接(连接的建立和关闭开销大)

      长连接:Connection:Keep-alive。保持TCp连接不断开;

      如何区分不同的HTTP请求:Content-Length(长度)|Transfer-Encoding(分段发送)

    cookie和session的区别:

      Session一般是服务器生成后交给客户端(通过url参数或cookie);

      Cookie是实现session的一种机制,通过HTTP cookie字段实现;

      Session通过服务器保存sessionid识别用户,cookie存储在客户端

  网络编程:

    TCP/UDP socket编程;HTTP编程:https://www.cnblogs.com/zengzy/p/5107516.html

      

    使用socket发送HTTP请求:

      。。。。

  IO多路复用:操作系统提供的同时监听多个socket的机制

    五种IO模型 

    常见的提升并发能力的方式:

      多线程模型;多进程模型; 

    IO多路复用:

      为了实现高并发需要一种机制并发处理多个socket;

      Linux常见的是select/poll/epoll

      select/poll/epoll的区别:https://www.cnblogs.com/Anker/p/3265058.html

  Python并发网络库:

    哪些并发网络库:

      Tornado vs Gevent ns Asyncio:

        Tornado并发网络库和同时也是一个web微框架;

        Gevent绿色线程(greenlet)实现并发,猴子补丁修改内置socket;

        Asyncio Python3内置的并发网络库,基于原生协程

    Tornado:

      适用于微服务,实现restful接口:

        低层基于Linux多路复用;

        可以通过协程或者回调实现异步编程;

        不过生态不够完善,相应的异步框架比如ORM不完善

    Gevent:

      基于轻量级绿色线程实现并发;

      需要注意monkey patch,gevent修改了内置的socket改为非阻塞;

      配合gunicorn和gevent部署作为wsgi server

    Asyncio:

      基于协程实现的内置并发网络库:

        Python3引入内置库,协程加事件循环;

        生态不够完善,没有大规模生产环境检验;

        目前应用不广泛,基于Aiohttp可以实现一些小的服务

五.数据库:

  Mysql基础考点:

    事务的原理,特性,事务并发控制;

    常用的字段,含义的区别;

    常用的数据库引擎之间的区别;

  什么是事务:

    事务是数据库并发控制的基本单位;

    事务可以看作是一些列SQL语句的集合;

    事务必须要么全部执行成功,要么全部执行失败(回滚),如转账操作。。。

  事务的四个特性(ACID):

    原子性:一个事务中的所有操作要不全部完成成功,要不全部失败;

    一致性:事务开始和结束后数据完整性没有被破坏;

    隔离性:允许多个事务同时对数据库修改和读写;

    持久性:事务结束后,修改是永不丢失的

  事务的并发控制:

    如果不对事务进行并发控制,可能产生四种异常情况:

      幻读:一个事务第二次查出现第一次没有结果;

      非重复读:一个事务重复读两次得到的结果不同;

      脏读:一个事务读取到另一个事务没有提交的修改;

      丢失修改:并发写入造成其中一些修改丢失

  为了解决并发控制异常,定义了4种事务隔离级别:

    读未提交:别的事务可以读取到未提交改变;

    读已提交:只能读已经提交的数据;

    可重复读:同一个事务先后查询的结果一样(默认);

    串行化:事务完全串行化串行,隔离级别最高,执行效率最低

  如何解决高并发场景下,写辱数据库有数据重复问题:

    使用数据库唯一索引;

    使用队列异步写入;

    使用redis等实现分布式锁  

  乐观锁和悲观锁:

    悲观锁是先获取锁再进行操作。一锁二查三更新 select for update;

    乐观锁先修改,更新的时候发现数据已经变了就回滚 check and set;

    使需要根据响应速度,冲突频率,重试代价来判断使用哪一种

  数据类型:。。。。int(10),表示最小显示十个字符宽

  Innodb和MyISAM区别:

    MyISAM不支持事务u,InnoDB支持事务;

    MyISAM不支持外键,InnoDB支持外键;

    MyISAM只支持表锁,InnoDB支持行锁和表锁;

    MyISAM支持全文索引,InnoDB不支持;

    。。。

  Mysql索引原理及优化:

    索引的原理,类型,结构;

    创建索引的注意事项,使用原则;

    如何排查和消除慢查询

    

    什么是索引:

      索引是数据表种一个或者多个列进行排序的数据结构,索引能够大幅提升检索速度(查找结构:线性,二分,二叉树。。。。);

      创建,更新索引本身也会耗费空间和时间;

    什么是B-Tree:

      查找结构进化史:

        线性查找:一个个找;实现简单;太慢;

        二分查找:有序;简单;要求有序,插入特别慢;

        HASH:查询块;占用空间;不太适合存储大规模数据;

        二叉查找树:插入和查询很快(logn);无法存储大规模数据,复杂度退化(单边增长,成线性);

        平衡树:解决bst退化的问题,树是平衡的;节点非常多的时候,依然树高很高;

        多路查找树:一个父亲多个孩子节点(度);节点过多树高不会特别深;

        多路平衡查找树:B-Tree(无法使用范围查找)

    B-Tree:多路平衡查找树(每个节点最多m(m>=2)个孩子,称未m阶或者度);

        叶节点具有相同的深度;

        节点种的数据key从左到右是递增的;

    B+Tree:

      B+Tree是B-Tree的变形:

        Mysql实际使用的B+Tree作为索引的数据结构;

        只在叶子节点带有记录的指针(为什么?可以增加树的度)

        叶子节点通过指针相连。为什么?实现范围查询

      阶(度/孩子)越多越好吗?

        操作系统内存分页,硬盘分块;

        以磁盘块的大小去确定阶的大小,这样操作系统去读取和缓存数据就非常友好。

    索引类型:

      普通索引(create index);

      唯一索引,索引列的值必循唯一(create unique index);

      多列索引

      主键索引:一个表只能有一个

      全文索引:InnoDB不支持,倒排索引

    什么时候创建索引:

       经常用作查询条件的字段(WHERE条件);

       进场用做表连接的字段;

       经常出现在order by,group by之后的字段

    创建索引需要注意的:

      最佳实践:   

        非空字段 NOT NULL,Mysql很难对空值作查询优化;

        区分度高,离散度大,作为索引的字段值尽量不要大量相同值;

        索引的长度不要太长(比较耗费时间)

    索引什么时候失效:

      记忆口诀:模糊匹配,类型隐转,最左匹配;

      以%开头的LIKE语句;

      出现隐式类型转换(在Python这种动态语言查询中需要注意),无法匹配;

      没有满足最左前缀匹配(为什么是最左前缀) (a,b,c) (1,2,3)  (1,2,4),相当于元组,从左往右比较,如(a,b,c),(a,b),(a)能比较,但是(a,b,c)和(b,c)不能比较,第一个都不相同

    聚集索引和非聚集索引:

      聚集还是非聚集指得是B+Tree叶节点存的是指针还是数据记录;

      MyISAM索引和数据分离,使用的是非聚集索引;

      InnoDB数据文件就是索引文件,主键索引就是聚集索引    

    如何排查慢查询:

      慢查询缺少索引,索引不合理或者业务代码实现导致:

        slow_query_log_file开始并且查询慢查询日志;

        通过explain排查索引问题;

        调整数据修改索引;业务代码层限制不合理访问。

    Mysql语句:

      常用连接:

        内连接:两个表都存在匹配时,才会返回匹配行;

        外连接:返回一个表的行,即使另一个没有匹配;

        全连接:只要某一个表存在匹配就返回

      内连接:

        将左表和右表能够关联的数据连接返回;

        类似于求两个表的“交集”;

        select * from A inner join B on a.id=b.id

       外连接:

        左连接:返回左表的所有记录,即使右表中没有任何满足匹配的记录;

          

      右连接:返回右表的所有记录,即使左表中没有任何满足匹配的记录;

      

      全连接:

        只要某一个表存在匹配,就返回行;

        类似于求两个表的“并集”;

        到时Mysql不支持,可以用left join 和union,right join联合使用模拟

      

    缓存及redis:

      缓存的使用场景,为什么使用:

        内存缓存(常见的有Redis,Memcached):

        缓解关系数据库(常见的是Mysql)并发访问的压力:热点数据;

        减少响应时间:内存IO速度比磁盘快;

        提升吞吐量:Redis等内存数据库单机就可以支撑很大并发

    redis和memcached的区别:

      https://www.cnblogs.com/457248499-qq-com/p/7392653.html

    resis常用数据类型及使用场景:

      String(字符串):用来实现简单的KV键值对存储,比如计数器;

      List(链表):实现双向链表,比如用户的关注,粉丝列表;

      Hash(哈希表):用来存储彼此相关信息的键值对;

      Set(集合):存储不重复元素,比如用户的关注者;

      Sorted Set(有序集合):实时信息排行榜

    Redis内置实现:使用C语言实现;

            String:整数或者sds(Simple Dynamic String);

            List:ziplist(压缩链表,通过一个连续的内存块实现list结构,其中的每个entry节点头部保存前后节点长度信息,实现双链表功能)或者double linked list;

            Hash:ziplist或者hashtable(可以自己设置,大于某个值使用什么);

            Set:intset或者hashtable;

            SortedSet:skiplist跳跃表

    时间空间复杂度。。。。

    Sorted Set为了简化实现,使用skiplist而不是平衡树实现:

    Redis有哪些持久化的方式:

      快照方式:把数据快照放在磁盘二进制文件中,dump.rdb;

      AOF(Append Only File):每一个写命令追加到appendonly.aof中;

      可以修改Redis配置实现使用哪种方式

    Redis的事务:

      将多个请求打包,一次性,按序执行多个命令的机制;

      Redis通过MULTI,EXEC,WATCH等命令实现事务功能;

      Python redis-py,pipeline=conn.pipeline(transaction=True)

    Redis实现分布式锁:

      使用setnx实现加锁,可以同时通过expire添加超时时间;

      锁的value值可以使用一个随机的uuid或者特定的命名;

      释放锁的时候,通过uuid判断是否是该锁,是则执行delete释放锁

    使用缓存的模式:

      Cache Aside:同时更新缓存和数据库;

      Read/Write Througr:先更新缓存,缓存负责同步更新数据库;

      Write behind Caching:先更新缓存,缓存定期异步更新数据库。

      (先更新数据库后更新缓存,并发写操作可能导致缓存读取的是脏数据,一般先更新数据库然后删除缓存)

    缓存穿透的问题:

      大量在缓存查询不到的数据的请求落到后端数据库,数据库压力增大

      由于大量缓存查不到就去数据库取,数据库也没有要查的数据(爬虫id遍历,有可能大量的id没有)

      解决:对于没查到的返回为None的数据也缓存;

      插入数据的时候删除相应缓存,或者设置较短的超时时间

    缓存击穿的问题:

      某些非常热点的数据key过期,大量请求达到后端数据库;

      热点数据key失效导致大量请求达到数据库增加数据库压力;

      分布式锁:获取锁的线程从数据库拿数据更新缓存,其他线程等待

      异步后台更新:后台任务针对过期的key自动刷新

    缓存雪崩:

      大量缓存不可用或者大量的缓存key同时失效(或重启缓存服务器),大量的请求直接打到数据库

        解决:多级缓存:不同级别的key设置不同的超时时间;

           随机超时:key的超时时间随机设置,防止同时超时;

           架构层:提升系统的可用性。监控,报警完善

    Mysql问题:https://blog.csdn.net/HeatDeath/article/details/79833462

    Redis实现分布式锁:https://baijiahao.baidu.com/s?id=1623086259657780069&wfr=spider&for=pc

六.Python web

   WSGI;常见Web框架:

   什么是WSGI?为什么需要它?

     Python Web Server Gateway Interface(pep3333)

     解决Python Web Server乱象mod_python,CGI,FastCGI等

     描述了Web Server(Gunicorn/uWSGI)如何与web框架(Flask/Django)交互,Web框架如何处理请求 

     def application(environ,start_response):

      application就是WSGI app,一个可调用的对象

      参数:

        environ:一个包含WSGI环境信息的字典,由WSGI服务器提供,常见的key有PATH_INFO,QUERY_STRING等

        start_response:生成WSGI响应的回调函数,接收两个参数,status和headers

      函数返回响应体的迭代器

  编写兼容WSGI的小应用(使用Python内置的WSGI server):  

    函数: 

 def myapp(environ,start_response):
status="200 OK"
headers=[('Content-Type','text/html;charset=utf8')]
start_response(status,headers)
#可迭代对象,字节
return [b'<h1>Hello World</h1>'] if __name__=="__main__":
from wsgiref.simple_server import make_server
httpd=make_server('127.0.0.1',8888,myapp)
httpd.serve_forever()

效果

      封装成类:

 class Application(object):

     def __init__(self,routers,**kwargs):
self.routers=routers def __call__(self, environ, start_response):
try:
request=Request(environ)
callback,args=routers.match(request.path)
response=callback(request,*args)
except FileNotFoundError:
response=Response("<h1>Not Found</h1>",status=404)
start_response(response.status,response.headers.items())
return iter(response)

  常用Python Web框架对比:

    Django VS Flask VS Tarnado

    Django大而全,内置ORM,Admin等组件,第三方插件较多

    Flask:微框架,插件机制,比较灵活

    Tornado:异步支持的微框加和异步网络库

  什么是MVC:Model(模型),View(视图),控制器(Controller)【解耦数据,展示和操作】

    Model:负责业务对象和数据库的交互(ORM)

    View:负责与用户的交互展示

    Controller:接收请求参数调用模型和视图完成请求

  什么是ORM(对象关系映射):提升开发效率和维护性

    用于实现业务对象与数据表的字段映射

    优势:代码更加面对对象,代码量更少,灵活性高,提升开发效率;

    缺点:拼接对象比较耗时,有一定性能影响

  一个Web框架的组成:

    中间件:用于请求之前和请求之后做一些处理(比如记录日志等)

    路由,表单验证,权限认证,ORM,视图函数,模板渲染,序列化

    第三方插件:Redis连接,RESTful支持等

  什么是Gunicorn:gunicorn -w 4 myapp:app(部署4个进程)

    纯Python编写的高性能WSGI Server

    pre-fork预先分配多个worker进程处理请求(master-slave)

    多种worker支持:Sync/Async(Gevent)/Tornado/AsyncIO

  Web安全:

    SQL注入:

      通过构造特殊的参数传入Web应用,导致后端执行了恶意的SQL

      通常由于程序员未对输入进行过滤,直接动态拼接SQL产生

      可以使用开源工具sqlmap,SQLninja检测

       创建表并插入几条数据:

 from hashlib import md5
def ha_md(s):
m=md5()
m.update('haha'.encode('utf-8'))
return m.hexdigest() sql="""
create table users(
id int PRIMARY KEY not null auto_increment,
name varchar(50) null,
email varchar(50) null,
password varchar(50) null
);
insert into users(name,email,password) values('LYQ','lyq@haha.com','{0}');
insert into users(name,email,password) values('LYQ2','lyq2@haha.com','{1}');
insert into users(name,email,password) values('LYQ3','lyq3@haha.com','{2}');
""".format(ha_md(''),ha_md('adade'),ha_md('')) #sql注入演示代码
import os
import MySQLdb
db=MySQLdb.connect(host='localhost',
user='root',
passwd='',
db='test',
charset='utf8') cur=db.cursor()
cur.execute(sql)
cur.close()
db.commit()
db.close()

  

       直接拼接:如果password输入--(表示注释),只要name对了就能找到,修改为sql="select * from name=%s AND password=md5(%s)"就不会了

 #sql注入演示代码
import MySQLdb
db=MySQLdb.connect(host='localhost',
user='root',
passwd='',
db='test',
charset='utf8') cur=db.cursor()
name=input("请输入姓名:")
print("您1输入的姓名是:{0}".format(name))
password=input("请输入密码:")
print("您输入的密码是:{0}".format(password))
#直接拼接sql
sql="select * from name='"+name+"'"+" AND password=md5('"+password+"')"
cur.execute(sql)
cur.close()
db.commit()
db.close()

         如何防范SQL注入:

        web安全一大法则:永远不要相信用户的任何输入

        对输入参数做好检查(类型和范围);过滤和转义特殊字符;

        不要直接拼接sql,使用ORM可以大大降低sql注入的风险;

        数据库层:做好权限管理配置i;不要明文存储敏感信息

    XSS攻击:跨站脚本攻击

      恶意用户将代码植入到提供给其他用户使用的页面中,未经转义的恶意代码输出到其他用户的浏览器被执行;

      用户浏览页面的时候嵌入页面中的脚本(js)会被执行,攻击用户;

      主要分为两类:反射型(非持久型),存储型(持久型)

      如:博客评论:<script>alert("haha");</script>,如果没有转义js代码,访问会弹出haha;(存储型)

        还有的可以手机用户的document.cookie发送到指定的服务器上

        反射型如把脚本放在url参数里面,诱导用户去点击

      xss危害:

        盗用cookie,获取敏感信息;

        利用用户私人账号执行一些违法行为,比如盗取个人或者商业资料,执行一些隐私操作;

        甚至可以在一些访问量很大的网站上实现DDos攻击

      如何访问xss攻击:

        过滤(输入和参数)。对敏感标签<script><img> <a>等进行过滤。;

        转义。对常见符号(“&”,“<”,“and”)转义(python3有个html.escape转义);

        设置HttpOnly禁止浏览器访问和操作Document.cookie

    CSRF:跨站请求伪造

      利用网站对已认证用户的权限去执行未授权的命令的一种恶意攻击;

      攻击者会盗用你的登录信息,以你的身份模拟发送请求;

      web身份认证机制只能识别一个请求是否来自某个用户的浏览器,但是无法保证请求是用户自己或者批准发的

    csrf:https://www.cnblogs.com/lovesong/p/5233195.html

    csrf攻击具备的两个条件:

      受害者已经登录到了目标网站并且没有退出(保持登录状态);

      受害者访问攻击者链接或者吧表单

      二者缺一不可

    如何防范csrf:不要再任何GET请求中有任何数据修改操作

      令牌操作(STP):再用户请求的表单中嵌入一个隐藏的csrf_token,服务端验证其是否与cookie中的一致(基于同源策略其他网站是无法获取cookie中的csrf_token)  

      黑客是拿不到你cookie中的csrftoken值的,前提是网站本身没有xss漏洞;

      如果是js提交需要先从cookie获取csrf_token作为X-CSRFToken请求头提交

      其他:检测来源HTTP Referer(容易被伪造);验证码方式(安全但是繁琐)

  前后端分离和Restful:

    什么是前后端分离:

      后端只负责数据接口,不再渲染模板,前端获取数据并呈现

    前后端分离的意义和方式:

      前后端解耦,接口复用(前端和客户端公用接口),减少开发量;

      各司其职,前后端同步开发,提升工作效率。定义好接口规范 ;

      更有利于调试,测试和运维部署

    什么是RESTfulL:

      表现层状态转移,由HTTP协议的主要设计者Roy Fielding提出;

      资源(使用URL指向的一个实体),表现层(资源的表现形式,比如图片,HTML文本等),状态转化(GET,POST,PUT,DELETE等动词来操作资源,实现资源状态的改变);

      是一种资源为中心的web软件架构风格,可以用Ajax和RESTful web服务构建应用

    设计的概念和准则:

      所有事物抽象为资源,资源对应唯一的标识;

      资源通过接口进行操作实现状态转移,操作本身是无状态的

    什么是RESTful API:

      RESTful风格的API接口:

        通过HTTP GET/POST/DELETE 获取/新建/更新/删除资源

        一般通过JSON格式返回数据;

        一般web框架都有相应的插件支持RESTful API

    如何设计RESTful API

      GET http://[hostname]/api/users 检索用户列表.....

    什么是https。。。http和https的区别。。。你了解对称加密和非对称加密码。。。

    HTTPS的通信过程?

六.系统设计

  什么是系统设计:

    系统设计是一个定义系统架构,模块,接口和数据满足特定需求的过程;

    比如设计一个短网址服务,评论服务,Feed流服务,抢红包系统;

    微服务架构很多系统被按照业务拆分,需要单独设计一个系统服务

  系统设计难点:中高级工程师必经之路

    需要具备相关领域,算法经验,有一定的架构设计能力;

    熟悉后端技术组件,比如消息队列,缓存,数据库,框架;

    具备文档撰写,流程图绘制,架构设计,编码实现等综合能力

  系统设计的要素:

    使用场景和限制条件;

    数据存储设计;

    算法模块设计

  按照三个元素来答:

    什么场景和条件下使用;(比如短网址系统提供站内各种服务生成短网址;限制条件:用户估计有多少?至少要能支撑多少用户(服务)?估算并发qps:峰值qps是多少?平均qps是多少)

    设计存储模块;

    设计算法模块

    数据存储设计:

    

    短网址系统的存储设计?需要存储哪些字段?

    如何设计短网址系统?

      按照设计数据库,需要哪些字段,使用类型》数据增长规模;

      数据库选型:是否需要持久化?使用关系型还是NoSQL;

      如何优化?如何设计索引?是否可以使用缓存?

    算法模块设计:

      需要哪些接口?几口如何设计?

      使用什么算法或者模型?

      不同实现方式之间的优劣对比?如何取舍?

    扩展:用户多了,qps高了如何处理?

        数据存储多了不够存了如何处理?

       故障如何处理?单点失败?多点失败,雪崩问题

  短网址系统的设计和实现:

    什么是短网址系统,包含哪些功能?

      把一个长网址转成短网址的服务。

      比如https://bitly.com。

      转换之后网址的后缀不超过7位(字符或数字)

    使用场景:

      一个长网址转成短网址并存储;根据短网址还原长url;

      要求短网址的后缀不超过7位(大小写字母和数字);

      预估峰值插入请求数量级:数百;查询请求数量级:数千。

    数据存储设计:

      根据需求设计数据存储方式:

        使用Mysql即可满足;

        需要的字段有哪些:

          id token(索引设计) url(原网址) created_time

    短网址生成算法有哪些?对比优缺点

      两个API:long2short_url,short2long_url

      md5摘要算法:得到的是32位,可以阶段,取前7个字符,但是冲突/

      类似于62进制的数字。二进制:0,1;十六进制0-9,a-f;十进制->62进制

      十进制->二进制转换:

 #不断取余,倒序输出
def mybin(num):
if num==0:
return 0
result=[]
while num:
num,rem=divmod(num,2)
result.append(str(rem))
return ''.join(reversed(result)) print(mybin(10))

      十进制->16进制:递增序列算法

 CHARS='abcdefghijklmnopqrstuvwxwzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
def encode(num):
if num==0:
return CHARS[0]
res=[]
while num:
num,rem=divmod(num,len(CHARS))
res.append(CHARS[rem])
return ''.join(reversed(res))
print(encode(61))

      (需要自增id,需要一个计数器Redis incr)

       request->redis incr index->token->mysql->url

    如何设计秒杀系统:

      什么是秒杀系统?有没有用过?

      如何根据三要素设计?

      秒杀系统涉及到哪些后端组件?

      https://www.jianshu.com/p/d789ea15d060      

   

      

Python面试知识点小结的更多相关文章

  1. Python基础知识点小结

    1.Python基础知识 在Python中的两种注释方法,分别是#注释和引号('''   ''')注释,#注释类似于C语言中的//注释,引号注释类似于C语言中的/*   */注释.接着在Python中 ...

  2. Python面试简介及并行并发

    今天的分享内容大体如下: 一. 面试 1. 什么是面试 2. 优秀的面试 二. Python综述 1. Python设计哲学及版本变迁 2. Python发展现状及其他语言使用场景 3. GIL 4. ...

  3. C++重要知识点小结---3

    C++重要知识点小结---1:http://www.cnblogs.com/heyonggang/p/3246631.html C++重要知识点小结---2:http://www.cnblogs.co ...

  4. C++重要知识点小结---2

    C++重要知识点小结--1 :http://www.cnblogs.com/heyonggang/p/3246631.html 1.C++允许程序员声明一个不能有实例对象的类,这样的类惟一的用途是被继 ...

  5. 知名互联网公司校招 Java 开发岗面试知识点解析

    天之道,损有余而补不足,是故虚胜实,不足胜有余. 本文作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向.在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几 ...

  6. Java 面试知识点解析(一)——基础知识篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  7. Java 面试知识点解析(二)——高并发编程篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  8. Java 面试知识点解析(三)——JVM篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  9. Java开发岗面试知识点解析

    本文作者参加过多场面试,应聘岗位均为 Java 开发方向.在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几个部分: Java 基础知识点 Java 常见集合 高并发编 ...

随机推荐

  1. Spring笔记01_下载_概述_监听器

    目录 Spring笔记01 1.Spring介绍 1.1 Spring概述 1.2 Spring好处 1.3 Spring结构体系 1.4 在项目中的架构 1.5 程序的耦合和解耦 2. Spring ...

  2. Python 面向对象之反射

    Python 面向对象之反射 TOC 什么是反射? hasattr getattr setattr delattr 哪些对象可以使用反射 反射的好处 例子一 例子二 什么是反射? 程序可以访问.检查和 ...

  3. 学习前端笔记1(HTML)

    (注:此文是在看过许多学习资料和视频之后,加上自身理解拼凑而成,仅作学习之用.若有版权问题,麻烦及时联系) 标准页面结构: HTML发展历史:  注:每一种HTML需要有对应的doctype声明. H ...

  4. 外媒评李开复的《AI·未来》:四大浪潮正在席卷全球

    外媒评李开复的<AI·未来>:四大浪潮正在席卷全球 https://mp.weixin.qq.com/s/oElub0QOYjOROhqN3ULUkg [网易智能讯 9月17日消息]李开复 ...

  5. 【Dojo 1.x】笔记3 等待DOM加载完成

    有的web页面总是得等DOM加载完成才能继续执行功能,例如,待页面DOM加载完成后,才能在DIV上进行渲染图形. Dojo提供了这个功能的模块,叫domReady,但是由于它很特殊,就在结尾加了个叹号 ...

  6. TLS / SSL密码强化的建议

    TLS / SSL密码强化的建议 传输层安全性(TLS)及其前身安全套接字层(SSL)是广泛使用的协议,旨在通过身份验证,加密和完整性来保护客户端和服务器之间的数据传输安全. 与常见假设相反,TLS ...

  7. 使用docker部署skywalking

    使用docker部署skywalking Intro 之前在本地搭建过一次 skywalking + elasticsearch ,但是想要迁移到别的机器上使用就很麻烦了,于是 docker 就成了很 ...

  8. JMeter接口测试 (二) ~ 参数化

    对应qq群号:616961231 上篇内容介绍了jmeter的基本使用, 略微提了如何做参数化,  本篇对参数化做进一步深入讲解, 参数化可以将一个变量使用不同数据, 比如有多个用户下单购买商品,调用 ...

  9. 第四篇 CSS

    在标签上设置style属性: background-color:#2459a2: height:48px: ... 编写CSS样式: 如何注释:/* 或 */ 一. 在标签的属性中编写 <!DO ...

  10. Linux简单配置SendMail发送邮件

    本文简单整理了一下如何在Linux服务器上安装.配置SendMail发送邮件的步骤,此文不是配置邮件服务器,具体测试环境为CentOS Linux release 7.2.1511 (Core) ,如 ...