欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python常见面试问题(一)

程序员文章站 2024-03-23 22:15:04
...

python面试常见问题(一)

一. 装饰器

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,
装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、
缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能
本身无关的雷同代码并继续重用.

装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:

  1. 不能修改被装饰的函数的源代码
  2. 不能修改被装饰的函数的调用方式
    满足1、2的情况下给程序增添功能

二. 关系型数据库和非关系型数据库:

非关系型数据库优势:

  1. 可扩展性:同样是因为是基于键值对的,数据之间无关系,这样就非常容易扩展
  2. 大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性
  3. 数据库的结构简单

非关系型数据库劣势:

  1. 不提供sql支持,学习和使用成本较高;
  2. 无事务处理;

关系型数据库的优势:

  1. 复杂查询:可以用SQL语句方便地在一个表以及多个表之间做非常复杂的数据查询
  2. 事务支持:使得对于安全性能很高的数据访问要求得以实现
  3. 容易理解:关系模型指的就是二维表格模型
  4. 使用方便:通用的SQL语言使得操作关系型数据库非常方便;

关系型数据库的劣势

  1. 为了维护一致性所付出的巨大代价就是其读写性能比较差;
  2. 固定的表结构;
  3. 高并发读写需求;
  4. 海量数据的高效率读写;

三. cherrypick & git reverse

git cherry-pick可以理解为”挑拣”提交,它会获取某一个分支的单笔提交,并作为一个新的提交引入到你当前分支上。
、当我们需要在本地合入其他分支的提交时,如果我们不想对整个分支进行合并,而是只想将某一次提交合入到本地当前分支上,
那么就要使用git cherry-pick了。

git cherry-pick [<options>] <commit-ish>...

常用options:
    --quit                退出当前的chery-pick序列
    --continue            继续当前的chery-pick序列
    --abort               取消当前的chery-pick序列,恢复当前分支
    -n, --no-commit       不自动提交
    -e, --edit            编辑提交信息


git cherry-pick 2555c6e

当cherry-pick时,没有成功自动提交,这说明存在冲突,因此首先需要解决冲突,
解决冲突后需要git commit手动进行提交:

$ git commit 
[branch1 790f431] [Description]:branch2 commit 2
 Date: Fri Jul 13 18:36:44 2018 +0800
 1 file changed, 1 insertion(+)
 create mode 100644 only-for-branch2.txt

或者git add .后直接使用git cherry-pick --continue继续。

git reset --hard HEAD~
git cherry-pick -n 23d9422
git cherry-pick -e 23d9422
git cherry-pick --quit
git cherry-pick --abort
git cherry-pick master

# 如果在git cherry-pick后加一个分支名,则表示将该分支顶端提交进cherry-pick
$ git cherry-pick master

python常见面试问题(一)

1,commit  之前的撤销


未添加至暂存区的撤销(add 之前)
git status
git checkout .


已添加至暂存区的撤销(add 之后,有或者没有commit操作都可以执行)
git reflog
git reset --hard  bca6882(relog 中展示的commitId)



2,push之后的撤销

(
git add .

git commit -m "commit"

git  push  origin brandname
)
等一系列操作后的撤销


不想将本次的操作生效


git revert  commitId(提交的id)


git push origin brandname

此次操作就会撤销掉上一次的push



在这个分支上,会生成一条revert 的提交记录


revert 是负负得正,什么意思呢,就是说第一次revert的时候,
上一次push的命令就失效了,再一次revert 后,会回退到之前的之前的状态,


详细的介绍一下

比如我操作一个文件UserController.java
 
 将userName 改为username 经过一系列的git操作
 git add .
 git commit -m "commit"
 git push orgin 
 后,再进行git revert commitId
 再进行git push origin branchname
 userName 还是userName,


如果我再revert 的时候,
git revert
git push origin branchname
将userName 还是改为username.

总结为:

git revert 为奇数时生效,为偶数时,失效

3,git 回滚:
将git 回滚到某一次commit操作,在该commitId之前的操作,将全部失效(此方法不建议使用,有风险),

git reset --hard commitId


git push  origin branchname --force   (强制覆盖)

因为是不可逆的,不建议使用


git reflog
git reset --hard bca6882(relog 中展示的commitId)
git push origin branchname --force

git revert commitId
git push origin branchname

二者区别:
revert 是放弃指定提交的修改,但是会生成一次新的提交,需要填写提交注释,以前的历史记录都在;
reset 是指将HEAD指针指到指定提交,历史记录中不会出现放弃的提交记录。

Git中tag的用法及作用
 

首先说一下作用:Git 中的tag指向一次commit的id,通常用来给开发分支做一个标记,如标记一个版本号。

下面就说一下具体的用法:
1.添加标签: git tag -a version -m "note" commitId
注解:git tag 是打标签的命令,-a 是添加标签,其后要跟新标签号,-m 及后面的字符串是对该标签的注释。
2.提交标签到远程仓库
:git push origin -tags注解:就像git push origin master 把本地修改提交到远程仓库一样,-tags可以把本地的打的标签全部提交到远程仓库。
3.删除标签:git tag -d

version注解:-d 表示删除,后面跟要删除的tag名字
4.删除远程标签:git push origin :refs/tags/version注解:就像git push origin :branch_1 可以删除远程仓库的分支branch_1一样, 冒号前为空表示删除远程仓库的tag。
5.查看标签:git tag或者git
 tag -l

git tag -a version -m "note" commitId
git show version
git push or --tags
git tag -d version
git push or :refs/tags/version

$ git checkout mywork
$ git rebase origin

git add -u
git rebase --continue
git rebase --abort

四. es机制

Lucene实现快速搜索的核心就是倒排索引,那什么是倒排索引(单词到文档id的关联关系)一个字段有一个自己的倒排索引。18,20这些叫做term,而[1,3]就是posting list。Posting list就是一个int的数组,
存储了所有符合某个term的文档id和term Index
假设我们有很多个term,比如:
Carla,Sara,Elin,Ada,Patty,Kate,Selena
如果按照这样的顺序排列,找出某个特定的term一定很慢,因为term没有排序,需要全部过滤一遍才能找出特定的term。排序之后就变成了:
Ada,Carla,Elin,Kate,Patty,Sara,Selena
这样我们可以用二分查找的方式,比全遍历更快地找出目标的term。这个就是 term dictionary。有了term dictionary之后,可以用 logN 次磁盘查找得到目标。但是磁盘的随机读操作仍然是非常昂贵的(一次random access大概需要10ms的时间)。
所以尽量少的读磁盘,有必要把一些数据缓存到内存里。但是整个term dictionary本身又太大了,无法完整地放到内存里。于是就有了term index。term index有点像一本字典的大的章节表。比如:
A开头的term ……………. Xxx页
C开头的term ……………. Xxx页
E开头的term ……………. Xxx页
如果所有的term都是英文字符的话,可能这个term index就真的是26个英文字符表构成的了。但是实际的情况是,term未必都是英文字符,term可以是任意的byte数组。而且26个英文字符也未必是每一个字符都有均等的term,比如x字符开头的term可能一个都没有,而s开头的term又特别多。实际的term index是一棵树。

lucene/Elasticsearch就是尽量将磁盘里的东西搬进内存(term index是放在内存里面的),
减少磁盘随机读取次数(同时也利用磁盘顺序读特性),结合各种压缩算法(term index用FST进行压缩, term dictionary压缩),高效使用内存,从而达到快速搜索的特性。

五. flask和django的区别

大大提升开发效率
让应用开发更加规范、拓展性更强
让程序员把更多的精力放在业务逻辑的实现上,而不是重复、而复杂的基础环境上(比如web服务器、底层实现等)

flask的优点:

  1. 小巧、灵活,让程序员自己决定定制哪些功能,非常适用于小型网站。
  2. 对于普通的工人来说将毛坯房装修为城市综合体还是很麻烦的,使用Flask来开发大型网站也一样,开发的难度较大,代码架构需要自己设计,开发成本取决于开发者的能力和经验。

Django:

  1. 大而全,功能极其强大,是Python web框架的先驱,用户多,第三方库极其丰富。
  2. 非常适合企业级网站的开发,但是对于小型的微服务来说,总有“杀鸡焉有宰牛刀”的感觉,体量较大,非常臃肿,定制化程度没有Flask高,也没有Flask那么灵活。

Tornado:

天生异步,性能强悍是 Tornado 的名片,然而 Tornado 相比 Django 是较为原始的框架,
诸多内容需要自己去处理。当然,随着项目越来越大,框架能够提供的功能占比越来越小,更多的内容需要团队自己去实现,而大项目往往需要性能的保证,这时候 Tornado 就是比较好的选择。Tornado项目代表:知乎

六. 微服务和分布式

集群是个物理形态,分布式是个工作方式。
分布式:一个业务分拆多个子业务,部署在不同的服务器上
集群:同一个业务,部署在多个服务器上
分布式中的每一个节点,都可以做集群。而集群并不一定就是分布式的。

分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。

好的设计应该是分布式和集群的结合,先分布式再集群,具体实现就是业务拆分成很多子业务,然后针对每个子业务进行集群部署,
这样每个子业务如果出了问题,整个系统完全不会受影响

微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。
在所有情况下,每个任务代表着一个小的业务能力。

集群模式是不同服务器部署同一套服务对外访问,实现服务的负载均衡。区别集群的方式是根据部署多台服务器业务是否相同。
注:集群模式需要做好session共享,确保在不同服务器切换的过程中不会因为没有获取到session而中止退出服务。
一般配置Nginx*的负载容器实现:静态资源缓存、Session共享可以附带实现,Nginx支持5000个并发量。

七. 快排 & 冒泡排序的时间复杂度

快排在最糟糕得情况下时间复杂度是O(n²),平均的复杂度是O(nlogn),
这里的n和logn, 分别代表了调用栈的高度,和完成每层的时间,在a取
数组的第一项时候,是最糟的情况,完成每层需要的时间是n,栈的高
读是n,时间复杂度就是n²,当取中间的值得时候,完成每层的时间是
n,但是调用栈的高度变成了logn,所以这个时候时间复杂度是nlogn

退出服务。
一般配置Nginx*的负载容器实现:静态资源缓存、Session共享可以附带实现,Nginx支持5000个并发量。

七. 快排 & 冒泡排序的时间复杂度

快排在最糟糕得情况下时间复杂度是O(n²),平均的复杂度是O(nlogn),
这里的n和logn, 分别代表了调用栈的高度,和完成每层的时间,在a取
数组的第一项时候,是最糟的情况,完成每层需要的时间是n,栈的高
读是n,时间复杂度就是n²,当取中间的值得时候,完成每层的时间是
n,但是调用栈的高度变成了logn,所以这个时候时间复杂度是nlogn

冒泡排序的时间复杂度是O(n^2)