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