<面试题>面试题整理(1-100)

1.continue和 break有什么区别?

  continue:跳过当前循环剩余的语句执行下一次循环   

  break:结束整个循环

2.python中的作用域

  作用域指变量的生效范围,由被赋值的地方所决定

  遇到一个变量,搜索路径为:LEGB

    L:Local(局部),首先在局部作用域查找变量的声明和值

    E:Enclosed(嵌套),一般出现在函数中嵌套了一个函数,在外围的函数中的作用域;主要目的是实现闭包;---(只有闭包函数需要考虑)
    G:Global(全局):模块文件顶层声明的变量具有全局作用域,从外部开来,模块的全局变量就是一个模块对象的属性;仅限于单个模块文件中;

    B:Built-in(内置):系统内解释器定义的变量,如预定义在builtin 模块内的变量;解释器在则在,解释器亡则亡;

    

 3.谈谈对闭包的理解

  在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

#闭包函数的实例
# outer是外部函数 a和b都是外函数的临时变量
def outer( a ):
     b = 10
     # inner是内函数
     def inner():
        #在内函数中 用到了外函数的临时变量
         print(a+b)
    # 外函数的返回值是内函数的引用
     return inner

  如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

4.谈谈深浅拷贝和赋值  

拷贝的问题

    引用:无论怎么变都一起变

    浅拷贝:只拷贝父对象,不会拷贝父对象中的子对象

    深拷贝:完全拷贝,重新划分内存空间

 

5.python的垃圾回收机制

1. 引用计数(主要)
  引用计数也是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集技术。当 Python 的某 个对象的引用计数降为 0 时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为 1。如果引用被删除,对象的引用计数为 0,那么该对象就可以被垃圾回收。不过如果出现循环引用的话,引用计数机制就不再起有效的作用了
2. 标记清除(去除,循环引用)
  如果两个对象的引用计数都为 1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的,也就是说,它们的引用计数虽然表现为非 0,但实际上有效的引用计数为 0。所以先将循环引用摘掉,就会得出这两个对象的有效计数。
3. 分代回收(多次没清除的保留,下次不回收这些内容---以空间换时间)

  Python 中使用了某些启发式算法(heuristics)来加速垃圾回收。例如,越晚创建的对象更有可能被回收。对象被创建之后,垃圾回收器会分配它们所属的代(generation)。每个对象都会被分配一个代,而被分配更年轻代的对象是优先被处理的。

 

看到的一种简单的回答:

  Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。

 

6.什么是 lambda 函数?它有什么好处?

lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数

7.请写出一段 Python 代码实现删除一个list 里面的重复元素?

a = [1, 2, 4, 2, 4, 5, 6, 5, 7, 8, 9, 0]
b = list(set(a))
print(b)

8.谈谈python的异常处理try...except...finally

# 标准模板
s1 = 100
try:
	int(s1)
except IndexError as e:
	print(e)
except KeyError as e:
	print(e)
except ValueError as e:
	print(e)
except Exception as e:
	print("万能的异常%s"%e)
else:
	print('try内代码块没有异常则执行我')
finally:
	print('无论异常与否,都会执行该模块,通常是进行清理工作')

9.如何用 Python 来进行查询和替换一个文本字符串?

import re

s = "Hello world!!!"
# old new count
s1 = s.replace('!', '$', 2)
# 正则匹配
p = re.compile('!')
# new old count
s2 = p.sub('$', s, count=2)
print(s1, s2)
# Hello world$$! Hello world$$!

10.Python 里面 match()和 search()的区别?

import re
# 开始位置匹配,匹配不到返回none
s1 = re.match('super', '123super234super')
# 扫描整个string查找匹配,返回第一个匹配值(start,end),匹配不到返回none
s2 = re.search('super', '123super234super').span()
print(s1,s2)

11.python里面如何设置全局变量 

a = 1


def func1():
	global a
	a = 99


print(a)  # 1
func1()
a += 1
print(a)  # 100

12.下面的代码会输出什么? 

def f(x, l=[]):
	for i in range(x):
		l.append(i * i)
	print(l, id(l))


f(2)  # [0, 1] id = 2634833064840
f(3, [3, 2, 1])  # 自己传入l [3,2,1,0,1,4] id = 2634815781704
f(3)  # [0,1,0,1,4] id = 2634833064840

 

13.生成器面试题

def demo():
    for i in range(4):
        yield i

g=demo()

g1=(i for i in g)
g2=(i for i in g1)

print(list(g1))  #[0, 1, 2, 3]
print(list(g2)) # []

# 生成器,类似于篮子装东西,取完值则不可再取。

分析:

  按照生成器表达式和生成器函数不取值不执行的做法

  一直到10行list(g1) 找g1取值,

  10行list()找7行g1取值

  g1找5行g 取值

  g找1行生成器函数取值 ,得到g = (0,1,2,3)

  g1 = (0,1,2,3)

  list(g1) == [0,1,2,3]  由于生成器只能取一次值,此时g1 = ()

  11行执行的时候,g2找g1要值,g1为空,因此list(g2)==[]

 

14.生成器面试题2

def add(n,i):
    return n+i

def test():
    for i in range(4):
        yield i

g = test()

for n in [1,10]:#所有for循环里套生成器表达式的,要把for循环拆开来算,不容易晕
    g=(add(n,i) for i in g)
'''
上两句代码相当于:
n = 1
g = (add(n,i) for i in g) #生成器表达式  返回的是一个生成器,表达式里的代码不执行
n = 10          #此后代码的n值都为10
g = (add(n,i) for i in g)  
———> g = (add(10,i) for i in (add(10,i) for i in g)) 
———> g = (add(10,i) for i in (add(10,i) for i in test())) 
———> g = (add(10,i) for i in (add(10,i)) for i in (0,1,2,3))
———> g = (add(10,i) for i in (add(10,i)) for i in (0,1,2,3))
———> g = (add(10,i) for i in (10,11,12,13))
———> g = (20,21,22,23)
'''
# [20, 21, 22, 23]
print(list(g))#list 找 g要值,然后g找上一个g要值,上一个g找test要值
'''
执行list(g)的时候,前面的生成器表达式才工作,此时n已经为10了,所以,所有的生成器表达式里的值都要用n=10来计算
'''

  

15.for循环套生成器的面试题 

题目:

 1 def add(n,i):
 2     return n+i
 3 
 4 def test():
 5     for i in range(4):
 6         yield i
 7 
 8 g  = test()
 9 for n in [1,10,5]:
10     g = (add(n,i) for i in g)
11 
12 
13 print(list(g))

分析:

  我们知道生成器函数和生成器表达式都很"懒",只要你不找它取值它就不执行.

  函数从上到下开始执行,一直到13行都没开始,13行list()函数找g要值,9-10行的生成器表达式才开始执行,我们将9-10行拆开:

1 n =1 
2 g = (add(n,i) for i in g)
3 n =10 
4 g = (add(n,i) for i in g)
5 n =5 
6 g = (add(n,i) for i in g)

  根据执行顺序,1-4行都是生成器表达式,在13行找此时6行要值的时候,1-4行都还未执行,我们将表达式都写在6行,得到:

1 n =5 
2 g = (add(n,i) for i in (add(n,i) for i in (add(n,i) for i in test())))

  从里面往外面执行,

  test() == (0,1,2,3)

  (add(n,i) for i in test()) == (5,6,7,8)

  (add(n,i) for i in (add(n,i) for i in test())) ==(10,11,12,13)

  (add(n,i) for i in (add(n,i) for i in (add(n,i) for i in test()) )) ==(15,16,17,18)

  g=(15,16,17,18)

  list(g) == [15,16,17,18]

  输出结果:

1 [15, 16, 17, 18]

 

16.二分查找

# 二分查找
'''
1.end问题
2.44对应的end<start 找不到情况
3.返回值递归的情况
4,611,aim太大的情况
'''
l = [2, 3, 5, 10, 15, 16, 18, 22, 26, 30, 32, 35, 41, 42, 43, 55, 56, 66, 67, 69, 72, 76, 82, 83, 88]


def find(l, aim, start=0, end=None):
	end = len(l) if end is None else end
	Mid_index = (end - start) // 2 + start
	if aim <= l[len(l) - 1]:
		if end >= start:
			if l[Mid_index] > aim:
				return find(l, aim, start=start, end=Mid_index - 1)
			elif l[Mid_index] < aim:
				return find(l, aim, start=Mid_index + 1, end=end)
			elif l[Mid_index] == aim:
				return Mid_index
		else:
			return '找不到!'
	else:
		return '比列表最大数都大,找不到!'


l = [2, 3, 5, 10, 15, 16, 18, 22, 26, 30, 32, 35, 41, 42, 43, 55, 56, 66, 67, 69, 72, 76, 82, 83, 88]
ret = find(l, 411)
ret1 = find(l, 44)
ret2 = find(l, 66)
ret3 = find(l, 67)
print(ret, ret1, ret2, ret3)  #比列表最大数都大,找不到! 找不到! 17 18

  

 

17.这两个参数是什么意思:*args,**kwargs?我们为什么要使用它们?

如果我们不确定要往函数中传入多少个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使要用*args;

如果我们不知道要往函数中传入多少个关键词参数,或者想传入字典的值作为关键词参数时,那就要使用**kwargs。

args和kwargs这两个标识符是约定俗成的用法。

18.Python中list和tuples的差别是什么?

  list:可变的,可以增删改,适合长度不固定或需要变动的数据

  tuples:不可变,可哈希,适合一些只读数据,比list更省内存

19.代码举例什么是装饰器函数

  为函数添加功能但不改变调用方式

def fn(fc):
	def f():
		print('<a>' + fc() + '</a>')

	return f


@fn
def f2():
	return '我是一个超链接'


f2()  # <a>我是一个超链接</a>

20.请用自己的算法,按升序合并如下两个List , 并去除重复的元素

List1 = [2, 3, 8, 4, 9, 5, 6]
List2 = [5, 6, 10, 17, 11, 2]
# 合并
List1.extend(List2)
# 去重
List1 = list(set(List1))
# 排序
for i in range(len(List1)-1):
	if List1[i] > List1[i + 1]:
		List1[i], List1[i + 1] = List1[i + 1], List1[i]

print(List1)

21.遍历多层嵌套列表中的每个元素 

A = [1, 2, [3, 4, ['434', [5, ['535'], 6]]]]


def printiter(lst):
	for i in lst:
		if type(i) == list:
			printiter(i)
		else:
			print(i,end=' ')

# 1 2 3 4 434 5 535 6 
printiter(A)

22.用自己的话说明迭代器和生成器,它们之间的关系?

    迭代器对可迭代对象所有数据进行遍历,生成器则会暂停,执行一次运行一次

    生成器就是一种特殊的迭代器,yield

迭代器:类中有next和iter方法,可以使用for循环(例:字符串(string),列表(list),字典(dict),元组(tuple))

生成器:能创建迭代器,只需要在返回的时候将return换成yield就是生成器

区别:生成器能做到迭代器所能做的所有事,能节约内存且更加简洁,生成器终结时,能自动抛出StopIteration异常

 

------------------------------------

Python 中提供的 生成器:

1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行

2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

生成器Generator:

本质:迭代器 ( 所以自带了 __iter__方法和 __next__方法,不需要我们去实现)

特点:惰性运算,开发者自定义

 

 

生成器表达式

1.把列表解析的[]换成()得到的就是生成器表达式

2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。

 

23.字符串格式化:% 和 format的区别

1.不需要理会数据类型的问题,在%方法中%s只能替代字符串类型

2.单个参数可以多次输出,参数顺序可以不相同

3.填充方式十分灵活,对齐方式十分强大

format函数非常灵活,很强大,可以接受的参数不限个数,并且位置可以不按顺序,而且有较为强大的格式限定符(比如:填充,对齐,精度等)

24.举例说明python中的继承类型 

class A:
	pass
class B:
	pass

# 单继承
class C(A):
	pass

# 多继承
class D(A, B):
	pass

25. 谈一下类方法、实例方法和静态方法的区别?

类方法:
  1. 类方法只能访问'类变量'的方法

  2. 类方法需要使用@classmethod装饰器定义

  3. 类方法的第一个参数是类实例,约定写为cls

说明:

     类(实例)和对象(实例)都可以调用类方法

           类方法不能访问实例变量

静态方法:@staticmethod

       静态方法是普通函数,

     静态方法定义在类的内部,只能凭借该类或实例调用

           静态方法需要使用@staticmethod装饰器定义

           静态方法写普通函数定义相同,不需要传入self和cls 参数

说明:

  类和实例都可以调用静态方法

  静态方法不能访问类变量和实例变量

类实例方法:是类实例化对象的方法,只有实例对象可以调用,形参为 self,指代对象本身;

class Game(object):
	top_count = 0

	def __init__(self, player_name):
		self.player_name = player_name
		Game.top_count += 1

	# 静态方法:不需要调用实例属性和类属性--普通函数
	@staticmethod
	def game_help():
		print("游戏帮助")

	# 类方法:需要调用类属性
	@classmethod
	def count_top(cls):
		print("游戏最高分%s" % cls.top_count)

	# 实例方法:调用实例属性
	def start_game(self):
		print("%s开始游戏" % self.player_name)


game = Game("小米")
game2 = Game("小2")
Game.game_help()
Game.count_top()
game.start_game()
# 游戏帮助
# 游戏最高分2
# 小米开始游戏

应用场景:

(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。 1.网站的计数器 2.应用配置 3.多线程池 4.数据库配置,数据库连接池 5.应用程序的日志应用....

26. __init__ 和__new__的区别

__init__ 在对象创建后,对对象进行初始化。
__new__ 是在对象创建之前创建一个对象,并将该对象返回给 init。

__new__: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self)

      其必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。

class Bar(object):
    pass

class Foo(object):
    def __new__(cls, *args, **kwargs):
        return Bar()

print(Foo()) 

27.单例模式 

单例模式

目的:让类创建对象,在系统中只有唯一一个实例

每一次执行类名()返回对象时,内存地址是相同的

 

new方法

class MusicPlayer(object):
	# __new__方法:object提供的静态方法,
	# 1.为内存中的对象分配空间(类名()创建对象时分配的空间就是用这个方法),
	# 2.返回对象的引用
	def __new__(cls, *args, **kwargs):
		# 创建对象,new对象自动调用
		print("01.创建对象,分配空间")
		# 为对象分配空间,用父类的new方法分配空间
		instance =super().__new__(cls)
		# 必须返回,不然得不到内存
		return instance

	def __init__(self):
		print("02.初始化")


player = MusicPlayer()
print(player)

  

 单例模式

# 单例模式,无论调用多少次,得到对象的引用相同(内存地址相同)
# 思路:没创建对象就先分配空间,分配好空间每次都返回这个内存空间地址,不再使用new方法重新分配
class MusicPlayer(object):
	# 记录单例对象的引用,第一个被创建的对象
	instance = None
	# 记录是否执行过初始化动作
	init_flag = False

	def __new__(cls, *args, **kwargs):
		# 1.判断类属性是否为空对象
		if cls.instance is None:
			# 2.调用父类的方法,为第一个对象分配空间
			cls.instance = super().__new__(cls)
		# 3.返回保存的对象引用
		return cls.instance

	# 4.改写初始化方法,使它只执行一次
	def __init__(self):
		# 5.判断是否执行过初始化动作,为Ture直接返回
		if MusicPlayer.init_flag :
			return
		# 6.如果没有执行就进行初始化
		print("初始化")

		# 7.修改类属性标记
		MusicPlayer.init_flag = True

# 创建多个对象,地址相同
player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)

 28.4G 内存怎么读取一个 5G 的数据?

"""方法1:通过生成器多次读取"""


# 生成器
def get_lines():
	with open('Cookie和Session.py', 'r', encoding='utf-8') as f:
		while True:
			data = f.readlines(100)
			if data:
				yield data
			else:
				break


# 迭代器
file = get_lines()
print(next(file))
print(next(file))
print(next(file))

方法2:使用linux命令切割文件,分多次进行读取


"""可以通过linux命令split切割成小文件,然后再对数据进行处理,此方法效率比较高。可以按照行数切割,可以按照文件大小切割"""

python@ubuntu:~/Desktop$ split -l 2 requirements.txt re.txt 
 意思:按照两行切割requirements.txt文件,切割后的文件名叫re.txt。回车后会生成好多re文件,里面都是两行代码

29.OSI七层模型? IP ,TCP/UDP ,HTTP ,RTSP ,FTP 分别在哪层?

IP: 网络层
TCP/UDP: 传输层
HTTP、RTSP、FTP: 应用层协议 

应用层:http ftp协议

传输层:TCP UDP

网络层:IP arp协议(将ip地址解析成mac地址)

数据链路层:mac地址(网卡地址)

物理层:物理设备

30.请简单说一下三次握手和四次挥手?

三次握手过程:
1 首先客户端向服务端发送一个带有 SYN 标志,以及随机生成的序号 100(0 字节)的报文
2 服务端收到报文后返回一个报文(SYN200(0 字节),ACk1001(字节+1))给客户端
3 客户端再次发送带有 ACk 标志 201(字节+)序号的报文给服务端
至此三次握手过程结束,客户端开始向服务端发送数据。

1 客户端向服务端发起请求:我想给你通信,你准备好了么?
2 服务端收到请求后回应客户端:I'ok,你准备好了么
3 客户端礼貌的再次回一下客户端:准备就绪,咱们开始通信吧!
整个过程跟打电话的过程一模一样:1 喂,你在吗 2 在,我说的你听得到不 3 恩,听得到(接下来请
开始你的表演)
补充:SYN:请求询问,ACk:回复,回应。
四次挥手过程:
由于 TCP 连接是可以双向通信的(全双工),因此每个方向都必须单独进行关闭(这句话才是
精辟,后面四个挥手过程都是其具体实现的语言描述)
四次挥手过程,客户端和服务端都可以先开始断开连接
1 客户端发送带有 fin 标识的报文给服务端,请求通信关闭
2 服务端收到信息后,回复 ACK 答应关闭客户端通信(连接)请求
3 服务端发送带有 fin 标识的报文给客户端,也请求关闭通信
4 客户端回应 ack 给服务端,答应关闭服务端的通信(连接)请求

31.简述 TCP 和 UDP 的区别以及优缺点?

UDP 是面向无连接的通讯协议,UDP 数据包括目的端口号和源端口号信息。
优点:UDP 速度快、操作简单、要求系统资源较少,由于通讯不需要连接,可以实现广播发送
缺点:UDP 传送数据前并不与对方建立连接,对接收到的数据也不发送确认信号,发送端不知道数
据是否会正确接收,也不重复发送,不可靠。

TCP 是面向连接的通讯协议,通过三次握手建立连接,通讯完成时四次挥手
优点:TCP 在数据传递时,有确认、窗口、重传、阻塞等控制机制,能保证数据正确性,较为可靠。
缺点:TCP 相对于 UDP 速度慢一点,要求系统资源较多。

区别:

  1、基于连接与无连接
        2、TCP要求系统资源较多,UDP较少;   
  3、流模式(TCP)与数据报模式(UDP);   
  4、TCP保证数据正确性,UDP可能丢包 
        5、TCP保证数据顺序,UDP不保证

32.创建简单的TCP服务器需要的流程

  1.socket创建一个套接字

  2.bind绑定ip和port

  3.listen使用套接字变为被动链接

  4.accept等待用户链接

  5.recv/send接收发送数据

33.创建TCP连接的流程?

server端

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
sk.listen()          #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024)  #接收客户端信息
print(ret)       #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
conn.close()       #关闭客户端套接字
sk.close()        #关闭服务器套接字(可选)

  

client端

import socket
sk = socket.socket()           # 创建客户套接字
sk.connect(('127.0.0.1',8898))    # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024)         # 对话(发送/接收)
print(ret)
sk.close()            # 关闭客户套接字

34.创建UDP连接流程?

server端

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
udp_sk.close()                         # 关闭服务器套接字

  

client端

import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)

35.谈谈计算机系统:硬件,操作系统,应用程序(点击一个应用程序,计算机内部流程)  

硬件:

  CPU(大脑负责运算)

  内存(记忆负责临时存储)

  硬盘(笔记本负责永久存储)

  输入设备(人的耳朵和眼睛,负责接受外部的信息,传给CPU)

以上设备都通过总线连接

       CPU的工作流程,从内存中取出指令→解码→执行,周而复始

       例子:点击应用程序→告诉操作系统一个路径→从磁盘读取文件到内存中→CPU从内存中读取执行.

36.谈谈python中的编码和解码

  s.decode(xxx):将s解码成xxx

  s.encode(xxx):将s编码成xxx

37.并发和并行

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。

你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。  (不一定是同时的)

你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。  

 

并发的关键是你有处理多个任务的能力,不一定要同时。  

并行的关键是你有同时处理多个任务的能力。  

 

所以我认为它们最关键的点就是:是否是『同时』。

并发是轮流处理多个任务,并行是同时处理多个任务
-------------------------------------------------------------------------

  如果某个系统支持两个或者多个动作(Action)同时存在,那么这个系统就是一个并发系统。如果某个系统支持两个或者多个动作同时执行,那么这个系统就是一个并行系统。并发系统与并行系统这两个定义之间的关键差异在于“存在”这个词。

   在并发程序中可以同时拥有两个或者多个线程。这意味着,如果程序在单核处理器上运行,那么这两个线程将交替地换入或者换出内存。这些线程是同时“存在”的——每个线程都处于执行过程中的某个状态。如果程序能够并行执行,那么就一定是运行在多核处理器上。此时,程序中的每个线程都将分配到一个独立的处理器核上,因此可以同时运行。

   我相信你已经能够得出结论——“并行”概念是“并发”概念的一个子集。也就是说,你可以编写一个拥有多个线程或者进程的并发程序,但如果没有多核处理器来执行这个程序,那么就不能以并行方式来运行代码。因此,凡是在求解单个问题时涉及多个执行流程的编程模式或者执行行为,都属于并发编程的范畴。

摘自:《并发的艺术》 — 〔美〕布雷谢斯

38.说说下面几个概念:同步,异步,阻塞,非阻塞?

同步:多个任务之间有先后顺序执行,一个执行完下个才能执行。
异步:多个任务之间没有先后顺序,可以同时执行有时候一个任务可能要在必要的时候获取另一个
同时执行的任务的结果,这个就叫回调!
阻塞:如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。
非阻塞:如果不会卡住,可以继续执行,就是说非阻塞的。
同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。

 39.GET方法和POST请求的区别?

1.请求方式

GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。URL的编码格式采用的是ASCII编码,而不是uniclde,即是说所有的非ASCII字符都要编码之后再传输。

POST请求:POST请求会把请求的数据放置在HTTP请求包的包体中。上面的item=bandsaw就是实际的传输数据。

因此,GET请求的数据会暴露在地址栏中,而POST请求则不会。

2、传输数据的大小

在HTTP规范中,没有对URL的长度和传输的数据大小进行限制。但是在实际开发过程中,对于GET,特定的浏览器和服务器对URL的长度有限制。因此,在使用GET请求时,传输数据会受到URL长度的限制。

对于POST,由于不是URL传值,理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制,Apache、IIS都有各自的配置。

3、安全性

GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。

40.python文件处理中,with open()底层是如何实现的 

class OperateTest(object):

	def open(self):
		print("此处打开文件了!")

	def close(self):
		print("此处关闭文件了!")
	# 进行文件处理
	def operate(self, txt_file):
		print("文件处理中!%s" % txt_file)

	# 进入with语句块时被调用
	def __enter__(self):
		self.open()
		return self

	# 退出with语句块时被调用
	def __exit__(self, exc_type, exc_val, exc_tb):
		self.close()


with OperateTest() as fp:
	fp.operate("text_file")

__enter__方法返回文件对象,__exit__方法则关闭文件,也就是把我们的定义的方法,添加到系统默认调用的方法中进行二次封装,以此来达到简化代码的效果;

41. 谈谈你对多进程,多线程,以及协程的理解,项目是否用到?

  这个问题被问的概率相当之大,其实多线程,多进程,在实际开发中用到的很少,除非是那些对项目性能要求特别高的,有的开发工作几年了,也确实没用过,你可以这么回答,给他扯扯什么是进程,
线程(cpython 中是伪多线程)的概念就行,实在不行你就说你之前写过下载文件时,用过多线程技术,或者业余时间用过多线程写爬虫,提升效率。  

进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所以进程间数据不共享,开销大。
线程: 调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
协程:是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

协程也只是单CPU,但是能减小切换代价提升性能

42. Python 中的进程与线程的使用场景? 

多进程适合在 CPU 密集型操作(cpu 操作指令比较多,如位数多的浮点运算)。

多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)。

线程是并发,进程是并行;

进程之间相互独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。

并行:使用多个内核,同一时刻多个任务同时在运行。多进程 多线程

并发:在同一时间间隔内多个任务都在运行,但是并不会在同一时刻同时运行,存在交替执行的情况。某一时间点实际只处理一个任务,IO多路复用 协程 循环服务器。单线程

实现并行的库有:multiprocessing
实现并发的库有:threading
程序需要执行较多的读写、请求和回复任务的需要大量的 IO 操作,IO 密集型操作使用并发更好。

CPU 运算量大的程序程序,使用并行会更好。、

IO 密集型:系统运作,大部分的状况是 CPU 在等 I/O (硬盘/内存)的读/写。

CPU 密集型:大部份时间用来做计算、逻辑判断等 CPU 动作的程序称之 CPU 密集型。

43.什么是多线程竞争?

线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态即:数据几乎同步会被多个线程占用,造成数据混乱 ,即所谓的线程不安全

那么怎么解决多线程竞争问题?-- 锁。

锁的好处:确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。

锁的坏处:阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了

锁的致命问题:死锁:若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。

GIL 锁 全局解释器锁(只在 cpython 里才有)作用:限制多线程同时执行,保证同一时间只有一个线程执行,所以 cpython 里的多线程其实是伪多线程!

三者的关系:进程里有线程,线程里有协程。

多线程的例子

https://www.cnblogs.com/shuimohei/p/10500634.html

44.谈谈你对多线程的理解

  多线程:一个进程可以开启多个线程,每条线程可以并行(同时)执行不同的任务,多线程技术可以提高程序的执行效率,多线程中,所有变量都由所有线程共享,任何一个变量都可被任何一个

线程修改。线程之间共享数据的最大危险在于多个线程同时更改一个变量。

缺点:   1. 需要用到同步互斥

    2. 可能受到GIL的影响,但是网络IO线程并发还是可以的

优点: 资源消耗比较少

原理:同一时间内,CPU 只能处理一条线程,只有一条线程在执行(工作),多线程并发(同时)执行,其实是 CPU 快速的在多线程之间调度(切换)。因为CPU 切换特别快,造成了多线程并发

的假象。

45.什么是pyc文件?pyc文件的作用

  pyc文件就是py程序编译后得到的文件,是一种二进制文件。当作为模板被调用的时候会自动生成pyc文件,当然也可以通过代码去生成。

作用:

1、提高运行效率:python是解释性语言,需要通过python解释器编译,先编译出pyc文件可以降低编译时间提高运行效率。

2、不想让源码泄露:pyc文件可以独立于py文件,删除py文件也不会出错,第二次运行会优先寻找pyc文件。而且pyc文件无法进行反编译,所以,为了防止源码泄露可以直接发布pyc文件。

46.print 调用 Python 中底层的什么方法?

print 方法默认调用 sys.stdout.write 方法,即往控制台打印字符串。

import sys

obj = "hello world!"

print(obj)
# print等同于
sys.stdout.write(obj + '\n')

47.解释什么是异步非阻塞?

同步异步指的是在客户端

同步意味着客户端提出了一个请求以后,在回应之前只能等待

异步意味着 客户端提出一个请求以后,还可以继续提其他请求阻塞

非阻塞 指的是服务器端

阻塞意味着服务器接受一个请求后,在返回结果以前不能接受其他请求

非阻塞意味着服务器接受一个请求后,尽管没有返回结果,还是可以继续接受其他请求

48.简述数据三大范式?

第一范式:确保每列的原子性. 如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式.

  例如:顾客表(姓名、编号、地址、……)其中"地址"列还可以细分为国家、省、市、区等。

第二范式:在第一范式的基础上更进一层,目标是确保表中的每列都和主键相关. 如果一个关系满足第一范式,并且除了主键以外的其它列,都依赖于该主键,则满足第二范式.

  例如:订单表(订单编号、产品编号、定购日期、价格、……),"订单编号"为主键,"产品编号"和主键列没有直接的关系,即"产品编号"列不依赖于主键列,应删除该列。

第三范式:在第二范式的基础上更进一层,目标是确保每列都和主键列直接相关,而不是间接相关. 如果一个关系满足第二范式,并且除了主键以外的其它列都不依赖于主键列,则满足第三范式. 为了理解第三范式,需要根据Armstrong公里之一定义传递依赖。

  假设A、B和C是关系R的三个属性,如果A-〉B且B-〉C,则从这些函数依赖中,可以得出A-〉C,如上所述,依赖A-〉C是传递依赖。

  例如:订单表(订单编号,定购日期,顾客编号,顾客姓名,……),初看该表没有问题,满足第二范式,每列都和主键列"订单编号"相关,再细看你会发现"顾客姓名"和"顾客编号"相关,"顾客编号"和"订单编号"又相关,最后经过传递依赖,"顾客姓名"也和"订单编号"相关。为了满足第三范式,应去掉"顾客姓名"列,放入客户表中。

49.怎么查看 进程的状态?进程有那些信息,如何保存 ?

  ps -aux 查看进程的信息

  PCB (进程控制块)在内存中开辟的一块空间,里面保存着进程的信息,会随着进程的创建而创建,随着进程的结束而消失    

    1.程序ID(PID、进程句柄):它是唯一的,一个进程都必须对应一个PID。PID一般是整形数字
    2.特征信息:一般分系统进程、用户进程、或者内核进程等
    3.进程状态:运行、就绪、阻塞,表示进程现的运行情况
    4.优先级:表示获得CPU控制权的优先级大小
    5.通信信息:进程之间的通信关系的反映,由于操作系统会提供通信信道
    6.现场保护区:保护阻塞的进程用
    7.资源需求、分配控制信息
    8.进程实体信息,指明程序路径和名称,进程数据在物理内存还是在交换分区(分页)中
    9.其他信息:工作单位,工作区,文件信息等 。

50.谈谈你对python全局解释器锁的理解

python --》 支持多线程 --》同步互斥 --》加锁 --》 超级锁(全局解释器锁,把解释器锁住) --》 在同一时刻,解释器只能解释一个线程 --》大量python库为了省事沿用了这种方法 --》python 多线程效率低下

 

GIL 问题 : 由于python的全局解释器锁造成python的多线程执行效率低下

解决方案:

  多进程vs多线程:不使用线程,使用多进程(Python 执行效率低)

  替代Python解释器:不使用c 、c++ 做解释器 C# java

  python线程适合高用时的IO操作,网路IO。不适合cpu密集型程序

51.平常使用什么文件查找命令?除了find还使用什么?有什么区别或使用场景?

  Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。

  find通常用来再特定的目录下搜索符合条件的文件,也可以用来搜索特定用户属主的文件。

  实际上, find基本上 就相当于 linux下的 "搜索" , 相当于windows下的搜索功能! 它是用来搜索文件的,而grep则是用来搜索文本的, 用来在standard input或文件内部的内容中, 来搜索 文字 内容 的!或者说, 因为这两个都是 "搜索" "查找"的相关命令, 所以还是容易混淆的, 但是记住:

  grep是用来查找文字内容的, 而find是用来查找文件的. 这样就清晰了!

52.10 个常用的 Linux 命令?

pwd 显示工作路径
ls 查看目录中的文件
cd
mkdir 
rm
mv
rmdir
grep:查找内容
find:查找文件
clear
more
date
ps
kill

tar -cvfz 压缩
tar -xvfz 解压

53.python 中 yield 的用法?

  yield简单说来就是一个生成器,这样的函数它记住上次返回时在函数体中的位置。对生成器第二次(或n次)调用时跳转至该位置继续执行。而且相较于return可以降低内存的消耗。

54.关于 Python 程序的运行方面,有什么手段能提升性能?

1、使用多进程,充分利用机器的多核性能
2、对于性能影响较大的部分代码,可以使用 C 或 C++编写
3、对于 IO 阻塞造成的性能影响,可以使用 IO 多路复用来解决
4、尽量使用 Python 的内建函数
5、尽量使用局部变量

55. 常用的 Python 标准库都有哪些?

os 操作系统,time 时间,random 随机,pymysql 连接数据库,threading 线程,multiprocessing进程,queue 队列。
第三方库:
django 和 flask 也是第三方库,requests,virtualenv,selenium,scrapy,xadmin,celery,re,hashlib,md5。
常用的科学计算库(如 Numpy,Scipy,Pandas)。

56. range 和 xrange 的区别?

range返回一个列表,直接开辟一块内存空间保存列表
xrange返回一个生成器,边循环边使用,只有在使用时才会开辟空间

57.代码中要修改不可变数据会出现什么问题? 抛出什么异常? 

  代码不会正常运行,抛出 TypeError 异常。

# 比如修改元祖。会报错
TypeError: 'tuple' object does not support item assignment

58..打乱一个排好序的列表

import random

a = [1, 2, 3, 4]
# 返回值为None
random.shuffle(a)
print(a)

59.为什么函数名字可以当做参数用?

Python 中一切皆对象,函数名是函数在内存中的空间,也是一个对象。

60.回调函数,如何通信的?

回调函数是把函数的指针(地址)作为参数传递给另一个函数,将整个函数当作一个对象,赋值给调用的函数。

61.贪婪匹配和非贪婪匹配

<.*>是贪婪匹配,会从第一个“<”开始匹配,直到最后一个“>”中间所有的字符都会匹配到,中间可能会包含“<>”。
<.*?>是非贪婪匹配,从第一个“<”开始往后,遇到第一个“>”结束匹配,这中间的字符串都会匹配到,但是不会有“<>”。

62.HTTP 协议状态码有什么用,列出你知道的 HTTP 协议的状态码,然后讲出他们都表示什么意思?

100-199:表示服务器成功接收部分请求,要求客户端继续提交其余请求才能完成整个处理过程。
200-299:表示服务器成功接收请求并已完成处理过程,常用 200(OK 请求成功)。
300-399:为完成请求,客户需要进一步细化请求。302(所有请求页面已经临时转移到新的 url)。
304、307(使用缓存资源)。
400-499:客户端请求有错误,常用 404(服务器无法找到被请求页面),403(服务器拒绝访问,
权限不够)。
500-599:服务器端出现错误,常用 500(请求未完成,服务器遇到不可预知的情况)。

63.HTTP 常见请求头?

1. Host (主机和端口号)
2. Connection (链接类型)
3. Upgrade-Insecure-Requests (升级为 HTTPS 请求)
4. User-Agent (浏览器名称)
5. Accept (传输文件类型)
6. Referer (页面跳转处)
7. Accept-Encoding(文件编解码格式)
8. Cookie (Cookie)
9. x-requested-with :XMLHttpRequest (是 Ajax 异步请求)

64.在使用爬虫时会利用User-Agent伪造成对应浏览器成浏览器,User-Agent中包含浏览器的哪些数据,说说看

  Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16

1、浏览器标识--- Chrome

2、操作系统标识--win7内核版本号为WindowsNT6.1

3、加密等级标识---U

  N: 表示无安全加密 
  I: 表示弱安全加密 
  U: 表示强安全加密

4、浏览器语言---en-US

  在首选项 > 常规 > 语言中指定的语言

5、渲染引擎---AppleWebKit

显示浏览器使用的主流渲染引擎有:Gecko、WebKit、KHTML、Presto、Trident、Tasman等,格式为:渲染引擎/版本信息

6、版本信息---10.0.648.133

显示浏览器的真实版本信息,格式为:浏览器/版本信息

65.cookie 和session 的区别?

1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。

3、session会在一定时间内保存在服务器上。当访问增多,会比较占用服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。

4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

5、建议:
   将登陆信息等重要信息存放为SESSION
   其他信息如果需要保留,可以放在COOKIE中

66. 你所遵循的代码规范是什么?请举例说明其要求?

pep8

变量:

  1.常量:大写加下划线ABC_DEF

  2.私有变量:变量名前面加一个前导的下划线_private_value

   3.内置变量:小写,前后各加2个下划线__class__

函数和方法 :

  1.函数名写法:小写和下划线(当然还有小驼峰,大驼峰等)

   2.私有方法:小写和前导一个下划线(跟私有变量类似)

   3.特殊方法:小写,前后各加2个下划线__repe__

代码量:

   1.一行不要超过79列(不要超过满屏显示的列数)

   2.一个函数不要超过30行代码

   3.一个类不要超过200行代码,不要超过10个方法. 4.一个模块不要超过500行

67.什么是线程安全?

在多线程的环境下,保证多个线程同时执行且运行正确,保证对于共享数据可以由多个线程存取,但同一时刻只能有一个线程进行存取.

多线程解决资源竞争的方法是加锁,加锁能保证存取操作的唯一性

68.编写程序,输入一个自然数,输出它的二进制、八进制、十六进制表示形式

Num = input("请输入任性自然数:")
Num = eval(Num)
print("二进制:", bin(Num))
print("八进制:", oct(Num))
print("十六进制:", hex(Num))

69.什么是面向对象编程

将相近相似的逻辑和操作、应用数据、状态以类的形式描述出来,以对象实例在软件系统中复用,以达到提高开发效率的作用

70. AJAX 是什么?如何使用 AJAX?

ajax(异步的 javascript 和 xml) 能够刷新局部网页数据而不是重新加载整个网页。
第一步,创建 xmlhttprequest 对象,var xmlhttp =new XMLHttpRequest();XMLHttpRequest对象用来和服务器交换数据。
第二步,使用 xmlhttprequest 对象的 open()和 send()方法发送资源请求给服务器。
第三步,使用 xmlhttprequest 对象的 responseText 或 responseXML 属性获得服务器的响应。
第四步,onreadystatechange 函数,当发送请求到服务器,我们想要服务器响应执行一些功能就
需要使用 onreadystatechange 函数,每次 xmlhttprequest 对象的 readyState 发生改变都会触发onreadystatechange 函数。

71.MySQL如何优化?结合你的经验

Mysql性能优化主要从三个方面考虑:
一、优化MySQL所在服务器内核(此优化一般由运维人员完成)。
二、对MySQL配置参数进行优化(my.cnf)此优化需要进行压力测试来进行参数调整。
三、对SQL语句以及表优化。

主要针对第三点总结一下:

三、对MySQL语句性能优化的16条经验:
1、使用查询缓存优化查询---查询缓存能使你不用遇到相同查询直接返回缓存结果,像CURDATE(),NOW()不会开启查询缓存,需要使用一个变量去替换SQL函数
2、 EXPLAIN 我们的SELECT查询(可以查看执行的行数)
3、 当只要一行数据时使用LIMIT 1----引擎会查询到一条符合的数据后停止搜索,以提高查询性能
4、 为搜索字段建立索引---索引不一定就是给主键或者是唯一的字段,如果在表中,有某个字段经常用来做搜索,需要将其建立索引。
5、 在Join表的时候使用相当类型的列,并将其索引---应该保证两个表中join的字段时被建立过索引的,且是相同类型,才会启动优化JOIN的SQL语句的机制
6、 千万不要 ORDER BY RAND ()---如果仅为了打乱数据有很多种方法,而这种方法会使查询速度呈指数上升
7、 避免SELECT *------会全搜索
8、 永远为每张表设置一个ID主键----需要使用ID来构建我们的数据结构,集群,分区等操作很依赖ID的性能,主键会提高查询速度
9、 可以使用ENUM 而不要VARCHAR---有限选择的字段,如性别,民族,状态,部门,国家等不要使用VARCHAR而使用ENUM(枚举)
10、 尽可能的使用NOT NULL---null在ORACLA等数据库中是占用空间的
11、固定长度的表会更快--多分配一点空间,但是可以提高查询速度
12、垂直分割----是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。
13、 拆分大的DELETE或INSERT语句---这两个操作是会锁表的,表一锁住了,别的操作都进不来了。
14、 越小的列会越快----大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。所以,把我们的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。
15、 选择正确的存储引擎
16、小心 “永久链接”---“永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了,它会永远处在连接的状态,就算是数据库操作已经结束了。

72.SQL中,drop、delete、truncate有什么区别?

drop:drop table 表名

      删除内容和定义,并释放空间。执行drop语句,将使此表的结构一起删除。

truncate (清空表中的数据):truncate table 表名

      删除内容、释放空间但不删除定义(也就是保留表的数据结构)。与drop不同的是,只是清空表数据而已。

      truncate不能删除行数据,虽然只删除数据,但是比delete彻底,它只删除表数据。

delete:delete from 表名 (where 列名 = 值)

       与truncate类似,delete也只删除内容、释放空间但不删除定义;但是delete即可以对行数据进行删除,也可以对整表数据进行删除。

三者都是删除的意思,但是三者各有些区别

· delete和truncate只删除表的数据不删除表的结构

· 速度 drop > truncate > delete

· 想删除部分数据时, delete 删除时要带上where语句

· 保留表而想删除所有的数据时用truncate

73. 写SQL语句

  

1、每门课都大于80分的学生姓名

select name from table1 GROUP BY name having min(fenshu)>80;

2.一个叫team的表,里面只有一个字段name,一共有4条纪录,分别是a,b,c,d,对应四个球队,现在四个球队进行比赛,用一条sql语句显示所有可能的比赛组合。

select * from team a,team b where a.name>b.name;

74. 索引,主键,外键的区别?

答案:

    定义:

         主键--唯一标识一条记录,不能有重复的,不允许为空

         外键--表的外键是另一表的主键, 外键可以有重复的, 可以是空值

         索引--该字段没有重复值,但可以有一个空值

   作用:

         主键--用来保证数据完整性

         外键--用来和其他表建立联系用的

         索引--是提高查询排序的速度

   个数:

         主键--主键只能有一个

         外键--一个表可以有多个外键

         索引--一个表可以有多个唯一索引

75.解释MySQL外连接、内连接与自连接的区别?

1、Join语法

JOIN 按照功能大致分为如下三类:

INNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录。

LEFT JOIN(左连接):取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录。

RIGHT JOIN(右连接):与 LEFT JOIN 相反,取得右表(table2)完全记录,即是左表(table1)并无匹配对应记录。

1.1、Inner join

内连接,也叫等值连接,inner join产生同时符合A和B的一组数据。

1.2、Left join

左连接从左表(A)产生一套完整的记录,与匹配的记录(右表(B)) .如果没有匹配,右侧将包含null。

 

1.3、Right join(同左连接)

 76.char和varchar的区别?

  char是一种固定长度的类型,varchar则是一种可变长度的类型,它们的区别是:  char(M)类型的数据列里,每个值都占用M个字节,如果某个长度小于M,MySQL就会在它的右边用空格字符补足.(在检索操作中那些填补出来的空格字符将被去掉)在varchar(M)类型的数据列里,每个值只占用刚好够用的字节再加上一个用来记录其长度的字节(即总长度为L+1字节).  

    varchar得适用场景:字符串列得最大长度比平均长度大很多 2.字符串很少被更新,容易产生存储碎片 3.使用多字节字符集存储字符串

    Char得场景:存储具有近似得长度(md5值,身份证,手机号),长度比较短小得字符串(因为varchar需要额外空间记录字符串长度),更适合经常更新得字符串,更新时不会出现页分裂得情况,避免出现存储碎片,获得更好的io性能

78.什么是数据库事务?

 单个逻辑单元执行的一系列操作,这些操作要么全做要么全不做,是不可分割的.事务的开始和结束用户是可以控制的,如果没控制则由数据库默认的划分事务.事务具有以下性质:

    (1)原子性

     指一个事务要么全执行,要么全不执行.也就是说一个事务不可能执行到一半就停止了.比如:你去买东西,钱付掉了,东西没拿.这两步必须同时执行 ,要么都不执行.

    (2)一致性

     指事务的运行并不改变数据库中的一致性.比如 a+b=10;a改变了,b也应该随之改变.

    (3)独立性

     两个以上的事务不会出现交替运行的状态,因为这样可能导致数据的不一致

    (4)持久性

     事务运行成功之后数据库的更新是永久的,不会无缘无故的回滚

79.findall方法在正则匹配中起到什么样的作用?

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

注意: match 和 search 是匹配一次 findall 匹配所有。

80.店铺ID为00000000-99999999这样的8位数,商家不希望含有XXXX这几个数字。

  a.设计一种方式,能按顺序生成出不重复且不含XXX这几个数字的店铺ID;

  b.计算符合上述规则的店铺ID共有多少个

# 迭代器
import itertools
a = [i for i in range(0, 10)]
b = input("请输入不希望包含的数字(逗号隔开):").split(",")
# 去引号
for i in range(len(b)):
	b[i] = int(b[i])
# 需要迭代的数组
c = list(set(a) - set(b))
# 笛卡尔积
res = [x for x in itertools.product(c, repeat=8)]
print(len(res)) 

81.从版本库中删除文件的时候,如果发现删错了可以用什么命令将版本库中的文件进行一键还原?

  git checkout --文件名

 checkout 可以理解为“切回某个文件”的意思。但是要注意,这个命令是对工作区生效的。

82.请分别指出以下几个名词的译名。

 ①Workspace②Index / Stage③Repository④ Remote

①工作区; ②暂存区; ③仓库区(或本地仓库); ④远程仓库

83.. 简述以下git的工作流程。

1、在工作目录中修改某些文件

2、对修改后的文件进行快照,然后保存到暂存区域

3、提交更新,将保存在暂存区域的文件快照永久转储到Git目录中

84.谈谈ARP协议 

 地址解析协议(Address Resolution Protocol):根据 IP 地址获取物理地址的一个 TCP/IP 协议

85.CGI 和 和 WSGI

CGI 是通用网关接口,是连接 web 服务器和应用程序的接口,用户通过 CGI 来获取动态数据或文件等。
CGI 程序是一个独立的程序,它可以用几乎所有语言来写,包括 perl,c,lua,python 等等。
WSGI, Web Server Gateway?Interface,是 Python 应用程序或框架和 Web 服务器之间的一种接口,WSGI 的其中一个目的就是让用户可以用统一的语言(Python)编写前后端。

 86.什么是浏览器缓存

  简单来说,浏览器缓存就是把一个已经请求过的 Web 资源(如 html 页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的 URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个 URL 地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页

87.说说 nginx 和 和 uWISG 服务器之间如何配合工作的?

   首先浏览器发起 http 请求到 nginx 服务器,Nginx 根据接收到请求包,进行 url 分析,判断访问的资源类型,如果是静态资源,直接读取静态资源返回给浏览器,如果请求的是动态资源就转交给 uwsgi 服务器,uwsgi 服务器根据自身的 uwsgi 和 WSGI 协议,找到对应的 Django 框架,Django 框架下的应用进行逻辑处理后,将返回值发送到 uwsgi 服务器,然后 uwsgi 服务器再返回给 nginx,最后 nginx 将返回值返回给浏览器进行渲染显示给用户。

88.django  开发中数据库做过什么优化?

1.设计表时,尽量少使用外键,因为外键约束会影响插入和删除性能;

2.使用缓存,减少对数据库的访问;

3.在 orm 框架下设置表时,能用 varchar 确定字段长度时,就别用 text;

4.可以给搜索频率高的字段属性,在定义时创建索引;

5.Django orm 框架下的 Querysets 本来就有缓存的;\

6.如果一个页面需要多次连接数据库,最好一次性取出所有需要的数据,减少对数据库的查询次数;

7.若页面只需要数据库里某一个两个字段时,可以用 QuerySet.values();

8.在模板标签里使用 with 标签可以缓存 Qset 的查询结果

89.什么是csrf  攻击原理?如何解决?

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

90.有过部署经验?用的什么技术?可以满足多少压力?

  1.有部署经验,在阿里云服务器上部署的
  2.技术有:nginx + uwsgi 的方式来部署 Django 项目
  3.无标准答案(例:压力测试一两千)

91.解释一下MVC开发模式

MVC的具体含义是:model+view+control,即模型+视图+控制

它们各自处理自己的任务:

(1)模型:模型持有所有的数据、状态和程序逻辑。模型独立于视图和控制器。

(2)视图:用来呈现模型。视图通常直接从模型中取得它需要显示的状态与数据。对于相同的信息可以有多个不同的显示形式或视图。

(3)控制器:位于视图和模型中间,负责接受用户的输入,将输入进行解析并反馈给模型,通常一个视图具有一个控制器。

MVC模式将它们分离以提高系统的灵活性和复用性,不使用MVC模式,用户界面设计往往将这些对象混在一起。MVC模式实现了模型和视图的分离,这带来了几个好处。

(1)一个模型提供不同的多个视图表现形式,也能够为一个模型创建新的视图而无须重写模型。一旦模型的数据发生变化,模型将通知有关的视图,每个视图相应地刷新自己。

(2)模型可复用。因为模型是独立于视图的,所以可以把一个模型独立地移植到新的平台工作。

(3)提高开发效率。在开发界面显示部分时,你仅仅需要考虑的是如何布局一个好的用户界面;开发模型时,你仅仅要考虑的是业务逻辑和数据维护,这样能使开发者专注于某一方面的开发,提高开发效率。

92. Python 中的 os 模块常见方法?

 os.remove()删除文件
 os.rename()重命名文件
 os.walk()生成目录树下的所有文件名
 os.chdir()改变目录

 os.mkdir/makedirs 创建目录/多层目录
 os.rmdir/removedirs 删除目录/多层目录
 os.listdir()列出指定目录的文件
 os.getcwd()取得当前工作目录
 os.chmod()改变目录权限
 os.path.basename()去掉目录路径,返回文件名
 os.path.dirname()去掉文件名,返回目录路径
 os.path.join()将分离的各部分组合成一个路径名
 os.path.split()返回(dirname(),basename())元组
 os.path.splitext()(返回 filename,extension)元组
 os.path.getatime\ctime\mtime 分别返回最近访问、创建、修改时间
 os.path.getsize()返回文件大小
 os.path.exists()是否存在
 os.path.isabs()是否为绝对路径
 os.path.isdir()是否为目录
 os.path.isfile()是否为文件

93.Python 的 sys 模块常用方法?

 sys.exit(n) 退出程序,正常退出时 exit(0)
 sys.hexversion 获取 Python 解释程序的版本值,16 进制格式如:0x020403F0
 sys.version 获取 Python 解释程序的版本信息
 sys.maxint 最大的 Int 值
 sys.maxunicode 最大的 Unicode 值
 sys.modules 返回系统导入的模块字段,key 是模块名,value 是模块
 sys.path 返回模块的搜索路径,初始化时使用 PYTHONPATH 环境变量的值
 sys.platform 返回操作系统平台名称
 sys.stdout 标准输出
 sys.stdin 标准输入
 sys.stderr 错误输出
 sys.exc_clear() 用来清除当前线程所出现的当前的或最近的错误信息
 sys.exec_prefix 返回平台独立的 python 文件安装的位置
 sys.byteorder 本地字节规则的指示器,big-endian 平台的值是'big',little-endian 平台的值是'little'
 sys.copyright 记录 python 版权相关的东西
 sys.api_version 解释器的 C 的 API 版本
 sys.version_info 元组则提供一个更简单的方法来使你的程序具备 Python 版本要求功能

94.Python 中三大框架各自的应用场景?

django:主要是用来搞快速开发的,他的亮点就是快速开发,节约成本,正常的并发量不过 10000,如果要实现高并发的话,就要对 django 进行二次开发,比如把整个笨重的框架给拆掉,自己写 socket实现 http 的通信,底层用纯 c,c++写提升效率,ORM 框架给干掉,自己编写封装与数据库交互的框架,因为啥呢,ORM 虽然面向对象来操作数据库,但是它的效率很低,使用外键来联系表与表之间的查询;
flask:轻量级,主要是用来写接口的一个框架,实现前后端分离,提升开发效率,Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展 Flask-Mail,用户认证 Flask-Login),都需要用第三方的扩展来实现。比如可以用 Flask-extension 加入 ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。

Tornado: Tornado 是一种 Web 服务器软件的开源版本。Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,因此 Tornado是实时 Web 服务的一个 理想框架。

95.验证码过期时间怎么设置?

  将验证码保存到数据库或 session,设置过期时间为 1 分钟,然后页面设置一个倒计时(一般是前端
js 实现 这个计时)的展示,一分钟过后再次点击获取新的信息。

96. 对 MVC,MVT 解读的理解?

M:Model,模型,和 MVC 中的 M 功能相同,和数据库进行交互。

V:view,视图,和 MVC 中的 C 功能相同,接收请求,进行处理,与 M 和 T 进行交互,返回应答。
T:Template,模板,和 MVC 中的 V 功能相同,产生 Html 页面

1、 用户点击注册按钮,将要注册的内容发送给网站的服务器。
2、 View 视图,接收到用户发来的注册数据,View 告诉 Model 将用户的注册信息保存进数据库。
3、 Model 层将用户的注册信息保存到数据库中。
4、 数据库将保存的结果返回给 Model
5、 Model 将保存的结果给 View 视图。
6、 View 视图告诉 Template 模板去产生一个 Html 页面。
7、 Template 生成 html 内容返回给 View 视图。

8、 View 将 html 页面内容返回给浏览器。
9、 浏览器拿到 view 返回的 html 页面内容进行解析,展示。

97. Django 创建项目的命令?

django-admin startproject 项目名称

python manage.py startapp 应用 app 名

98.Flask 和 Django 路由映射的区别?  

  在 django 中,路由是浏览器访问服务器时,先访问的项目中的 url,再由项目中的 url 找到应用中
url,这些 url 是放在一个列表里,遵从从前往后匹配的规则。在 flask 中,路由是通过装饰器给每个视
图函数提供的,而且根据请求方式的不同可以一个 url 用于不同的作用。

 99..url 的形式?

scheme://host[:port#]/path/…/[?query-string][#anchor]

协议://IP地址:端口号/资源路径/参数 锚

100.POST,GET请求的过程

POST 请求的过程:
1.浏览器请求 tcp 连接(第一次握手)
2.服务器答应进行 tcp 连接(第二次握手)
3.浏览器确认,并发送 post 请求头(第三次握手,这个报文比较小,所以 http 会在此时进行
第一次数据发送)
4.服务器返回 100 continue 响应
5.浏览器开始发送数据
6.服务器返回 200 ok 响应

GET 请求的过程:
1.浏览器请求 tcp 连接(第一次握手)
2.服务器答应进行 tcp 连接(第二次握手)
3.浏览器确认,并发送 get 请求头和数据(第三次握手,这个报文比较小,所以 http 会在此时
进行第一次数据发送)
4.服务器返回 200 OK 响应

posted @ 2020-07-02 14:55  水墨黑  阅读(308)  评论(0编辑  收藏  举报