python -- 基础面试题汇总


朝闻道,夕死可矣。 -- 《论语》


简述解释型和编译型编程语言

  1. 编译型语言:把做好的源程序全部编译成二进制代码的可运行程序,然后,可直接运行这个程序
  2. 解释型语言:把做好的源程序翻译一句,然后运行一句,直至结束
  3. java很特殊,java程序也需要编译,但是没有直接编译称为机器语言,而是编译称为字节码,然后用解释方式执行字节码

细节:https://www.i3geek.com/archives/583


进制转换

  • 基本1: A进制(a) –> 十进制(d)
  • d=int(a,A)

    说明:A的取值为2,8,16

  • 基本2: 十进制(d)–> A进制(a)

  • a=bin(d)
  • a=oct(d)
  • a=hex(d)

python 递归的最大层数

试验了以下,发现 2 和 3 的版本都默认为 1000 次 可以通过下面的函数进行获取或者设置

import sys

sys.getrecursionlimit()

sys.setrecursionlimit(1000)

一些布尔运算

and 和 or 运算时从左往右的

>>> 1 or 3
1
>>> 1 and 3
3
>>> 0 and 2 and 1
0
>>> 0 and 2 or 1
1
>>> 0 or False and 1

ascii、unicode、utf-8、gbk 区别

  1. ascii: 一个字节的后7位进行表示,最后一位统一为 0, 共 127位
  2. unicode: 世界通用码,但是与ascii区分有问题
  3. utf-8: unicode 的一种实现方式
  4. gbk 汉字专用,两个字节表示一个汉字

细节: http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html


三元运算符以及使用环境

在一般条件下可以简化 if/else 语句

result2 = 'test_true' if 1 == 2 else 'test_false'

列举 python2 和 python3 的区别

  1. print不再是语句,而是函数,比如原来是 print 'abc' 现在是 print('abc')

  2. Python2 的默认编码是 asscii, Python 3 默认采用了 UTF-8

# py2
>>> sys.getdefaultencoding()
'ascii'

#py3
>>> sys.getdefaultencoding()
'utf-8'
  1. True 和 False 在 Python2 中是两个全局变量(名字),在数值上分别对应 1 和 0, 可以随意修改。True 和 False 在 Python2 中是两个全局变量(名字),在数值上分别对应 1 和 0

列举布尔值为False的常见值

None, 0, 0.0, 0j, "", (), [], {}, 自定义示例的 bool 方法 返回的Flase

细节: https://www.kancloud.cn/digest/python-master/143559


is和==的区别

Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)

is比较的是两个对象的id值是否相等,也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址

==比较的是两个对象的内容是否相等,默认会调用对象的__eq__()方法

>>> a = [1, 2, 3]
>>> b = a
>>> b is a 
True
>>> b == a
True
>>> b = a[:]
>>> b is a
False
>>> b == a
True

细节:https://juejin.im/entry/5a3b62446fb9a0451f311b5c


简述Python的深浅拷贝

深拷贝的时候python将字典的所有数据在内存中新建了一份,所以如果你修改新的模版的时候老模版不会变。相反,在浅copy 的时候,python仅仅将最外层的内容在内存中新建了一份出来,字典第二层的列表并没有在内存中新建,所以你修改了新模版,默认模版也被修改了

浅拷贝指仅仅拷贝数据集合的第一层数据,深拷贝指拷贝数据集合的所有层。所以对于只有一层的数据集合来说深浅拷贝的意义是一样的,比如字符串,数字,还有仅仅一层的字典、列表、元祖等

多层情况

>>> n1 = {"name":1, "age": {2:2, 3:3}}
>>> n1
{'name': 1, 'age': {2: 2, 3: 3}}
>>> n2 = copy.copy(n1)
>>> n3 = copy.deepcopy(n1)
>>> n3
{'name': 1, 'age': {2: 2, 3: 3}}
>>> n2
{'name': 1, 'age': {2: 2, 3: 3}}
>>> n1['age'][2] = 4
>>> n1
{'name': 1, 'age': {2: 4, 3: 3}}
>>> n2
{'name': 1, 'age': {2: 4, 3: 3}}
>>> n3
{'name': 1, 'age': {2: 2, 3: 3}}

细节: https://www.cnblogs.com/zhuifeng-mayi/p/9179647.html


垃圾回收机制

python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略

细节:https://www.jianshu.com/p/1e375fb40506


一行代码写出 9*9 乘法表

print("\n".join("\t".join(["{0}*{1}={2}".format(x,y,x*y) for y in range(1, x+1)]) for x in range(1, 10)))

正则表达式使用

细节: https://songlee24.github.io/2014/09/01/python-library-02/


一行代码实现删除列表中重复的值

>>> a = [1,1,1,12,2,2,3,4]
>>> list(set(a))
[1, 2, 3, 4, 12]

>>> b = list({}.fromkeys(a).keys())
>>> b
[1, 12, 2, 3, 4]

全局变量

定义在函数外的变量默认为全局变量,但是在除主函数之外的函数中对其进行修改时就会变成局部变量,需要加上 global 声明次变量才能进行修改


python 闭包



os和sys模块的作用

os就是一个普通的python库,用来向Python程序提供运行环境,特别是在文件系统、创建新进程、获取操作系统本身的一些信息(比如uname),并屏蔽各种不同操作系统之间的细节差异

这个模块提供了一种方便的使用操作系统函数的方法

sys模块则是python程序用来请求解释器行为的接口。比如关于调试类的(trace, frames,except)等,profiling类(stats, getsizeof),运行时环境类(python path, stderr, stdout),解释器本身(如version)。inspect某种程度上可以看成是在sys提供的功能上的一个包装

这个模块可供访问由解释器使用或维护的变量和与解释器进行交互的函数


删除文件

import os

file_path = ""
os.remove(file_path)

Python面向对象中的继承有什么特点

1、在继承中基类的构造(init()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。有别于C#

2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数

3、Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)

class Parent(object): # define parent class 
  parentAttr = 100 
  def __init__(self): 
    print "Calling parent constructor" 

  def parentMethod(self): 
    print 'Calling parent method' 

  def setAttr(self, attr): 
    Parent.parentAttr = attr 

  def getAttr(self): 
    print "Parent attribute :", Parent.parentAttr

class Child1(Parent): # define child1 class 
  def __init__(self): 
    print "Calling child1 constructor" 

  def childMethod(self): 
    print 'Calling child1 method' 
    Parent.parentMethod(self) #调用基类的方法,所以要加上参数self

class Child2(Parent): # define child2 class 
  def childMethod(self): 
    print 'Calling child2 method' 
    self.parentMethod()  #子类调用自己从父类那继承过来的父类的方法

c1 = Child1() # 实例化子类 1
c2 = Child2() # 实例化子类 2
c1.childMethod() # 调用子类的方法 
c2.childMethod() # 调用子类的方法 
c1.parentMethod() # 调用父类方法 
c1.setAttr(200) # 再次调用父类的方法 
c1.getAttr() # 再次调用父类的方法

metaclass 作用

细节: http://blog.jobbole.com/21351/


多种方法创建单例模式


简述 OSI 七层协议

从下到上

  1. 物理层:原始比特流
  2. 数据链路层:物理寻址,同时将原始比特流转变为逻辑传输线路
  3. 网络层:控制子网的运行,如逻辑编制,分组传输,路由选择
  4. 传输层:接收上一层的数据,在必要的时候把数据进行分割,并将这些数据交给网络层,并保证这些数据段有效到达对端
  5. 会话层:不同机器上的用户之间建立及管理会话
  6. 表示层:信息的语法语义以及他们的关联
  7. 应用层:各种应用层协议,HTTP,FTP等

细节: https://blog.csdn.net/yaopeng_2005/article/details/7064869


简述 三次握手、四次挥手的流程

(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认

(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态

(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了

(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态

(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态

(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态

(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手

细节: https://blog.csdn.net/younglao/article/details/79453777


arp 协议

ARP (Address Resolution Protocol) 是个地址解析协议。最直白的说法是:在IP以太网中,当一个上层协议要发包时,有了该节点的IP地址,ARP就能提供该节点的MAC地址

细节: https://blog.csdn.net/tigerjibo/article/details/7351992


TCP, UDP 的区别

  1. 基于连接与无连接

  2. TCP要求系统资源较多,UDP较少;

  3. UDP程序结构较简单

  4. 流模式(TCP)与数据报模式(UDP);

  5. TCP保证数据正确性,UDP可能丢包

  6. TCP保证数据顺序,UDP不保证


简述Python进程、线程和协程的区别及应用场景

1.进程拥有自己独立的堆和栈,堆和栈都不共享,进程由操作系统调度 2.线程拥有自己独立的栈,但是堆却是共享的,标准的线程是由操作系统调度的 3.协程共享堆却不共享栈,协程是由程序员在协程的代码块里显示调度

1、进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

2、线程

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

3、协程

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

细节:https://www.cnblogs.com/lxmhhy/p/6041001.html


GIL锁

GIL : Global Interpreter Lock(全局解释器锁) 每个 CPU 同一时刻只能执行一个线程 在一个进程中,只有一个 GIL

在Python多线程下,每个线程的执行方式: 1.获取GIL 2.执行代码直到sleep或者是python虚拟机将其挂起。 3.释放GIL

所以 python 下想要充分利用多核CPU,就用多进程,原因是:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)


Python中如何使用线程池和进程池

import random
def Test(a, b):
    time.sleep(random.randint(5, 20))
    print(str(a) + '_' + str(b) + '\t')

# 线程池
import threadpool
def MultiThreadTest():
    pool = threadpool.ThreadPool(20)
    li = []
    for i in range(1000):
        li.append((None, {'a': i, 'b': i + 10}))
    requests = threadpool.makeRequests(Test, li)
    [pool.putRequest(req) for req in requests]
    pool.wait()

### 进程池
import multiprocessing
def MultiProcessTest():
    pool = multiprocessing.Pool(processes = 4)
    for i in range(1000):
        pool.apply_async(Test, (i, i + 10, ))
    pool.close()
    pool.join()

进程锁和线程锁的作用

线程锁:主要用来给方法、代码块加锁。当某个方法或者代码块使用锁时,那么在同一时刻至多仅有有一个线程在执行该段代码。当有多个线程访问同一对象的加锁方法/代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段。但是,其余线程是可以访问该对象中的非加锁代码块的

进程锁:也是为了控制同一操作系统中多个进程访问一个共享资源,只是因为程序的独立性,各个进程是无法控制其他进程对资源的访问的,但是可以使用本地系统的信号量控制


同步/异步,阻塞/非阻塞

同步和异步关注的是消息通信机制

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用的结果。而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程


路由器和交换机的区别

  1. 路由器可以给你的局域网自动分配IP,虚拟拨号,就像一个交通警察,指挥着你的电脑该往哪走,你自己不用操心那么多了。交换机只是用来分配网络数据的

  2. 路由器在网络层,路由器根据IP地址寻址,路由器可以处理TCP/IP协议,交换机不可以

  3. 交换机在中继层,交换机根据MAC地址寻址。路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。交换机可以把很多主机连起来,这些主机对外各有各的IP

  4. 路由器提供防火墙的服务,交换机不能提供该功能。集线器、交换机都是做端口扩展的,就是扩大局域网(通常都是以太网)的接入点,也就是能让局域网可以连进来更多的电脑。路由器是用来做网间连接,也就是用来连接不同的网络

交换机是利用物理地址或者说MAC地址来确定转发数据的目的地址。而路由器则是利用不同网络的ID号(即IP地址)来确定数据转发的地址。IP地址是在软件中实现的,描述的是设备所在的网络,有时这些第三层的地址也称为协议地址或者网络地址。MAC地址通常是硬件自带的,由网卡生产商来分配的,而且已经固化到了网卡中去,一般来说是不可更改的。而IP地址则通常由网络管理员或系统自动分配

细节:http://www.cnblogs.com/Lynn-Zhang/articles/5754336.html


magic string

所谓魔数和魔字符串就是指在代码中出现但没有解释的数字常量或字符串

细节:https://baike.baidu.com/item/%E9%AD%94%E6%95%B0


wsgi

WSGI的全称是Web Server Gateway Interface,翻译过来就是Web服务器网关接口。具体的来说,WSGI是一个规范,定义了Web服务器如何与Python应用程序进行交互,使得使用Python写的Web应用程序可以和Web服务器对接起来

细节:https://segmentfault.com/a/1190000003069785


django 请求的生命周期

  1. 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端 请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中

  2. url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配, 一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了

  3. 视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端

  4. 客户端浏览器接收到返回的数据,经过渲染后显示给用户

细节:https://www.cnblogs.com/renpingsheng/p/7534897.html


django的request对象是在什么时候创建的


如何给CBV的程序添加装饰器

@method_decorator(wrapper, name="get")
class IndexView(View):
    def get(self, request):
        user = request.session.get("user01", "游客")
        return render(request, "index.html", {"user": user})

Django 打印原生sql

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}