面试2

面试记录/总结/心得

刚开始看了一些关于外包外派等形式的工作很多资料、知乎文章,弊大于利,感觉还是挺轻浮,说先不考虑外包、外派的形式,所以Boos上很多主动聊要简历的都回绝了,现在想想自己真的很天真,现在在拉勾网一键投递的简历,几乎是没回音,石沉大海,Boss上也是机会没回信,压根没有投简历的机会,内心的煎熬和恐慌,胡思乱想了很多,抓狂,感觉太难了!

1、亿可能源科技

Python开发 3~5年 15k~18k 杨浦区 时间2020.3.24/11:00

    # 疫情在家没怎么学习,3.15到的上海,感觉没准备好不敢投简历,通过两周多的艰苦奋战(一般两点睡觉),改简历,简单复习后投简历,第一次面试不懂流程,比较紧张,发挥很不自然,言谈吞吐不流畅,面试官首先让做一下自我介绍,看技能项,寒暄几句,说基础挺好的哦,那我问你一下基础的。大部分问的都是一下作用、原理、应用场景,自我感觉准备太不充分了,都是模棱两可的简约答答就过了,一度很尴尬,简历写的也不是很好,该写不该写的内容等没有优化,自我介绍是说是本专业的自动化,就会问你是如何接触到Python,怎么学的等等问题,努力提升技巧和专业知识,加油吧!

2、上海钱咖网络科技有限公司

Python后端开发 1-3年 本科 10k~15k 时间2020.4.1/15:00 加HR提前预约 钉钉视频 远程面试 对面四个人 架构师 项目主管 组长 CTO

# 第一轮技术面 面试问的一些基础的问题,经过一段时间的复习总结,感觉比上一次相对来说有所进步,但还是缺乏实际生产环境,问的一些问题回答比较粗略,似乎暴露了,git问的比较详细,公司用的什么代码管理工具,答的是禅道,其实应该是公司内部自己搭建部署的gitlab~~~~

3、python面试总结

  • 1、可变不可变数据类型有哪些,可变不可变指的是?

#可变数据类型:值发生改变时,内存地址不变,即id不变,证明在改变原值
#不可变类型:值发生改变时,内存地址也发生改变,即id也变,证明是没有在改变原值,是产生了新的值。
"""
数据类型 可变/不可变
整型 不可变
字符串 不可变
元组 不可变
列表 可变
集合 可变
字典 可变

"""
  • 2、装饰器、迭代器、生成器作用,应用场景?

迭代器:iterable(可迭代的,可重复的)
    就是更新重复的过程,每次的迭代都必须基于上一次的结果,内部都是基于for循环原理
  # 为什么要用迭代器:迭代器提供了一个不需要按索引取值的方式。
注:迭代器对象无论执行多少次__iter__方法得到的还是迭代器对象本身
  迭代器取值的特点:只能依次往后取 不能倒退 取完就会报错
 
   #生成器:人为自定义实现迭代的功能就叫生成器
关键字:  yield  
函数内如果有yield关键字,那么加括号执行函数的时候并不会触发函数体代码的运行 ,程序中遇到yield会暂停运行等待调入参数。
# 生成器表达式:
    把列表解析的 [] 换成 () 得到的就是生成器表达式
          #生成器表达式 sum(i for i in range(100000000))#几乎不占内存
  • 3、yeild和return的区别?

yield
   1.帮你提供了一种自定义生成器方式
   2.会帮你将函数的运行状态暂停住
   3.可以返回值

# 与return之间的区别
   相同点:都可以返回值,并且都可以返回多个
   不同点:
       yield可以返回多次值,而return只能返回一次函数立即结束
       yield还可以接受外部传入的值
  • 4、闭包函数是什么,有什么作用,应用场景?

# 定义:函数里套有其他函数的形式,两层以上的.内部函数包含对外部作用域而非全局作用域名字的引用,该内部函数称为闭包函数
 装饰器的本质:一个闭包函数(可调用的对象)
 #装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展(不修改被装饰对象的源代码和调用方式)
#统计代码运行的时间
def timer(func):
   def inner(a):
       start = time.time()
       func(a)
       print(time.time() - start)
   return inner
@timer  
def func1(a):
   print(a)
func1(1)

# 装饰器固定格式
def outter(func):
   def inner(*args,**kwargs):
       print('执行被装饰函数之前 你可以做的操作')
       res = func(*args,**kwargs)
       print('执行被装饰函数之后 你可以做的操作')
       return res
   return inner
  • 5、面向对象中装饰器@classmethod,@perpory的作用,应用场景?

@classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。(# 绑定到类的发方法)
@property属性:将类方法用类属性的形式进行调用,封装成普通的函数,对私有属性进行修改获取等setter getter deltter
# 作用:
我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
 #使用场景:
1.修饰方法,是方法可以像属性一样访问。
class DataSet(object):
 @property
 def method_with_property(self): ##含有@property
     return 15
 def method_without_property(self): ##不含@property
     return 15
l = DataSet()
print(l.method_with_property) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。
print(l.method_without_property())  #没有加@property , 必须使用正常的调用方法的形式,即在后
python面向对象中的反射:通过字符串的形式操作对象相关的属性.
  • 6、git中rebase和merge的使用场景?有什么区别?

#rebase会把你当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像你从公共分支又重新拉出来这个分支一样。
# merge 会把公共分支和你当前的commit 合并在一起,形成一个新的 commit 提交
注意:
# 不要在公共分支使用rebase,会轻易的把之前commit的内容覆盖掉
本地和远端对应同一条分支,优先使用rebase,而不是merge
# 为什么不要再公共分支使用rebase?
因为往后放的这些commit 都是新的,这样其他从这个公共分支拉出去的人,都需要再 rebase,相当于你 rebase 东西进来,就都是新的 commit 了。
   git rebase -I dev 可以将dev分支合并到当前分支
  • 7、Linux中vim编辑器下如何查看某个特定的内容?

#命令行模式下  输入 “/+要查询的内容”
  • 8、Docker中两台服务器是如何通信的,修改哪些参数?

主要解决容器与容器之间,容器与主机之间的网络互通问题
    """
  --link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。
    docker -d --link 容器名:连接别名 nginx
案例:
# 第一步:创建一个容器
docker run -d --name nginx nginx
# 第二步:启动一个测试容器
docker run -it --link nginx:nginx centos
# 第三步:在测试容器中测试访问目标容器,看是否可以正常访问
使用 docker network create 命令创建用户定义的网桥网络。

docker run --name my-nginx 
  --network chenyang 

    """
  • 9、数据库中数据连接池的作用和使用场景?

# Python编程中可以使用MySQLdb进行数据库的连接及诸如查询/插入/更新等操作,但是每次连接MySQL数据库请求时,都是独立的去请求访问,相当浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响。因此,实际使用中,通常会使用数据库的连接池技术,来访问数据库达到资源复用的目的。
python的数据库连接池包 DBUtils:
1、写一个创建连接池,获取连接以及重新连接数据库的模块:
  # libby_db_pool.py
    # 代码如下:
    #-*- coding:utf-8 -*-
    from DBUtils.PooledDB import PooledDB
    import pymssql #sqlserver数据库适配器
    from pymssql import OperationalError, InternalError, ProgrammingError
    HOST = "127.0.0.1"
    PORT = "8080"
    CHARSET = "utf8"
    NAME = "zkeco_oracle"
    USER = "Eric"
    PASSWORK = "123456"
    conn_args = {
        'host':"%s"%HOST,
        'port':"%s"%PORT,
        'database':"%s"%NAME,
        'charset':"%s"%CHARSET,
        'user':"%s"%USER,
        'password':"%s"%PASSWORK
    }
    """
        mincached : 启动时开启的闲置连接数量(缺省值 0 以为着开始时不创建连接)
        maxcached : 连接池中允许的闲置的最多连接数量(缺省值 0 代表不闲置连接池大小)
        maxshared : 共享连接数允许的最大数量(缺省值 0 代表所有连接都是专用的)如果达到了最大数量,被请求为共享的连接将会被共享使用
        maxconnecyions : 创建连接池的最大数量(缺省值 0 代表不限制)
        blocking : 设置在连接池达到最大数量时的行为(缺省值 0 或 False 代表返回一个错误<toMany......>; 其他代表阻塞直到连接数减少,连接被分配)
        maxusage : 单个连接的最大允许复用次数(缺省值 0 或 False 代表不限制的复用).当达到最大数时,连接会自动重新连接(关闭和重新打开)
        setsession : 一个可选的SQL命令列表用于准备每个会话,如["set datestyle to german", ...]
    """
    args = (10,10,30,100,True,0,None)
    class DbManager():
        def __init__(self):
            try:
                self._pool = PooledDB(pymssql,*args,**conn_args)
            except Exception,e:
                print "The parameters for DBUtils is:",conn_args
        def _getConn(self):
            return self._pool.connection()
    _dbManager = DbManager()
    def getConn():
        """ 获取数据库连接 """
        return _dbManager._getConn()
    def _reConn():
        """ 重新连接数据库 """
        global _dbManager
        re = False
        try:
            _dbManager = DbManager()
            re = True
        except:
            import traceback
            traceback.print_exc()
        finally:
            return re
    import datetime
    def reConn():
        print "%s: now try to reconnect Database!"%(datatime.datatime.now())
        flag = _reConn()
        if flag:
            print "%s reconnect database success!"%(datatime.datatime.now())
        else:
            print "%s reconnect database failed!"%(datatime.datatime.now())
  • 10、从输入url到页面加载完成发生了什么?

# HTTP协议的四大特性:
    1.基于TCP/IP作用于应用层的协议
    2.基于请求响应
    3.无状态 (cookie,session,token)
    4.无连接(长连接:websocket)
    数据格式:请求首行、请求头、请求体
    
    # 响应状态码:
    	1xx  	信息,服务器收到请求,需要请求者继续执行操作
        2xx     成功,操作被成功接收并处理
        3xx     重定向,需要进一步的操作以完成请求
        4xx     客户端错误,请求包含语法错误或无法完成请求
        5xx     服务器错误,服务器在处理请求的过程中发生了错误
    # 常见状态码&含义
        200 - 请求成功,已经正常处理完毕
        301 - 请求永久重定向,转移到其它URL
        302 - 请求临时重定向
        304 - 请求被重定向到客户端本地缓存
        400 - 客户端请求存在语法错误
        401 - 客户端请求没有经过授权
        403 - 客户端的请求被服务器拒绝,一般为客户端没有访问权限
        404 - 客户端请求的URL在服务端不存在
        500 - 服务端永久错误
 
  # 大致的流程:
 """
  1、浏览器的地址栏输入URL并按下回车。
 2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
 3、DNS解析URL对应的IP。
 4、根据IP建立TCP连接(三次握手)。
 5、HTTP发起请求。
 6、服务器处理请求,浏览器接收HTTP响应。
 7、渲染页面,构建DOM树。
 8、关闭TCP连接(四次挥手)。

 """
  • 11、实现需求:统计代码运行的时间?

#方法1
start = time.time()
run_fun()
end = time.time()
print end-start

#方法2
import datetime
start = datetime.datetime.now()
run_function():
    # do something

end = datetime.datetime.now()
print (end-start)

#方法3
封装统计时间的函数,用装饰器实现
  • 12、阐述MVC和MTV架构?

MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分: 模型(Model) -- 存取数据  视图(View) -- 信息展示 控制器(Controller) -- 逻辑的控制 
# 具有耦合性低、重用性高、生命周期成本低等优点

# Django框架的不同之处在于它拆分的三部分为:Model(模型)、Template(模板)和View(视图),也就是MTV框架 Model(模型):负责业务对象与数据库的对象(ORM)  Template(模版):负责如何把页面展示给用户 View(视图):负责业务逻辑,并在适当的时候调用Model和Template 此外,Django还有一个urls分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和 Template
  • 13、数据库常见的优化方法?

微信图片_20200407200635

"""
性能优化
    表的设计合理化,符合三大范式(3NF)
    1NF是对属性的原子性约束,要求属性(列)具有原子性,不可再分解;(只要是关系型数据库都满足1NF)
    2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
    3NF是对字段冗余性的约束,它要求字段没有冗余。 没有冗余的数据库设计可以做到。
添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
    较频繁的作为查询条件字段应该创建索引;
    唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
    更新非常频繁的字段不适合创建索引
    不会出现在WHERE子句中的字段不该创建索引
    分表技术(水平分割、垂直分割);
    读写[写: update/delete/add]分离;
    存储过程 [模块化编程,可以提高速度];
    对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ];
    mysql服务器硬件升级;
    定时的去清除不需要的数据,定时进行碎片整理(MyISAM)。
SQL语句优化
    通过show status命令了解各种SQL的执行频率;
    定位执行效率较低的SQL语句-(重点select;
    通过explain分析低效率的SQL;
    确定问题并采取相应的优化措施。
添加索引
    索引主要可以分为以下几种:
    主键索引,主键自动的为主索引 (类型Primary);
    唯一索引 (UNIQUE);
    普通索引 (INDEX);
    全文索引 (FULLTEXT) [适用于MyISAM] ——》sphinx + 中文分词 coreseek [sphinx 的中文版 ];
    综合使用=>复合索引
可能使用到索引
    对于创建的多列索引,只要查询条件使用了最左边的列,索引一般就会被使用。
    对于使用like的查询,查询如果是 ‘%aaa’ 不会使用到索引, ‘aaa%’ 会使用到索引。

不使用索引
    如果条件中有or,即使其中有条件带索引也不会使用。
    对于多列索引,不是使用的第一部分,则不会使用索引。
    like查询是以%开头
    如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须’’)
    如果mysql估计使用全表扫描要比使用索引快,则不使用索引。"""
  • 14、数据库索引原理?

常见的:B+树索引(O(log(n))) 、聚集索引、hash索引、主键索引

# 创建时注意 一般来说,每张表都需要有一个主键id字段 常用于查询的字段应该设置索引 varchar类型的字段,在建立索引的时候,最好指定长度 查询有多个条件时,优先使用具有索引的条件 像LIKE条件这样的模糊搜索对于字段索引是无效的,需要另外建立关键词索引来解决 请尽量不要在数据库层面约束表和表之间的关系,这些表之间的依赖应该在代码层面去解决。
  • 15、简单的介绍一下的你了解的mysql中的存储引擎

    Innodb:默认的存储引擎 查询速度较myisam慢  但是更安全
    myisam:mysql老版本用的存储引擎
    memory:内存引擎(数据全部存在内存中)
    blackhole:无论存什么 都立马消失(黑洞)
  • 16、谈谈秒杀Rabbit+Redis的实现逻辑和遇到的技术难点

1.商品入库
2.把商品的ID存进redis中
3.当秒杀开始的时候,用户的请求到达的时候,先判断这个商品是否已经被秒杀,如果被秒杀返回,秒杀失败,如果存在进入rabbitmq队列。并在redis中把商品的ID(预处理)移除。
4.队列去处理订单,把订单保存进入订单列表,并把原始的商品信息改为已经秒杀。(用到事物)。
5.客户预秒杀的商品如果没有付款成功,则重新把数据库中的商品信息存在redis中。
6,如果使用分布式,则要把用户的信息进行session共享(放入redis中)。 SpringCloud session共享实现
  • 17、 如何解决跨域问题

为什么会有跨越问题?
因为所有支持Javascript的浏览器都会使用同源策略这个安全策略。
"""
方法1:安装django-cors-headers
方法2:使用JSONP
方案3.直接修改Django中的views.py文件,原理是修改请求头
	例如: //*表示支持所有网站访问,也可以额外配置相应网站
	 resp.setHeader("Access-Control-Allow-Origin", "*");
""""""
  • 18、 悲观锁和乐观锁了解吗谈谈实际开发场景

乐观锁:
    顾名思义,对当前操作的数据保持一个乐观的态度,认为不会有其他事务操作修改当前的数据记录。只有在提交事务更新时,会检测有没有被修改。若有则直接选择retry或定义的操作。例子:hiberante的version版本控制
悲观锁:
    悲观锁认为其他事务会对操作的数据进行修改,所以当查询时将数据上锁。若其他事务需要操作该数据则需要等待。例子:数据库中的行锁,表锁。
 # 使用场景
乐观锁:说应用场景之前,先要明白乐观锁是为了防止事务更新丢失。先讲一下更新丢失的问题
#商品扣库存的例子:   
#商品扣库存的问题
现象:数据与实际的库存数据不统一,缺失
"结合事务特性,在代码模块中,首先开启事务,事务回滚,在书写mysql查询语句时,书写update_new方法来更新库存的实际数据"
  • 两种锁如何选择?

需要结合这两种锁的特点,进行合理的选择
- 响应速度:选择乐观锁。要么冲突失败要么快速成功。悲观锁则需要等待释放锁才能被执行
- 冲突频率:频率高的话不应选择乐观锁,需要重试好几次,代价大。而悲观锁保证成功率
- 重试代价:若重试代价大则选择悲观锁

19、Git 产生冲突的原因和解决方案?

#git冲突场景
情景一:多个分支代码合并到一个分支时;
情景二:多个分支向同一个远端分支推送代码时;
	<1>两个分支中修改了同一个文件(不管什么地方)
  <2>两个分支中修改了同一个文件的名称

解决冲突的方法:
#情景一:在当前分支上,直接修改冲突代码--->add--->commit。
#情景二:在本地当前分支上,修改冲突代码--->add--->commit--->push
注:借用vim或者IDE或者直接找到冲突文件,修改。

20、JWT的三段数据包含了什么?实现token 的细节?

json web token,一般用于用户认证(前后端分离、微信小程序/APP开发) 其他直接用cookies/session即可.

用户登录,服务端给用户返回一个token(服务器不保存),

以后用户再来访问,需要携带token,服务器获取token后,再做token的校验, 优势:相较于传统的token相比,它无需在服务器端保存token。

"""
1) jwt = base64(头部).base(载荷).hash256(base64(头部).base(载荷).密钥)
2) base64是可逆的算法、hash256是不可逆的算法
3) 密钥是固定的字符串,保存在服务器
"""

#使用:
pyjwt.encode 生成token
pyjwt.decode token解密

21、简单介绍django的请求生命周期

wsgi—中间件—路由----视图—中间件—wsgi

django请求生命周期

22、cookie和session的区别?

"""
1、cookie数据存放在客户端,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上,当访问增多,会比较占用你服务器的性能,考虑性能应当使用cookie。
4、不同浏览器对cookie的数据大小限制不同,个数限制也不相同。
5、可以考虑将登陆信息等重要信息存放为session,不重要的信息可以放在cookie中。

"""

23、实现单例的几种方法?

#单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
1、基于__new__方法实现的单例(推荐,方便)
class Singleton(object):    
    def __new__(cls,*args,**kwargs):        
        if not hasattr(cls,"_instance"):            												cls._instance=super(Singleton,cls).__new__(cls)   
            return cls._instance 
        
a=Singleton()
b=Singleton()
c=Singleton()
print (a)
print (b)
print (c)
输出结果:
<__main__.Singleton object at 0x000002474C92D550><__main__.Singleton object at 0x000002474C92D550><__main__.Singleton object at 0x000002474C92D550>
    
2、基于元类实现的单例
import threading
class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name

obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)

3、使用类实现的单例模式
class Singleten(object):
    def __init__(self):
        pass
    @classmethod
    def instance(cls,*args,**kargs)
    	if not hasattr(Singleten,"_instance"):
            Singleten._instance = Singleten(*args,**kargs)
        return Singletn._instance
    
4、使用装饰器实现的单例
def Singleton(cls):
    _instance = {}
    def _singleton(*args,**kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args,**kargs)
          return _instance[cls]
    return _singleton


@Singleton
class A(object):
    a = 1
    def __init__(self,x=0)
    	self.x=x
a1=A(2)
a2=A(3)

23、什么是深浅拷贝,区别?

拷贝就是拷贝,何来深浅之说?
Python中,对象的赋值,拷贝(深/浅拷贝)之间是有差异的,如果使用的时候不注意,就可能产生意外的结果
其实这个是由于共享内存导致的结果。
拷贝:原则上就是把数据分离出来,复制其数据,并以后修改互不影响。
#1、浅拷贝:数据半共享(复制其数据独立内存存放,但是只拷贝成功第一层)
l1 = [1,2,3,[11,22,33]]
l2 = l1.copy()
print(l2) #[1,2,3,[11,22,33]]
l2[3][2]='aaa' # 只修改最外一层
print(l1) #[1, 2, 3, [11, 22, 'aaa']]
print(l2) #[1, 2, 3, [11, 22, 'aaa']]
l1[0]= 0
print(l1) #[0, 2, 3, [11, 22, 'aaa']]
print(l2) #[1, 2, 3, [11, 22, 'aaa']]
print(id(l1)==id(l2)) #Flase 所指向的内存地址没有发生变化

#2、深拷贝:数据完全不共享(复制其数据完完全全放独立的一个内存,完全拷贝,数据不共享)
深拷贝就是完完全全复制了一份,且数据不会互相影响,因为内存不共享。
import copy
l1 = [1, 2, 3, [11, 22, 33]]
# l2 = copy.copy(l1)  浅拷贝
l2 = copy.deepcopy(l1) # 深拷贝
print(l1,'>>>',l2)
l2[3][0] = 1111
print(l1,">>>",l2)
# 结果
>> 	[1, 2, 3, [11, 22, 33]] >>> [1, 2, 3, [11, 22, 33]]
	[1, 2, 3, [11, 22, 33]] >>> [1, 2, 3, [1111, 22, 33]]
 # 由此可见深拷贝就是数据完完全全独立拷贝出来一份。不会由原先数据变动而变动。

24、redis一些高级用法?

关系型数据库数据量过大,需要分库分表(把数据表拆到不同的库中)

分表:垂直分表、水平分表

image-20200407224220469

image-20200407224252335

Redis典型使用场景(高级用法)

"""
缓存系统:
计数器:网络访问量,转发量,评论数
消息队列:发布订阅,阻塞队列实现
排行榜:有序集合
社交网络:很多特性跟社交网络匹配,粉丝数,关注数
实时系统:垃圾邮件处理系统,布隆过滤器

"""

redis非关系型数据库,数据直接存储在内存中

redis的特性:速度快、持久化、多种数据结构、支持多种编程语言、功能丰富、主从复制、高可用和分布式

redis大部分是用来做缓存,因为速度快(内存数据库,单线程,单进程)

# geo地理位置(附近的人)、持久化方案、redis做读写分离、高可用redis哨兵模式、定位地理位置

25、gitFlow工作流

Gitflow 工作流通过为功能开发、发布准备和维护设立了独立的分支,让发布 迭代过程更流畅。严格的分支模型也为大型项目提供了一些非常必要的结构。

构成:

核心分支:master,dev

可能还会有:功能分支,bug修复分支,预发布分支

#github flow:是一个长期分支,就是master
    第一步:根据需求,从master拉出新分支,不区分功能分支或补丁分支。
    第二步:新分支开发完成后,或者需要讨论的时候,就向master发起一个pull request(简称PR)。
    第三步:Pull Request既是一个通知,让别人注意到你的请求,又是一种对话机制,大家一起评审和讨论你的代码。对话过程中,你还可以不断提交代码。
    第四步:你的Pull Request被接受,合并进master,重新部署后,原来你拉出来的那个分支就被删除。(先部署再合并也可。)

26、如何解决跨域问题?

# 跨域:跨域指的是浏览器不能执行其它网站的脚本,它是由浏览器的同源策略造成的,是浏览器对JavaScript 施加的安全限制。
同源:指的是:协议、端口、域名都相同
1、http://www.123.com/index.html 调用  http://www.123.com/welcome.jsp      协议、域名、端口号都相同,同源。
2、https://www.123.com/index.html 调用 http://www.123.com/welcome.jsp      协议不同,非同源。
3、http://www.123.com:8080/index.html 调用 http://www.123.com:8081/welcome.jsp   端口不同,非同源。
 # 解决跨域的问题:
1、response添加header
 //*表示支持所有网站访问,也可以额外配置相应网站
 resp.setHeader("Access-Control-Allow-Origin", "*");

2、在ajax中修改数据类型为”jsonp“
 $.ajax({
 2             type:"get",
 3             async:false,
 4             url:"http://localhost:8080/JavaWeb01/getPassWordByUserNameServlet?userName=Tom",
 5             dataType:"jsonp",//数据类型为jsonp
 6             jsonp:"backFunction",//服务端用于接收callBack调用的function名的参数
 7             success:function (data) {
 8                 alert(data["passWord"]);
 9             },
10             error:function () {
11                 alert("error");
12             }
13 
14         });
  
3、nginx进行请求的转发
	利用nginx反向代理,将请求分发到部署到相应项目的tomcat服务器,当然也不存在跨域问题。

26、事务、事务隔离级别、悲观锁、乐观锁的一些概念

# 事务的四大特性分别是(ACID):原子性、一致性、隔离性、持久性
"""
原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性: 执行事务前后,数据保持一致;
隔离性: 并发访问数据库时,一个用户的事物不被其他事物所干扰,各并发事务之间数据库是独立的;
持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
"""
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读
# 并发事务带来的问题
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致一下的问题。
4、脏读(Dirty read):
	当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
丢失修改(Lost to modify): 
	指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
不可重复读(Unrepeatableread): 
	指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
幻读(Phantom read): 
	幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复度和幻读区别:
不可重复读的重点是修改,幻读的重点在于新增或者删除。
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。

# 简述乐观锁和悲观锁
乐观锁和悲观锁都是一种思想,并不是真实存在于数据库中的一种机制。
# 悲观锁
当认为数据被并发修改的几率比较大,需要在修改之前借助于数据库锁机制,先对数据进行加锁的思想被称为悲观锁,又称PCC(Pessimistic Concurrency Control)。在效率方面
处理锁的操作会产生了额外的开销,而且增加了死锁的机会。当一个线程在处理某行数据的时候,其它线程只能等待。
# 乐观锁
乐观锁思想认为,数据一般是不会造成冲突的。只有在提交数据的时候,才会对数据的冲突进行检测。当发现冲突的时候,返回错误的信息,让用户决定如何去做。
乐观锁不会使用数据库提供的锁机制,一般的实现方式就是记录数据版本。

# MySQL事务隔离级别
mysql默认的事务隔离级别为repeatable-read 可重复读

27、2020.4.9下午2:30浦发银行外包面试:对面4个人,微信群聊的方式

1、range原理:常用在哪里?
2、redis怎么搭建集群?
3、常用的有哪些数据类型?hash(python - hash类型操作)
    在name对应的hash中获取根据key获取value
    hget(name,key)
    在name 对应的hash 中设备键值对
    hset(name, key, value)
    在name对应的hash中批量设置键值对
    hmset(name, mapping)
    在name对应的hash中获取多个key的值
    hmget(name, keys, *args)
    python中如何反转list?
反转list一共有3中方法?
    a=[1,2,3,4,5]
    1、list(reversed(a));
    2、sorted(a,revers=true)
    3、## 切片:a[: : -1]  其中[: : -1]代表从后向前取值,每次步进值为1,a【3: : -1】=[4,3,2,1] 代表从第3个坐标往前反转顺序输出,每次取1个值。
4、深浅拷贝区别?
5、项目介绍、从事的业务、有什么难点?
6、静态绑定方法、类装饰器、常见的装饰器,自己写过的装饰器?
7、闭包函数的原理及应用?
8、简单概述vue的请求生命周期?
生命周期钩子:
- 表示一个vue实例从创建到销毁的这个过程,将这个过程的一些时间节点赋予了对应的钩子函数
- 钩子函数: 满足特点条件被回调的方法
    new Vue({
    el: "#app",
    data: {
        msg: "message"
    },
    beforeCreate () {
        console.log("实例刚刚创建");
        console.log(this.msg             
    },
    created () {
        console.log("实例创建成功, data, methods已拥有");
        console.log(this.msg);
    },
    mounted () {
        console.log("页面已被vue实例渲染, data, methods已更新");
    }
    // 拿到需求 => 确定钩子函数 => 解决需求的逻辑代码块
})
9、序列化与反序列化数据类型?
    Json序列化 DataContractJsonSerializer和JavascriptSerializer
json	用于实现Python数据类型与通用(json)字符串之间的转换	dumps()、dump()、loads()、load()
pickle	用于实现Python数据类型与Python特定二进制格式之间的转换	dumps()、dump()、loads()、load()
shelve	专门用于将Python数据类型的数据持久化到磁盘,shelve是一个类似dict的对象,操作十分便捷	open()
#Python的JSON模块 序列化与反序列化的过程分别叫做:encoding 和 decoding。
encoding: 把Python对象转换成JSON字符串
decoding: 把JSON字符串转换成python对象        
10、常见的锁有哪些?有什么用?
11、微信小程序怎么部署到pc端?
12、cookies和session、token
13、多线程并发访问数据库的线程安全问题?
  #多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离,同时,又要小心死锁的发生。
     # 数据错乱和死锁的解决方法
     由于多个线程之间可以共享数据,那随之而来的问题就是:多个线程同时更改一个变量、使用一个资源时,就会出现#数据错乱、死锁等现象。              

28、Python中的各种锁?

# 一、全局解释器锁(GIL)
  1、什么是全局解释器锁
  在同一个进程中只要有一个线程获取了全局解释器(cpu)的使用权限,那么其他的线程就必须等待该线程的全局解释器(cpu)使用权消失后才能使用全局解释器(cpu),即时多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)。
  2、全局解释器锁的好处
     1、避免了大量的加锁解锁的好处
     2、使数据更加安全,解决多线程间的数据完整性和状态同步
  3、全局解释器的缺点
   多核处理器退化成单核处理器,只能并发不能并行。
同一时刻的某个进程下的某个线程只能被一个cpu所处理,所以在GIL锁下的线程只能被并发,不能被并行。
## gil总结
结论:在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
1、存在原因:
    GIL是一个互斥锁:保证数据的安全(以牺牲效率来换取数据的安全)
    阻止同一个进程内多个线程同时执行(不能并行但能并发)
    GIL全局解释器存在的原因是Cpython解释器的内存管理不是线程安全的
    同一个进程下的多个线程不能实现并行但是能够实现并发,多个进程下的线程能够实现并行。
2、多线程和多进程都有自己的优点,要根据项目需求合理选择
二、GIL全局解释器锁与普通锁的互斥锁的区别
对于不同的数据,要想保证数据的安全,需要加不同的锁处理
GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
保证的是同一个进行下多个线程之间的安全。
实例:
import time
import threading
def sub():
    global num
    num -= 1
    time.sleep(1)
num = 100  # 定义一个全局变量
l = []  # 定义一个空列表,用来存放所有的列表
for i in range(100):  # for循环100次
    t = threading.Thread(target=sub)  #每次循环开启一个线程
    t.start()  # 开启线程
    l.append(t)  # 将线程加入列表l
for i in l:
    i.join()  # 这里加上join保证所有的线程结束后才运行下面的代码
print(num)
# 输出结果为0
二、同步锁
  1、什么是同步锁?
    同一时刻的一个进程下的一个线程只能使用一个cpu,要确保这个线程下的程序在一段时间内被cpu执,那么就要用到同步锁。
  2、为什么用同步锁?
    因为有可能当一个线程在使用cpu时,该线程下的程序可能会遇到io操作,那么cpu就会切到别的线程上去,这样就有可能会影响到该程  序结果的完整性。
  3、怎么使用同步锁?
    只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。
  4、实例:

import time
import threading

R = threading.Lock()
def sub():
    global num
    R.acquire() # 加锁,保证同一时刻只有一个线程可以修改数据
    num -= 1
    R.release() # 修改完成就可以解锁
    time.sleep(1)
num = 100  # 定义一个全局变量
l = []  # 定义一个空列表,用来存放所有的列表
for i in range(100):  # for循环100次
    t = threading.Thread(target=sub)  # 每次循环开启一个线程
    t.start()  # 开启线程
    l.append(t)  # 将线程加入列表l
for i in l:
    i.join()  # 这里加上join保证所有的线程结束后才运行下面的代码
print(num)
# 输出结果为0
  5、扩展知识
    1、GIL的作用:多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。
    2、同步锁的作用:为了保证解释器级别下的自己编写的程序唯一使用共享资源产生了同步锁。
三、递归锁和死锁
  1、什么是死锁?
    指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源而相互等待的一个现象
import time
import threading

A = threading.Lock()
B = threading.Lock()
import threading

class obj(threading.Thread):
    def __init__(self):
        super().__init__()

    def run(self):
        self.a()   # 如果两个锁同时被多个线程运行,就会出现死锁现象
        self.b()
    def a(self):
        A.acquire()
        print('123')
        B.acquire()
        print(456)
        time.sleep(1)
        B.release()
        print('qweqwe')
        A.release()
    def b(self):
        B.acquire()
        print('asdfaaa')
        A.acquire()
        print('(⊙o⊙)哦(⊙v⊙)嗯')
        A.release()
        B.release()
for i in range(2):  # 循环两次,运行四个线程,第一个线程成功处理完数据,第二个和第三个就会出现死锁
    t = obj()
    t.start()
程序会出现阻塞现象
 2、什么是递归锁?
    在Python中为了支持同一个线程中多次请求同一资源,Python提供了可重入锁。这个RLock内部维护着一个Lock和一个counter
  变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获
  得资源。
import time
import threading
A = threading.RLock()  # 这里设置锁为递归锁
import threading

class obj(threading.Thread):
    def __init__(self):
        super().__init__()

    def run(self):
        self.a()
        self.b()
  
    def a(self): # 递归锁,就是将多个锁的钥匙放到一起,要拿就全拿,要么一个都拿不到
                # 以实现锁
        A.acquire()
        print('123')
        print(456)
        time.sleep(1)
        print('qweqwe')
        A.release()
    def b(self):
        A.acquire()
        print('asdfaaa')
        print('(⊙o⊙)哦(⊙v⊙)嗯')
        A.release()
for i in range(2):
    t = obj()
    t.start()
四、信号量(semaphore)
  1、什么是信号量?
    同进程的一样,semaphore管理一个内置的计数器,每当调用acquire()时内置函数-1,每当调用release()时内置函数+1。
   计数器不能为0,当计数器为0时acquire()将阻塞线程,直到其他线程调用release()。
import threading
import time
mysf = threading.Semaphore(5)  # 创建信号量对象,(5表示这个锁同时支持的个数)

def func():
    if mysf.acquire():  # 因为使用了信号量,下面的输出就会5个5个的同时输出
        print(threading.currentThread().getName() + 'get semaphore')  
        time.sleep(1)
        mysf.release()
for i in range(20):
    t = threading.Thread(target=func)
    t.start()

29、明天(4.10,上午10:00面试)云计算,服务管理方向

# 百度搜索资料
# 面试官问的面试题:
记录了一些题目:
1、equals与==的区别
2、List与Set的区别
3、接口与抽象类的区别
4、ajax的实现原理
5、MVC分别对应SSH框架中的什么
6、http状态码中404与500分别是什么
7、Object有什么方法
8、linux下有哪些命令
9、SSH框架是怎么实现的
10、jdbc的运行步骤
11、云计算
12、cookie和session的区别
13、http状态码
14、中文乱码问题
15、Web容器发布应用、在哪里修改端口号
16、SpringMVC原理
17、static静态块

30、关于ajax:

# 什么是ajax
ajax全称“Asynchronous Javascript And XML”,并不是一种新技术,是由javascript、xml、XMLHttpRequest组合在一起、能实现异步提交的功能,是一种创建交互式网页应用的网页开发技术。
同步和异步提交的区别
同步提交:当用户发送请求时,当前页面不可以使用,服务器响应页面到客户端,响应完成,用户才可以使用页面。
异步提交:当用户发送请求时,当前页面还可以继续使用,当异步请求的数据响应给页面,页面把数据显示出来 
# ajax的特点(异步提交)
可以快速创建动态网页;无需重新加载整个网页的情况下,能够更新部分网页;
提高用户的体验性;
# ajax的工作原理
客户端发送请求,请求交给ajax,ajax把请求提交给服务,服务器进行业务处理,服务器响应数据交给ajax对象,xhr对象接收数据,由javascript把数据写到页面上,如下图所示:

# ajax请求的格式
var rowsData = $('#receiptPrintList').datagrid('getSelections');
$.ajax({
    type: "post",
    url: "topupRecordController.do?updateReceiptInfo",
    data: {
        refNo:rowsData[0].refNo
    },
    dataType: "json",
    success: function(result){
    }
});
//作用同上
$.post("topupRecordController.do?updateReceiptInfo", {
        refNo:rowsData[0].refNo
    },
    function (data) {
    }, "json");

31、cookie和session的区别

# 1、存储位置不同
    cookie的数据信息存放在客户端浏览器上。
    session的数据信息存放在服务器上。
# 2、存储容量不同
    单个cookie保存的数据<=4KB,一个站点最多保存20个Cookie。
    对于session来说并没有上限,但出于对服务器端的性能考虑,session内不要存放过多的东西,并且设置session删除机制。

# 3、存储方式不同
    cookie中只能保管ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据。
    session中能够存储任何类型的数据,包括且不限于string,integer,list,map等。
# 4、隐私策略不同
    cookie对客户端是可见的,别有用心的人可以分析存放在本地的cookie并进行cookie欺骗,所以它是不安全的。
    session存储在服务器上,对客户端是透明对,不存在敏感信息泄漏的风险。
# 5、有效期上不同
    开发可以通过设置cookie的属性,达到使cookie长期有效的效果。
    session依赖于名为JSESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session不能达到长期有效的效果。
6、服务器压力不同
    cookie保管在客户端,不占用服务器资源。对于并发用户十分多的网站,cookie是很好的选择。
    session是保管在服务器端的,每个用户都会产生一个session。假如并发访问的用户十分多,会产生十分多的session,耗费大量的内存。
7、浏览器支持不同
假如客户端浏览器不支持cookie:
    cookie是需要客户端浏览器支持的,假如客户端禁用了cookie,或者不支持cookie,则会话跟踪会失效。关于WAP上的应用,常规的cookie就派不上用场了。
    运用session需要使用URL地址重写的方式。一切用到session程序的URL都要进行URL地址重写,否则session会话跟踪还会失效。
假如客户端支持cookie:
    cookie既能够设为本浏览器窗口以及子窗口内有效,也能够设为一切窗口内有效。
    session只能在本窗口以及子窗口内有效。
8、跨域支持上不同
    cookie支持跨域名访问。
    session不支持跨域名访问。

32、PASS、LaaS、DB管理系统

"""
IaaS:Infrastructure-as-a-Service(基础设施即服务)

PaaS:Platform-as-a-Service(平台即服务)

SaaS:Software-as-a-Service(软件即服务)
"""

33、常见的反扒手段

1.检测浏览器headers
2.ip封禁
3.图片验证码
4.滑动模块
5.js轨迹
6.前端反调试

手写装饰器

# 模板
def outter (func):
def inner (*args,**kwargs):
  print('执行被装饰函数之前 可以做的操作')
  res=func(*args,**kwargs)
  print('执行被装饰函数之后 可以做的操作')
  return res
return inner

# 例子
def wrapper(func):
def inner(*args,**kwargs)
	print(args,kwargs)
	res = func(*args,**kwargs)
	return res
return inner
@wrapper
def test(a,b):
print("这是原函数")
test(1,2)

  >>> 1,2
  >>> "这是原函数"

# 带参数的装饰器
def deco_para(parameter):
print('enter deco_para')

def deco_func(func):
  print('enter deco_func')
  def wrapper(*args, **kwargs):
      print('enter wrapper')
      print(parameter)
      print('---wrapper: before func---')
      func(*args, **kwargs)
      print('---wrapper: after func---')

  return wrapper
return deco_func

@deco_para(123)
def foo():
print('---foo---')
if __name__ == '__main__':
print('--start--')
foo()

34、2020.4.10,早上10:00外包博云面试(公司云计算方向)拉勾网的电话,不怎么关注拉勾网的信息**

1、说一下你最近的项目,在项目中从事哪些工作?
2、用过哪些中间件?redis、RabbitMQ、Kafka
3、是否熟悉在linux环境下开发?
4、是否会下一下脚本shell,Python脚本
5、后端用的什么框架?
6、有用过celery吗?怎么用?
7、你还有什么想问我的?

2020.4.11,12:20看完《教授与疯子》,影片类似肖申克的救赎里的主人公,一个曾经误杀而入狱的人,对自我的救赎,内心极度惭愧的心理,在监狱中受尽心灵上的折磨,想尽一起方法来弥补自己曾经犯下的错,参与牛津字典的编撰收录,作出了巨大贡献,教她学会识字,最终爱上了被害人的妻子,“如果爱 那该怎么办 ” ,心灵上的折磨,爱却不敢爱,精神病再次复发,折磨、煎熬这莫大的救赎,最终迎来了好的解决,被释放出狱,逐出境内,这也算是好的结局。

ps:最近正在追剧《不完美的她》,周迅主演的主人公,影片讲述一个从小父母被遗弃的孩子,在看到穆连生这个小女孩时,感觉就是自己的童年,不经想守护着她,当起了小女孩的妈妈。

35、2020.4.11 15:30 正在打游戏,接到电话,没有预约的,毫无预防,具体什么公司也不太了解

最后经了解,需要熟悉在Linux环境下开发,爬虫,爬取数据会进行处理,清洗,归类。

1、简单做一下自我介绍
2、liunx你熟悉吗,从远程终端下载内容,用什么命令,查找文件用什么指令?
	#2.1、使用scp命令
    	scp work@192.168.0.10:/home/work/source.txt /home/work/ #把192.168.0.10机器上的source.txt文件拷贝到本地的/home/work目录下
    	scp -r /home/work/sourcedir work@192.168.0.10:/home/work/ #拷贝文件夹,加-r参数
    #2.2  使用xshell工具
    就是使用rz,sz
    首先,服务器要安装了rz,sz
    yum install lrzsz
    当然你的本地windows主机也通过ssh连接了linux服务器
    运行rz,会将windows的文件传到linux服务器
    运行sz filename,会将文件下载到windows本地
3、说一下面向对象中的继承和重载?
	# 运算符重载 
当我们在使用某个符号时,python解释器都会为这个符号定义一个含义,同时调用对应的处理函数, 当我们需要自定义对象的比较规则时,就可在子类中覆盖 大于 等于 等一系列方法....
案例:
原本自定义对象无法直接使用大于小于来进行比较 ,我们可自定义运算符来实现,让自定义对象也支持比较运算符
class Student(object):
    def __init__(self,name='',score=0,age=0):
        self.name=name
        self.score=score
        self.age = age

    #  +  重载
    '''
    数据1 + 数据2 -- 数据1或传给self,数据2会传给other
    '''
    def __add__(self, other):
        #self是+前面的,other是+后面的
        return self.score + other.score
    # - 运算
    def __sub__(self, other):
        #self - other
        return self.score - other.score

    #注意 大于和小于一般情况下只需要重载一个,另外一个自动支持
    # < 运算
    def __lt__(self, other):
        return self.score < other.score
    #  > 运算
    def __gt__(self, other):
        return self.age > other.age

    def __repr__(self):
        return '>'+(str(self.__dict__)[1:-1])+'<'
stu1 = Student('小红',99,18)
stu2 = Student('小花',95,19)
stu3 = Student('小黄',96,18)
stu4 = Student('小蓝',97,19)
print(stu1 + stu2)
print(stu1 - stu2)
all_student = [stu1,stu2,stu3,stu4]
print(all_student)
print(max(all_student))
上述代码中,other指的是另一个参与比较的对象,
大于和小于只要实现一个即可,符号如果不同,解释器会自动交换两个对象的位置 
4、有使用过那些中间件?
5、之前的工作中数据库有用到那些多一点?
6、爬虫有了解吗,会对爬取的数据进行清洗处理吗?
  • 关于linux提问的命令

    1、查看文件最后20行
    	tail -n 20 filename
        head显示文件前几行,默认前10行
        tail显示文件后几行,默认后10行
    2、vim 块操作 列操作 行操作
    	ESC V 左右键 选中 块操作
        esc shift v 行操作 常用来删除多行
        esc ctrl v列操作 上下选中 d delete
    o 当前下一行
    O 当前上一行
    gg 第一行
    G  最后一行
    
    # 正常模式下:
    1.拷贝当前行,并粘贴
    	拷贝:yy    粘贴:p
    2.拷贝当前行向下5行,并粘贴
    	拷贝5行:5yy
    3.删除当前行
    	删除:dd
    4.删除当前行向下5行
    	删除5行:5dd
    5.光标移至最后一行
    	G
    6.光标移至首行
    	gg
    7.移动到第7行
    	7gg
    8.撤销
    	u
    # vim常用命令:
    1.查找某个关键字
    	/关键字
    2.取消高亮
    	:nohl
    3.显示行号
    	:set nu
    4.取消行号
    	:set nonu
    # 在Linux中如何从远程终端下载文件  
    #scp命令 基于ssh的登录
    上传:scp /home/a.tar.tz root@192.168.0.2:/home/tmp/
    下载:scp root@192.168.0.2:/home/a.tar.tz 
     # Xshell 下用rz sz filename
    #首先安装 sudo apt-get install lrzsz
    下载命令:rz
    上传命令:sz filename
    # 查找文件
    1、find命令
    	find /usr/local/mysql -name *.bin
    这句命令的意思是在目录“/usr/local/mysql”中搜索以.bin结尾的所有文件。
    2、locate命令
    	locate *.log 这句命令的意思是查找后缀为.log的文件
    3、grep(搜索字符串)
    通过文件内容中的关键字查找其所属的文件,比较常见的有两种:
    1. 命令原型:grep -rl "keyword" filepath
    比如现在我们要查找在/home/userlan/目录中哪些文件中包含foobar这个关键字
    $ grep -rl "foobar" /home/userlan/
    2.选项
       -i     忽略大小写
       -v    排除指定的字符串
    4、批量替换文件内容
     sed "s/原字符串/新字符串/" 文件
     vim下:s/name/title/  #替换当前行第一个 name 为 title
    5、查看进程
    	ps	
        -e:显示系统内所有进程的信息。与 -A 选项功能相同
        -f:使用完整 (full) 的格式显示进程信息
 小记
  
2020.4.14 周末过去了,起了个早,昨晚吃晚饭时看了电视剧(我是余欢水)第一集,不禁觉得主角悲催的人生,故事节奏紧凑有故事,深入人心,似乎很映射现在的自己,也许是想太多了,近期状态不好,心情极度要崩溃,不敢投简历面试,总感觉没做好准备,总感觉知识面太广,每次问的问题都千差万别,每次都很努力的总结学习知识点,尽管之前很努力的在学;看着别人的各种高offer,心中在做鬼跟自己较劲,闷生自问“为什么别人能行,自己不行”,自己懒散不够努力,没别人花的时间多,收起坏心情振奋起来,还是继续加油吧,科比“如果连你都不相信自己,那么还会有谁相信你”!3.15到的上海,今天4.14转眼已经一个月了!加油吧,少年,未来可期,总是自我暗示,自我安慰,需要正能量,负能量再见!peace~~~~
  • 类的property方法,如何修改类属性

class A:
    def __init__(self,x)
    	self.x = x
@property
def t(self):
    return self.x
@t.setter
def t(self,value)
	pass
def change_x(self,x):
    self.x = x
    
a = A(1)
a.t
>> 1
>>
a.change_x(2)
a.t
>> 2
a.t = 2
a.t
>> 2    
    
  • 谈一下uWSGI和Nginx的理解(原理)

    要注意 WSGI / uwsgi / uWSGI 这三个概念的区分。
    WSGI是一种通信协议。
    # uWSGI
    	一个基于自有的uwsgi协议、wsgi协议和http服务协议的web网关。
    uWSGI是实现了uwsgi和WSGI两种协议的Web服务器。
    # nginx
    	是一个开源的高性能的HTTP服务器和反向代理:
    1.作为web服务器,它处理静态文件和索引文件效果非常高;
    2.它的设计非常注重效率,最大支持5万个并发连接,但只占用很少的内存空间;
    3.稳定性高,配置简洁;
    4.强大的反向代理和负载均衡功能,平衡集群中各个服务器的负载压力应用。
    • Nginx

Ningx是一个反向代理服务器
什么是反向代理?
#正向代理,例如翻墙用的代理服务器就是正向代理,浏览器主动请求代理服务器,代理服务器转发请求到对应的目标服务器.

#反向代理,部署在Web服务器上,代理所有外部网络对内部网络的访问。浏览器访问服务器,必须经过这个代理,是被动的。
正向代理的主动方是客户端,反向代理的主动方是Web服务器。
# 常见的部署
1、uWSGI+Django比单独使用Django的好处:
	支持的并发量更高
	方便管理多进程,发挥多核的优势
	提升性能,因为uwsgi协议比WSGI协议有优势
2、Nginx+uWSGI+Django比uWSGI+Django好处(参考反向代理的作用):
  • sql注入及解决方案?

#sql注入解决方案
1.参数验证
2.特殊字符过滤(sql语句的注释# < >)
3.使用参数化语句,不要拼接sql
4.编码输出
5.平台过滤
总之就是要做好过滤与编码并使用参数化语句,这样sql注入漏洞基本能够解决呢
  • django实现读写分离
    #思路: **django在进行数据库操作的时候,读取数据与写数据(增、删、改)可以分别从不同的数据库进行操作。**
    手动书写数据库在迁移migrate的时候指定数据库
    1、#在settings的配置
    DATABASES = {
        'default': { # 写(主机)
            'ENGINE': 'django.db.backends.mysql', # 数据库引擎
            'HOST': 'xx.xxx.xxx.xxx', # 数据库主机
            'PORT': 3307, # 数据库端口
            'USER': 'root', # 数据库用户名
            'PASSWORD': '123456', # 数据库用户密码
            'NAME': 'test' # 数据库名字
        },
        'slave': { # 读(从机)
            'ENGINE': 'django.db.backends.mysql',
            'HOST': 'xx.xxx.xxx.xxx',
            'PORT': 3308,
            'USER': 'root',
            'PASSWORD': '123456',
            'NAME': 'test'
        }
    }
    2、创建数据库读写路由,在common/db_route.py中实现读写路由
    1.空间上的复用class MasterSlaveDBRouter(object):
        """数据库读写路由"""
    
        def db_for_read(self, model, **hints):
            """读"""
            return "slave"
    
        def db_for_write(self, model, **hints):
            """写"""
            return "default"
    
        def allow_relation(self, obj1, obj2, **hints):
            """是否运行关联操作"""
            return True
        3、配置数据库读写分离
        DATABASE_ROUTERS = ['common.db_router.MasterSlaveDBRouter']
        完成配置。正常写入数据库文件的时候则默认使用主机,读取数据时使用从机。
    打开数据库的日志可以看到读写时,使用的是哪一台服务器。
    
  • Django中间件

    1. 中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件负责做一些特定的功能。

    2. 说的直白一点,中间件可以帮助我们在视图函数执行之前和执行之后做一些额外的操作。 它的本质是一个自定义类,类中定义了几个方法,Django框架会在请求的特定时间去执行这些方法。

    3. 中间件的作用

      • 如果你想修改请求,例如被传送到view中的HttpRequest对象。 或者你想修改view返回的HttpResponse对象,这些都可以通过中间件来实现。

        可能你还想在view执行之前做一些操作,这种情况就可以用 middleware来实现。

    # 中间件的五种方法:
    process_request(self, request)
    process_view(self, request, view_func, view_args, view_kwargs)
    process_template_response(self, request, response)
    process_exception(self, request, exception)
    process_response(self, request, response)
    
    #中间件 //settings.py
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',# 认证相关
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'web.middleware.auth.AuthMiddleware'
    ]

    celery架构原理和使用方法

    ## Celery架构
    Celery的架构由三部分组成,消息中间件(message broker)、任务执行单元(worker)和 任务执行结果存储(backend  -  task result store)组成。
    #### 消息中间件
    Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
    #### 任务执行单元
    Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
    #### 任务结果存储
    Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
    
    # celery简介
    Celery是基于Python开发的分布式异步消息任务队列,它非常易于使用。通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用Celery。它#主要适用于两大类场景
    1、异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等。
    2、定时任务:定时执行某件事情,比如每天数据统计,执行脚本。
    # 简单的实例运用
    from celery import Celery
        app = Celery('hello', broker='amqp://guest@localhost//')
        @app.task
        def hello():
              return 'hello world'
            

    celery基本使用

    # 1)创建app + 任务
    # 2)启动celery(app)服务:
    # 非windows
    # 命令:celery worker -A celery_task -l info
    # windows:
    # pip3 install eventlet
    # celery worker -A celery_task -l info -P eventlet
    
    # 3)添加任务:手动添加,要自定义添加任务的脚本,右键执行脚本
    # 4)获取结果:手动获取,要自定义获取任务的脚本,右键执行脚本
    
    from celery import Celery
    
    broker = 'redis://127.0.0.1:6379/1'
    backend = 'redis://127.0.0.1:6379/2'
    app = Celery(broker=broker, backend=backend, include=['celery_task.tasks'])
    
    # 任务
    tasks.py
    from .celery import app
    import time
    @app.task
    def add(n, m):
        print(n)
        print(m)
        time.sleep(10)
        print('n+m的结果:%s' % (n + m))
        return n + m
    
    @app.task
    def low(n, m):
        print(n)
        print(m)
        print('n-m的结果:%s' % (n - m))
        return n - m

    add_task.py

    from celery_task import tasks
    
    # 添加立即执行任务
    t1 = tasks.add.delay(10, 20)
    t2 = tasks.low.delay(100, 50)
    print(t1.id)
    
    
    # 添加延迟任务
    from datetime import datetime, timedelta
    def eta_second(second):
        ctime = datetime.now()
        utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())
        time_delay = timedelta(seconds=second)
        return utc_ctime + time_delay
    
    tasks.low.apply_async(args=(200, 50), eta=eta_second(10))

    redis和mysql的数据如何进行同步

    mysql的事务

     

    2020.4.16 上海济熠智能科技有限公司 3~5年-本科 9k~13k 图形处理 地图

    职位描述

    1、工业级管理系统后台程序开发
    2、熟练掌握app端接口程序
    2、有gis地图工业级web端后台经验者优先
    3、机场或道路工程项目开发经验者优先
    1、自我介绍,讲一下项目,所负责的功能
    2、讲一下冒泡排序算法的原理
    3、阐述一下MVC架构MTV架构
    4、问前端有了解吗,会吗?
    5、除了python还会其他语言吗?

随机验证码

  # 生成随机验证码
  """
  大写字母 小写字母 数字
  5位数的随机验证码
  chr
  random.choice
  封装成一个函数,用户想生成几位就生成几位
  """
  import random
  def get_code(n):
      code = ''
      for i in range(n):
          # 先生成随机的大写字母 小写字母 数字
          upper_str = chr(random.randint(65,90))
          lower_str = chr(random.randint(97,122))
          random_int = str(random.randint(0,9))
          # 从上面三个中随机选择一个作为随机验证码的某一位
          code += random.choice([upper_str,lower_str,random_int])
      return code
  res = get_code(4)
  print(res)

线程池的使用场景有哪些

线程池适合单系统的大量的异步任务处理,比如发送短信、保存日志。

多道技术

  1.空间上的复用
  ​	多个程序共用一套计算机硬件
  2.时间上的复用
  ​	切换+保存状态
  	1.当一个程序遇到IO操作 操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率 并且也不影响程序的执行效率)
  	 2.当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)
  并发:看起来像同时运行的就可以
  并行:真正意义上的同时执行    单核的计算机不能实现并行,但是可以实现并发
  # 同步异步:表示的是任务的提交方式
同步:任务提交之后 原地等待的任务的执行并拿到返回结果才走 期间不做任何事(程序层面的表现就是卡住了)
异步:任务提交之后 不再原地等待 而是继续执行下一行代码(结果是要的 但是是用过其他方式获取)

OSI七层协议 应用层 表示层 会话层 传输层 网络层 数据链路层 物理连接层

TCP类似于打电话 UDP类似于发短信

  # 如何解决TCP的粘包问题
  	基于TCP实现大文件的上传
  	客户端:
  		1.先生成一个字典
  		2.制作字典的报头
  		3.发送字典的报头
  		4.发送字典数据
  		5.发送真实数据
  	服务端
  		1.接收固定长度的报头
  		2.解析获取字典的长度
  		3.接收字典数据
  		4.解析获取真实数据信息
  		5.接收真实数据

进程:资源单位(车间) 线程:执行单位(车间里面的流水线) ps: 1.每一个进程都默认自带一个"主线程" 线程在运行的时候所需要和产生的数据都是来源于当前线程所在的进程 2.一个进程下可以开设多个线程

  	进程与线程异同
  		1.开进程  开销较大
  			申请内存空间
  			"拷贝"代码
		2.开线程  开销较小
  # 进程间数据是隔离但是同一个进程下的多个线程数据是共享的

进程池与线程池

开进程开线程都需要消耗资源,只不过两者比较的情况线程消耗的资源比较少

在计算机能够承受范围之内最大限度的利用计算机。

什么是池? 在保证计算机硬件安全的情况下最大限度的利用计算机 池其实是降低了程序的运行效率 但是保证了计算机硬件的安全 (硬件的发展跟不上软件的速度)

  from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
  import time
  import os
  # pool = ThreadPoolExecutor(5)  # 括号内可以传参数指定线程池内的线程个数
  # # 也可以不传  不传默认是当前所在计算机的cpu个数乘5
  pool = ProcessPoolExecutor()  # 默认是当前计算机cpu的个数
  """
  池子中创建的进程/线程创建一次就不会再创建了
  至始至终用的都是最初的那几个
  这样的话节省了反复开辟进程/线程的资源
  """
  def task(n):
      print(n,os.getpid())  # 查看当前进程号
      time.sleep(2)
      return n**2
  def call_back(n):
      print('拿到了异步提交任务的返回结果:',n.result())
  """
  提交任务的方式
      同步:提交任务之后 原地等待任务的返回结果 期间不做任何事
      异步:提交任务之后 不等待任务的返回结果(异步的结果怎么拿???) 直接执行下一行代码
  """
  # pool.submit(task,1)  # 朝线程池中提交任务   异步提交
  # print('主')
  """
  异步回调机制:当异步提交的任务有返回结果之后,会自动触发回调函数的执行
  """
  if __name__ == '__main__':
  
      t_list = []
      for i in range(20):
          res = pool.submit(task,i).add_done_callback(call_back) 
          #提交任务的时候 绑定一个回调函数 一旦该任务有结果 立刻执行对于的回调函数
          # print(res.result())  # 原地等待任务的返回结果
          t_list.append(res)
  
      # pool.shutdown()  # 关闭池子 等待池子中所有的任务执行完毕之后 才会往下运行代码
    # for p in t_list:
      #     print('>>>:',p.result())
  
  # 	进程:资源单位
  	线程:执行单位
  	协程:单线程下实现并发
  	并发
  		切换+保存状态
  		ps:看起来像同时执行的 就可以称之为并发
  	协程:完全是程序员自己意淫出来的名词
  		单线程下实现并发
  	并发的条件?
  		多道技术
			空间上的复用
  			时间上的复用
				切换+保存状态

 

2020.4.17 特赞(上海)信息科技有限公司 Python开发工程师 3~5年 20K~30k 算法 AI 图形 视觉16:00视频面试 一对一

1、type(元类)?
2、docker 具体那些那些命令?
3、redis的数据类型:集合,有序集合,hash,list,str  怎么使用?
4、django高并发的时候如何处理?
5、原理,容器、镜像的原理 dockersfile?
6、cerely 数据存储在哪里的?
7、perery 封装?
8、_ __ 区别是什么?
9、索引优化具体方法?
10、__new__   __call__  __init__  作用和执行的顺序?
11、怎么分库分表?
12、缓存穿透 雪崩?
13、django与flask框架的区别?
14、cerely如何搭配消息中间件(MQ)使用 、redis?
15、协程

数据结构的原理

MySQL的数据类型 ?存储过程?

MySQL存储引擎

MySQL索引优化 聚集索引 非聚集索引

原生的MySQL语句和ORM的查询

1、面向对象编程有三大特性:封装、继承、多态,其中最重要的一个特性就是封装。封装指的就是把数据和功能都整合在一起。

隐藏属性(_ __的区别)

Python的Class机制采用双下划线开头的方式将属性隐藏起来(设置成私有的),但其实这仅仅只是一种变形操作,类中所有双下滑线开头的属性都会在类定义阶段、检测语法时自动变成“_类名__属性名”的形式:

  class Foo:
      __N=0 # 变形为_Foo__N
      
      def __init__(self): # 定义函数时,会检测函数语法,所以__开头的属性也会变形
          self.__x=10 # 变形为self._Foo__x
          
      def __f1(self): # 变形为_Foo__f1
          print('__f1 run')
      
      def f2(self):  # 定义函数时,会检测函数语法,所以__开头的属性也会变形
          self.__f1() #变形为self._Foo__f1()
  
  print(Foo.__N) # 报错AttributeError:类Foo没有属性__N
  
  obj = Foo()
  print(obbj.__x) # 报错AttributeError:对象obj没有属性__x

2、python中_ 和__ xx的区别https://www.jianshu.com/p/23ce9baa5a88

总结:"_"下划线是设为私有的方法和属性 
	"__"用来避免子类覆盖其内容
    
    1、"_"单下划线
  Python中不存在真正的私有方法。为了实现类似于c++中私有方法,可以在类的方法或属性前加一个“_”单下划线,意味着该方法或属性不应该去调用,它并不属于API。
  python源码:在使用propery时,经常出现的问题
  classBaseForm(StrAndUnicode):
       def_get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
         if self._errors is None:
  
                self.full_clean()
         return self._errors
  
  errors= property(_get_errors)
  这里的errors是一个属性,属于API的一部分,但是_get_errors是私有的,是不应该访问的,但可以通过errors来访问该错误结果。
  
  2、“__”双下划线
  	这个双下划线更会造成更多混乱,但它并不是用来标识一个方法或属性是私有的,真正作用是用来避免子类覆盖其内容。
   classA(object):
  
     def__method(self):
  
        print"I'm a method in A"
  
     def method(self):
  
        self.__method() 
  
  a = A() 
  
  a.method()
  
  >>> I'm a method in A
  在我们创建一个以"__"两个下划线开始的方法时,这意味着这个方法不能被重写,它只允许在该类的内部中使用
  如果你试图调用a.__method,它还是无法运行的,就如上面所说,只可以在类的内部调用__method。
  
  3、“__xx__”前后各双下划线
  	当你看到"__this__"的时,就知道不要调用它。为什么?因为它的意思是它是用于Python调用的,如下:
   >>> name ="igor"
  
  >>> name.__len__() 4 
  
  >>> len(name) 4 
  
  >>> number = 10 
  
  >>> number.__add__(20) 30 
  
  >>> number + 20 30
   “__xx__”经常是操作符或本地函数调用的magic methods。在上面的例子中,提供了一种重写类的操作符的功能。 
  
      在特殊的情况下,它只是python调用的hook。例如,__init__()函数是当对象被创建初始化时调用的;__new__()是用来创建实例。
  
  classCrazyNumber(object):
  
       def__init__(self, n):
  
           self.n=n
  
  def__add__(self, other):returnself.n -otherdef__sub__(self, other):returnself.n +otherdef__str__(self):returnstr(self.n)
  
  num= CrazyNumber(10)printnum#10printnum + 5#5printnum - 20#30
  

1.什么是封装

封装指的是隐藏对象的属性和实现细节,仅对外公开接口,控制程序中属性的访问权限;

python中的权限分为两种

1.公开 外界可以直接访问和修改

2.私有 外界不能直接访问和修改,在当前类中可以直接修改和访问

2.为什么需要封装

一.封装属性

对于属性而言,封装就为了限制属性的访问和修改,其目的是为了保护数据安全

例如:

学生对象拥有,姓名,性别,年龄,和身份证号,分数;其中身份证是一个相对隐私的数据,不应该让外界访问到;

分数属性,是一个非常关键的数据,决定学员能不能正常毕业,不应被随意修改;

二.封装方法

一个大的功能很多情况下是由很多个小功能组合而成的,而这些内部的小功能对于用户而言是没有意义的,所以封装方法的目的是为了隔离复杂度;

例如:

电脑的开机功能,内部需要启动BIOS,读取系统配置,启动硬盘,载入操作系统,等等一系列复杂的操作,但是用户不需要关心这些实现逻辑,只要按下开机键等待开机即可;

3.如何封装

在属性名前添加两个下划线__,将其设置为私有的

  class Student:
      def __init__(self,name,gender,age,id,score): # 初始化函数
          self.name = name
          self.gender = gender
          self.age = age
          self.__id = id  # 将id设置为私有的
          self.__score = score # 将score设置为私有的
      def test(self):
          print(self.__id)
          print(self.__score)
  
          
  stu = Student("Jack","man",20,"320684198901010001",780)
  #1.访问私有属性测试
  #print(stu.id) #	直接访问到隐私数据
  #print(stu.__id) # 换种写法
  #以上两行代码均输出相似的错误 

4.python封装实现原理

  #其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
  #类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:
  
  class A:
      __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
      def __init__(self):
          self.__X=10 #变形为self._A__X
      def __foo(self): #变形为_A__foo
          print('from A')
      def bar(self):
          self.__foo() #只有在类内部才可以通过__foo的形式访问到.
  
  #A._A__N是可以访问到的,
  #这种,在外部是无法通过__x这个名字访问到。
  a = A()
  print(a.__dict__)
  #输出 {'_A__X': 10}
  
  #定义运行阶段的赋值操作
  a.__Y = 1
  print(a.__dict__)
  #输出 {'_A__X': 10, '__Y': 1}    __y并没有发生变形
  
  """
  变形原理总结:
  1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问。
  2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形
  """

5.封装之property

property是什么

property是一个装饰器,将一个方法伪装成普通属性,其特殊之处在于,该方法会在修改属性值时自动执行

与之对应的是setter与deleter装饰器:

setter装饰的方法会在修改属性值时自动执行

deleter装饰的方法会在删除属性值自动执行

为什么需要property

当我们将一个属性设置为私有之后,就无法直接访问它们了,需要为其创建两个方法,一个用于访问,一个用于修改 ,但是对于使用者而言,私有的和普通都是属性,然而一个可以用点来访问,用等号来修改,另一个却要调用函数来存取,这就违反了统一访问原则

使用property

  class Foo:
      def __init__(self,val):
          self.__NAME=val #将所有的数据属性都隐藏起来
      @property
      def name(self):
          return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
  
      @name.setter
      def name(self,value):
          if not isinstance(value,str):  #在设定值之前进行类型检查
              raise TypeError('%s must be str' %value)
          self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
  
      @name.deleter
      def name(self):
          raise TypeError('Can not delete')
  
  f=Foo('Jack') 
  
  print(f.name) # 访问property属性
  #输出 Jack
  f.name="Rose" # 修改property属性 抛出异常'TypeError: 10 must be str'
  
  del f.name # 删除property属性 抛出异常'TypeError: Can not delete' 
  总结:property的作用是避免使用普通属性和私有属性时的方式发生变化

面向对象的内置方法

首先明确:类中存在一些名字带有__(双下滑线)开头的内置函数,这些函数会在某些时候被自动调用,例如之前学习的迭代器,__next__函数。

1.isinstance(obj,cls)

检查是否obj是否是类 cls 的对象

   class Foo(object):
       pass
   obj = Foo()
   isinstance(obj, Foo)

操作对象属性时自动触发

  __setattr__

使用点语法添加/修改属性会触发它的执行

  __delattr__

使用点语法删除属性的时候会触发

  __getattr__

使用点语法调用属性且属性不存在的时候才会触发

  __getattribute__

使用点语法调用属性的时候触发,无论属性是否存在都会执行

call

__call__是一个函数,在对象被调用时执行,调用就是加括号()

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()的区别

  class Foo:
      def __init__(self):
          pass
      def __call__(self, *args, **kwargs):
          print('__call__')
  obj = Foo() # 执行 __init__
  obj()       # 执行 __call__

令人迷惑的new函数与__init___函数

# __new__ 类级别的方法
1.是在类准备将自身实例化时调用,并且至少需要传递一个参数cls,此参数在实例化时由python解释器自动提供;
2.始终是类的静态方法,即使没有被加上静态方法装饰器;
3.必须要有返回值,返回实例化出来的实例;在自己实现__new__()时需要注意:可以return父类(通过super(当前类名,cls)).__new__出来的实例,
或者直接是object的__new__出来的实例.
# __init__  
1.有一个参数self,该self参数就是__new__()返回的实例;
2.__init__()在__new()的基础上完成初始化动作,不需要返回值;
3.若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用
4.创建的每个实例都有自己的属性,方便类中的实例方法调用;
  class M(type):
      def __new__(cls, *args, **kwargs):
          print("new")
  		#return type.__new__(cls,*args,**kwargs)
      def __init__(self,clsname,bases,namespace):
          print("init")
  class A(metaclass=M):
      n = 1
  print(A.__name__)
  print(A.__bases__)
  print(A.__dict__)

  """
  #输出
  new
  Traceback (most recent call last):
    File "/Users/jerry/PycharmProjects/元类属性查找.py", line 43, in <module>
      print(A.__name__)
  AttributeError: 'NoneType' object has no attribute '__name__'"""
  执行了__new__函数但是并没有执行__init__,因为__new__函数是真正用于创建类的方法,只有创建类成功了才会执行init函数,new必须要有返回值且返回值类型为__type__时才会执行__init__函数,
  
  # 总结:元类中__new__是用于创建类对象的  __init__是用于初始化类的其他信息的

python中的反射

python中通过以下四个函数来实现反射

  hasattr(object,name) # 判断对象是否拥有某个属性
  setattr(object,name,value) #为对象增加新的属性
  getattr(object,name,default) #从对象中获取某个属性
  delattr(object,name) #从对象中删除某个属性
  
  ### 3.为什么需要反射
  	一个类在定义的时候,可能一些属性的设计并不是很完美,而后期需要作出修改,或是增加新属性时,使用反射的方式可以不需要修改源代码。

元类

  class Teacher(object):
      school='tsinghua'
      def __init__(self,name,age):
          self.name=name
          self.age=age
  
      def say(self):
          print('%s says welcome to the Beijing' %self.name)
          
  t1=Teacher('egon',18)
  print(type(t1)) #查看对象t1的类是<class '__main__.Teacher'>

用于实例化产生类的类称之为元类 就是此时的type类;Teacher是通过type实例化得到的。

项目中的应用

在优酷系统中需要根据类的信息来生成创建表的语句; 必需知道类何时被创建了,使用元类可以轻松的拦截类的创建过程,获取类相关信息来生成建表语句。

  class MyMetaClass(type):
      def __init__(self,name,bases,dic):
          table_name = name
          columns = self.transfer_columns(dic)
          sql = "create table if not exists %s(%s)" % (table_name,columns)
          # 自动建表
          try:
              OBDB().conn.execute(sql)
          except Exception as e:
              pass
          super().__init__(name,bases,dic)
        
  s = 10  == s = int
  a = type("int")
  b = a
  b()
  c.__class__

元类实现的单例

什么是单例

单例是指的是单个实例,指一个类只能有一个实例对象

为什么要用单例

当一个类的实例中的数据不会变化时使用单例,数据是不变的

  • 使用元类实现单例模式

  #在类定义时 自动执行init 在init中创建实例 call中直接返回已有实例
  class MyMeta(type):
      __instance = None
  
      def __init__(self,name,bases,dic):
          if not self.__instance:
              self.__instance = object.__new__(self)
              self.__init__(self.__instance)
  
          super().__init__(name, bases, dic)
  
      def __call__(cls):
          return cls.__instance
  
  class Player(metaclass=MyMeta):
      def __init__(self):
          print("创建播放器了")
  Player()
  Player()
  # 仅执行一次创建播放器

python内置与地第三方库

1、内置模块

1 .sys # 系统
2 .os # 路径
3 .json
4 .pickle #序列化
5 .shutil
6 .time # 时间
7 .datetime
8.logging #日志
9.md5加密 
10 .getpass
11 .csv
12 .timeit
13 .random  # 随机

python函数传参是值传递还是引用传递

分情况讨论

不可变参数用值传递:

像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变 不可变对象

变参数是引用传递的:

比如像列表,字典这样的对象是通过引用传递、和 C 语言里面的用指针传递数组很相似,可变对象 能在函数内部改变。

作为函数参数,可变对象传递的是引用,不可变对象传递的是值

如果将不可变对象传递给一个方法,实际上传递过去的只是该不可变对象的值,你无法在方法中更改原始对象。
c = 1
print(id(c),id(1))  #1763241072 1763241072
def fun(c):
    c=c+1     #创建了局部变量并指向对象2
    print(c)  #2
    print(id(c),id(2))  #1763241104 1763241104

    c=c*2     #局部变量指向对象4
    print(c)  #4
    print(id(c),id(4))  #1763241168 1763241168

    c=c-2     #局部变量指向对象2
    print(c)  #2
    print(id(c),id(2))  #1763241104 1763241104
fun(c)
print(c) #  1   fun中进行的操作都未改变原始对象

如果将可变对象传递给一个方法,那么该方法会获得对同一个对象的引用,你可以通过这个引用改变原始对象的值,但是如果你在方法中将引用绑定到了其它对象上,那么之后所有的操作都不会再改变原始对象
c = [0]
print(id(c))  #88878234568
def fun(c):
    c.append(1)     #改变原始对象
    print(c)        #[0,1]
    print(id(c))    #88878234568

    c.append(2)     #改变原始对象
    print(c)        #[0, 1, 2]
    print(id(c))    #88878234568

    c = [6,6,6]    #创建局部变量并指向了新对象[6,6,6],后续的操作均与原始对象无关
    print(c)       #[6, 6, 6]
    print(id(c))   #88878261192
fun(c)
print(c) #  [0, 1, 2]   输出原始对象

2020.4.22 20:20 突然袭击 阿里外派 感觉主要是测试岗位

1、linux 文件重命名 修改文件权限

将main1.c重命名为main.c

rename main1.c main.c main1.c

修改文件权限

chmod 权限数字 文件名

2、多线程的局限性

# 缺点
上下文切换
线程安全
数据混乱
死锁
1、对程序员来说,比单线程应用更具挑战性,程序设计实现更加复杂;
2、多线程比单线程更加消耗资源,比如CPU、内存等;
3、计算机资源有限,太多的线程运行,会导致上下文频繁切换,甚至出现死锁等情况,出现非预期效果。
3、list 如何拼接
两个list合并:a=[1,2,3] 、 b=['a','b','c']
合并1:a+b如下
a=[1,2,3]
b=['a','b','c']
a+b
[1,2,3,'a','b','c']

2、a.extend(b)如下
a=[1,2,3]
b=['a','b','c']
a.extend(b)
print(a)
[1,2,3,'a','b','c']

3、a.append(b)如下
a=[1,2,3]
b=['a','b','c']
a.append(b)
print(a)
[1,2,3,['a','b','c']]

4、切片 a[0:0]=b如下
a=[1,2,3]
b=['a','b','c']
a.append(b)
print(a)
[1,2,3,['a','b','c']]

4、_ __ 用过那些___方法

5、字典取值

person = {'name':'xiaoming', 'age':18}

第一种 若键不存在则会抛出KeyError异常

person['city']

第二种 不会抛出异常,不存在则返回None,也可以设置默认返回值

person.get('city',"上海")

第三种 与第二种类似,区别在于setdefault方法会更新字典

person.setdefault('city', '上海')

6、django请求生命周期

7、面向对象中 with语句 open 实现上下文管理

在python中,上下文可以理解为是一个代码区间,一个范围 ,例如with open  打开的文件仅在这个上下文中有效 .
涉及到的两个方法:
## enter 
​	表示进入上下文,(进入某个场景 了)
## exit
  表示退出上下文,(退出某个场景 了)
当执行with 语句时,会先执行enter ,当代码执行完毕后执行exit,或者代码遇到了异常会立即执行exit,并传入错误信息
包含错误的类型.错误的信息.错误的追踪信息

enter 函数应该返回对象自己 
exit函数 可以有返回值,是一个bool类型,用于表示异常是否被处理,仅在上下文中出现异常有用
如果为True 则意味着,异常以及被处理了 
False,异常未被处理,程序将中断报错

8、装饰器方法,实现某些方法

9、django用过那些组件 from组件 auth组件

10、GIL 全局解释器锁?

11、测试 怎么测

 

2020.4.23 14:00 现场面试 平安好医 浦东新区

1、自我介绍
2、聊项目、负责的功能、某个功能具体怎么实现
3、上下文管理 with open
4、sql语句 查询统计商品数  conut group by
5、linux熟悉吗 项目这么部署的
6、在生产环境下 如何run器项目
7、项目需求分析 周期 人员分配情况
8、实际开发上线部署

2020.4.23 16:30 现场面试 达观数据 浦东张江高科软件园Y1

1、常用的有哪些数据类型
	int float str bool list tuple dict set 
2、==  is 的区别
is比较的是id值相同不相同,而==仅比较值
3、哪些是可变、不可变类型 有什么区别
	可变:值变id不变   不可变:值变ID变
	可变:list set dict 不可变:str float tuple
4、用过哪些装饰器?双下滑线的方法?
	__new__  __call__ __init__ __setter__ __getter__ .....
5、连表反转
6、常见的字符编码:
	ASCII GBK unicode UTF-8
6、列表list的拼接方法
	最简单的使用“+”;
    最意想不到的使用切片赋值的方法;
    使用列表自带的extend方法
7、字符串顺序反转位置不变:如:str:"how are you"
    str = "how are you"
	print(str[::-1])
    
    >>> woh era uoy
    # 双向指针
    class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        for i in range(len(s)>>1):
            s[i],s[len(s)-1-i]=s[len(s)-1-i],s[i]
    
8、一个大字典,通过字典取值value比较,拿出最大的值,输出key
dict = {"a":8,"b":2,"c":121,"d":81,}
res1=dict.items()
for i in res:
    print(i)
res2 = max(dict.items(),key=lambda x:x[1])
print(res2[0])
  1. 4.29 15:30外派英特尔芯片 Python开发 突然袭击 电话面试

# 基础问的挺多的 django也问了几个问题
1、GIL
2、进程、线程、协程
3、Python自带常用的装饰器,有什么区别
4、用过哪些魔法方法
5、装饰器本质 为什么要写装饰器
6、深浅拷贝
7、用过哪些魔法方法 __new__ __call__ __int__ 什么时候调用 
8、列表去重?
	1. for 循环去重
list1 = [2, 1, 3, 6, 2, 1]
temp = []
for i in list1:
    if not i in temp:
        temp.append(i)
  2、# set 去重
    list.set(list1)
9、django 如何生成表模型?
10、如何查询?
	# models.book.object.filter(id=5).first()  跟面试官较劲了 他说哪来的models object
    
 defer与only
res = models.Author.objects.all().values('name')  # 这样虽然能拿到对象的name属性值,但是已经是queryset里面套字典的格式,我现在想拿到queryset里面放的还是一个个的对象,并且这这些对象“只有”name属性,其他属性也能拿,但是会重新查询数据库,类似于for循环N次走N次数据库查询
res = models.Author.objects.all().only('name')  # 一旦指导了only就不要取对象没有的属性值了
defer与only刚好相反,取不包括括号内的其他属性值
11、csrf跨域问题,如何解决?
    前后端不分离的CSRF
    在页面HTML提交的form表单前加{ % csrf_token %}
    最简单粗暴的方法就是将settings.py配置文件中的CSRF的中间件注掉
    前后端分离的CSRF
    注销Django中的中间件(django.middleware.csrf.CsrfViewMiddleware)
    不注销中间件,为特定的视图加装饰器@csrf_exempt,豁免csrf跨域
    继承自APIView的类视图可以自动豁免csrf
12、models如何修改表名
# 在Model中
class Meta:
    db_table = "new_tablename"  # 更改表名
    
13、列表和集合的区别?
14、*args和**kwargs的区别?
    *args 和 **kwargs 都代表 1个或多个参数的意思。*args 传入tuple 类型的无名参数,而 **kwargs 传入的参数是 dict 类型。
15、什么是深浅拷贝,区别?
16、F与Q查询区别?

装饰器模板

def outter (func):
    def inner (*args,**kwargs):
        print('执行被装饰函数之前 可以做的操作')
        res=func(*args,**kwargs)
        print('执行被装饰函数之后 可以做的操作')
        return res
    return inner

 

什么是数据库连接池呢?

数据库连接池概念:数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个,这项技术能明显提高对数据库操作的性能。

TCP黏包问题解决

# 服务端
import socket
import subprocess
import struct
import json


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)


while True:
    conn, addr = server.accept()
    while True:
        try:
            cmd = conn.recv(1024)
            if len(cmd) == 0:break
            cmd = cmd.decode('utf-8')
            obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            res = obj.stdout.read() + obj.stderr.read()
            d = {'name':'jason','file_size':len(res),'info':'asdhjkshasdad'}
            json_d = json.dumps(d)
            # 1.先制作一个字典的报头
            header = struct.pack('i',len(json_d))
            # 2.发送字典报头
            conn.send(header)
            # 3.发送字典
            conn.send(json_d.encode('utf-8'))
            # 4.再发真实数据
            conn.send(res)
            # conn.send(obj.stdout.read())
            # conn.send(obj.stderr.read())
        except ConnectionResetError:
            break
    conn.close()
    
    # 客户端
    
import socket
import struct
import json
client = socket.socket()
client.connect(('127.0.0.1',8080))

while True:
    msg = input('>>>:').encode('utf-8')
    if len(msg) == 0:continue
    client.send(msg)
    # 1.先接受字典报头
    header_dict = client.recv(4)
    # 2.解析报头 获取字典的长度
    dict_size = struct.unpack('i',header_dict)[0]  # 解包的时候一定要加上索引0
    # 3.接收字典数据
    dict_bytes = client.recv(dict_size)
    dict_json = json.loads(dict_bytes.decode('utf-8'))
    # 4.从字典中获取信息
    print(dict_json)
    recv_size = 0
    real_data = b''
    while recv_size < dict_json.get('file_size'):  # real_size = 102400
        data = client.recv(1024)
        real_data += data
        recv_size += len(data)
    print(real_data.decode('gbk'))
"""
1.如何将对方发送的数据收干净
"""

Python函数调用的时候参数的传递方式是值传递还是引用传递?

Python的参数传递有:位置参数、默认参数、可变参数、关键字参数。
函数的传值到底是值传递还是引用传递,要分情况:
# 不可变参数用值传递:

像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变
# 不可变对象

可变参数是引用传递的:
比如像列表,字典这样的对象是通过引用传递、和C语言里面的用指针传递数组很相似,可变对象能在函数内部改变。

语法糖(多层装饰器)问题

"""
语法糖就是@加上调用的函数名,执行过程是:
语法糖会把紧挨着它的下面的函数名当做参数来调用函数,
之后创建一个变量名来指向调用的这个函数
函数是从上往下依次执行的
语法糖是从下往上依次执行的
最后会用最开始的调用方式的函数名创建一个变量名指向最终调用的函数
"""

# 装饰器在装饰的时候  顺序从下往上
# 装饰器在执行的时候  顺序从上往下

# 例子
def deco1(func):
    print(1)
    def wrapper1():
        print(2)
        func()
        print(3)
    print(4)
    return wrapper1

def deco2(func):
    print(5)
    def wrapper2():
        print(6)
        func()
        print(7)
    print(8)
    return wrapper2

@deco1
@deco2
def foo():
    print('foo')


if __name__ == '__main__':
    foo()
————————————————
>>>>#结果
5
8
1
4
2
6
foo
7
3

# 分析
1、修饰器本质上就是一个函数,只不过它的传入参数同样是一个函数。因此,依次加了deco1和deco2两个装饰器的原函数foo实际上相当于deco1(deco2(foo))。
2、明白了第1步后,下面进入这个复合函数。首先执行的是内层函数deco2(foo)。因此第一个打印值是5。接下来要注意,在deco2这个函数内定义了一个wrapper2函数,但是并没有类似于wrapper2()的语句,因此该函数内的语句并没有立即执行,而是作为了返回值。因此wrapper2内的3条语句作为输入参数传递到了deco1内。wrapper2函数内还有一行print(8),因此第二个打印值为8。
3、下一步是执行deco1()函数内容。与2类似,先打印出1和4,返回值为wrapper1。由于更外层没有装饰器,因此接下来就将执行wrapper1内的内容。第五个打印值为2。接着执行func()函数,注意此处func()表示的是wrapper2中的内容,因此跳到wrapper2中执行。第六个打印值为6。类似的,wrapper2中的func()为foo(),因此接着会输出foo。最后,回到wrapper2和wrapper1的最后一行,依次输出7和3。到这里,整个装饰器的运行过程结束。

什么时csrf攻击原理?如何解决?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

# 简单来说就是:
	你访问了信任网站A,然后A 会用保存你的个人信息并返回给你的浏览器一个
cookie,然后呢,在cookie 的过期时间之内,你去访问了恶意网站B,它给你返回一些恶意请求代码,
要求你去访问网站A,而你的浏览器在收到这个恶意请求之后,在你不知情的情况下,会带上保存在本
地浏览器的cookie 信息去访问网站A,然后网站A 误以为是用户本身的操作,导致来自恶意网站C 的
攻击代码会被执:发邮件,发消息,修改你的密码,购物,转账,偷窥你的个人信息,导致私人信息泄
漏和账户财产安全收到威胁。

image-20200506134501302

常见的解决方法:

Cookies Hashing:每一个表单请求中都加入随机的Cookie,由于网站中存在XSS漏洞而被偷窃的危险。 HTTP refer:可以对服务器获得的请求来路进行欺骗以使得他们看起来合法,这种方法不能够有效防止攻击。 验证码:用户提交的每一个表单中使用一个随机验证码,让用户在文本框中填写图片上的随机字符串,并且在提交表单后对其进行检测。 令牌Token:一次性令牌在完成他们的工作后将被销毁,比较安全。

Django 中哪里用到了线程?哪里用到了协程?哪里用到了进程?

1.Django 中耗时的任务用一个进程或者线程来执行,比如发邮件,使用celery。 2.部署django 项目的时候,配置文件中设置了进程和协程的相关配置。

Celery 分布式任务队列?

情景:用户发起request,并等待response 返回。在本些views 中,可能需要执行一段耗时的程 序,那么用户就会等待很长时间,造成不好的用户体验,比如发送邮件、手机验证码等。 使用celery 后,情况就不一样了。解决:将耗时的程序放到celery 中执行。 将多个耗时的任务添加到队列queue 中,也就是用redis 实现broker 中间人,然后用多个worker 去监听队列 里的任务去执行。

image-20200506135024657

任务task:就是一个Python 函数。  队列queue:将需要执行的任务加入到队列中。  工人worker:在一个新进程中,负责执行队列中的任务。  代理人broker:负责调度,在布置环境中使用redis。

ngnix 的正向代理与反向代理?

web 开发中,部署方式大致类似。简单来说,使用Nginx 主要是为了实现分流、转发、负载均衡, 以及分担服务器的压力。Nginx 部署简单,内存消耗少,成本低。Nginx 既可以做正向代理,也可以 做反向代理。

正向代理:请求经过代理服务器从局域网发出,然后到达互联网上的服务器。 特点:服务端并不知道真正的客户端是谁。 反向代理:请求从互联网发出,先进入代理服务器,再转发给局域网内的服务器。 特点:客户端并不知道真正的服务端是谁。 区别:正向代理的对象是客户端。反向代理的对象是服务端。

 

2020.5.8 徐家汇 财联社 7~12k 13薪 python开发工程师

1、登记面试表格-自我介绍-问项目-细聊技术点-问两个python基础

2、进程间的数据是隔离的,如何共享呢?
  • 多进程之间数据是隔离的

  • 在windows操作系统上,启动多进程必须是放在__main__

import os
from multiprocessing import Process

def fun():
    global n #声明一个全局变量
    n = 0    #给该变量进行赋值
    print('子进程pid: %s' %os.getpid(), n)

if __name__ == '__main__':
    n = 100
    p = Process(target=fun)
    p.start()
    p.join()
    print('主进程pid:',os.getpid(), n)
    
>>>>
子进程pid: 1380 0
主进程pid: 15712 100
Process finished with exit code 0

3、进程间的数据通信与数据共享

进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的。

"""
队列:先进先出
堆栈:先进后出
"""
import multiprocessing
from multiprocessing import Queue
def run(q):
    q.put('hello')
    q.put('mainprocess')
    print('subprocess q ID:',id(q))
 
if __name__ == '__main__':
    q=multiprocessing.Queue()#用于进程间通信的队列
    t=multiprocessing.Process(target=run,args=(q,))
    t.start()
    t.join()
 
    print('main process q ID:',id(q))
    print(q.get())
    print(q.get())
'''
subprocess q ID: 2359100652232
main process q ID: 2025858869736
hello
mainprocess
'''

4、线程间的通信

(1)共享变量通信

事实上共享变量通信是会造成线程安全的,除非我们对这个共享变量是有足够了解的,如非必要就不要使用共享变量在线程间进行通信

from threading import Thread

money = 666
def task():
    global money
    money = 999

t = Thread(target=task)
t.start()
t.join()
print(money)

(2)Queue通信

使用消息队列的过程和上面一样,只不过queue进行了很好的封装,在放值和取值的时候时线程安全的。

queue模块实现了多生产者,多消费者的队列。当 要求信息必须在多线程间安全交换,这个模块在线程编程时非常有用 。里面主要实现了3中队列。

操作基本一样,只不过queue.Queue()保证了线程安全。

 

生产者与消费者模型

利用多线程和队列可以实现生产者消费者模式。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。

1.什么是生产者和消费者?

  • 在线程世界里,生产者就是生产数据(或者说发布任务)的线程,消费者就是消费数据(或者说处理任务)的线程。在任务执行过程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者提供更多的任务,本质上,这是一种供需不平衡的表现。为了解决这个问题,我们创造了生产者和消费者模式。

2.生产者消费者模式的工作机制

  • 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不直接找生产者要数据,而是从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,解耦了生产者和消费者

下面是一个利用threading和queue模块,模拟一个简单的厨师做包子,顾客吃包子的例子,是生产者消费者模式的典型例子

import time
import queue
import threading
q = queue.Queue(10)  # 生成一个队列,用来保存“包子”,最大数量为10
# 生产者
def productor(i):
    # 厨师不停地每2s做一个包子
    while True:
        q.put("厨师%s做的包子!" % i)
        time.sleep(2)
def consumer(i):
    # 顾客不停地每1s吃一个包子
    while True:
        print("顾客%s吃了一个%s" % (j, q.get()))
        time.sleep(1)
# 实例化3个生产者(厨师)
for i in range(3):
    t = threading.Thread(target=productor, args=(i,))
    t.start()

# 实例化10个消费者
for j in range(10):
    v = threading.Thread(target=consumer, args=(j,))
    v.start()

 

原文地址:https://www.cnblogs.com/Gaimo/p/13418143.html