Python面试知识点小结

时间:2022-12-10 05:40:07

一.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:

Python面试知识点小结

    4.2自己写的猴子补丁:

      Python面试知识点小结

   5.is和==:

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

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

Python面试知识点小结

  7.Python之禅:

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

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

Python面试知识点小结

                

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8.Python2/3的差异:

    Python3改进:

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

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

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

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

Python面试知识点小结

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

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

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

Python面试知识点小结

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

Python面试知识点小结

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

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

Python面试知识点小结

      生成的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)

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

Python面试知识点小结

     Python面试知识点小结

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

Python面试知识点小结

默认参数只计算一次

    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操作来分析字节码。

Python面试知识点小结

    如何剖析程序性能:

      使用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.... 

Python面试知识点小结

    collections中常用模块:

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

Python面试知识点小结

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

Python面试知识点小结

      Counter:实现计数的功能

Python面试知识点小结

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

Python面试知识点小结

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

Python面试知识点小结

      dict低层使用的hash表:

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

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

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

      list vs tuple:

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

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

       Python面试知识点小结

        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请求的组成:

      状态行;

      请求头;

      消息主体(可能为空)

    Python面试知识点小结

Python面试知识点小结

    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

      Python面试知识点小结

    使用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语句:

      常用连接:

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

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

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

Python面试知识点小结

Python面试知识点小结

      内连接:

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

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

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

Python面试知识点小结

       外连接:

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

          Python面试知识点小结

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

      Python面试知识点小结

      全连接:

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

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

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

      Python面试知识点小结

    缓存及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()

Python面试知识点小结

效果

      封装成类:

 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()

  Python面试知识点小结

       直接拼接:如果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身份认证机制只能识别一个请求是否来自某个用户的浏览器,但是无法保证请求是用户自己或者批准发的

Python面试知识点小结

    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