豆瓣面试官
写代码:
int要考虑负数,0
17、@property有什么作用?如何实现成员变量的只读属性?
@property装饰器就是负责把一个方法变成属性调用,通常用在属性的get方法和set方法,通过设置@property可以实现实例成员变量的直接访问,又保留了参数的检查。另外通过设置get方法而不定义set方法可以实现成员变量的只读属性。
16、简述__new__和__init__的区别
创建一个新实例时调用__new__,初始化一个实例时用__init__,这是它们最本质的区别。 new方法会返回所构造的对象,init则不会. new函数必须以cls作为第一个参数,而init则以self作为其第一个参数.
15、装饰器有什么作用
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。易重用
14、匿名函数,匿名函数有什么局限性
匿名函数,也就是lambda函数,通常用在函数体比较简单的函数上。匿名函数顾名思义就是函数没有名字,因此不用担心函数名冲突。不过Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。
13、Redis是一个使用编写的、支持、基于、可选的
12.1、从浏览器地址栏输入网址,到网页彻底打开,中间都发生了什么?
三大部分:一块是客户端,一块是网络传输层,一块是服务端。
客户端部分:URL由http协议+域名+文件路径+端口,从输入网址开始,这个网址在客户端就会有一个解析,浏览器如果有钩子做出反应。在host列 表中寻找ip。
网络传输部分:1、询问本地域名服务器,域名查询请求会发到服务商的dns服务器,服务商的dns服务器会检查缓存,如果不存在你要访问的这个域名或者缓存状态已经过期,会访问根域名服务器,2、询问根域名服务器,会有二级根域名服务器,三级根域名服务器,根域名服务器也会先检查缓存,如果无法直接返回结果,会检查这个域名是哪个dns服务器负责解析的,然后将请求转发过去,获得这个域名所对应的ip,然后返回给本地服务商的dns,本地服务商的dns就更新缓存,然后把ip返回给客户端。
服务器端:然后到了指定ip,服务器会在80端口有一个守护进程接受这个请求,细说的话这里还有三次握手的协议,那么下面就是webserver是怎么工作的,静态页面的处理比较简单,动态脚本还需要一个解释器系统工作,执行一段代码后将返回结果输出。这里可能又涉及有服务端缓存的,数据库,负载均衡和轮询等等。
数据返回你的电脑时,都存在一个在子网被劫持被篡改的风险,ARP欺骗,ARP协议是什么,为什么你要访问的内容会被劫持会被篡改。
12,HTTP 通信过程
七步
1 建立 TCP 连接 三次握手
2 浏览器 向服务器发送请求命令 请求行:
例如:GET/sample/hello.jsp HTTP/1.1
3 浏览器发送 请求头 信息 以空白行表示结束
4 服务器应答 状态行: HTTP/1.1 200 OK
5 服务器 发送 响应头 信息 以空白行表示结束
6 服务器向浏览器发送数据
7 服务器关闭 TCP 连接
11, 用多线程的方式发起一个 http 请求
线程基类:
import threadingclass base_thread(thread.Thread): def __init__(self, func): threading.Thread.__init__(self) self.func = func # print(type(self.func)) def run(self): self.func
http请求:
# -*- coding: utf-8 -*-#!/bin/env python# Python2.7import requestsimport jsonclass ConfigHttp: def __init__(self, host, port, headers): self.host = host self.port = port self.headers = headers # 封装HTTP GET请求方法 def get(self, url, params): # params = urllib.parse.urlencode(params) url = "http://" + self.host + ":" + self.port + url try: r = requests.get(url, params=params, headers = self.headers) r.encoding = 'UTF-8' return r.text except Exception: print('no json data returned') return {} # 封装HTTP POST请求方法,支持上传图片 def post(self, url, data, files=None): data = eval(data) url = 'http://' + self.host + ':' + str(self.port)+url r =requests.post(url, files=files, data=data) print(data) json_response = r.text return json_response
10, 打印一个给定路径下的所有文件路径
使用os.walk
os.walk(top, topdown=True, οnerrοr=None, followlinks=False)
可以得到一个三元tupple(dirpath, dirnames, filenames),
第一个为起始路径,第二个为起始路径下的文件夹,第三个是起始路径下的文件。
dirpath 是一个string,代表目录的路径,
dirnames 是一个list,包含了dirpath下所有子目录的名字。
filenames 是一个list,包含了非目录文件的名字。
这些名字不包含路径信息,如果需要得到全路径,需要使用os.path.join(dirpath, name).
import ospp = '/Users/yly/Desktop/wh8xfyYvLPtW'print os.listdir(pp), '\n'def Test1(rootDir): for root,dirs,files in os.walk(rootDir): for filespath in files: # 打印文件路径 print os.path.join(root,filespath) for dirpath in dirs: # 打印目录名称 print os.path.join(root,dirpath) # 使用递归def Test2(rootDir): for lists in os.listdir(rootDir): path = os.path.join(rootDir, lists) print path if os.path.isdir(path): Test2(path)print 'Test1, 先打印第一层,再第二层'Test1(pp)print 'Test2:'Test2(pp)
['.DS_Store', '11', '123'] Test1:/Users/yly/Desktop/wh8xfyYvLPtW/.DS_Store/Users/yly/Desktop/wh8xfyYvLPtW/11/Users/yly/Desktop/wh8xfyYvLPtW/12/Users/yly/Desktop/wh8xfyYvLPtW/12/.DS_Store/Users/yly/Desktop/wh8xfyYvLPtW/12/21Test2:/Users/yly/Desktop/wh8xfyYvLPtW/.DS_Store/Users/yly/Desktop/wh8xfyYvLPtW/12/Users/yly/Desktop/wh8xfyYvLPtW/12/.DS_Store/Users/yly/Desktop/wh8xfyYvLPtW/12/21/Users/yly/Desktop/wh8xfyYvLPtW/3[Finished in 0.0s]
9,yield
带有 yield 的函数不再是一个普通函数,而是一个生成器generator,可用于迭代,有next()方法
yield 就是 return 返回一个值,下一次返回这个值的下一个值
send(msg) 与 next() 的区别:send可以传递参数给 yield表达式
def g(): print '1' x = yield'hello' print '2', 'x =', x y = 5 + (yield x) print '3', 'y = ', yf = g() #首先生成一个迭代器f
print f.next(), '\n' print f.send(5), '\n' print f.send(2)
1 hello #f.next()会使生成器函数执行到yield,生成一个值然后挂起。2 x = 5 #f.send(msg)会在生成器函数内部返回值,执行到第二个yield,生成值后挂起5 Traceback (most recent call last): File "/Users/yly/Desktop/1 2.py", line 17, inprint f.send(2)StopIteration #后面没有 yield 了,抛出异常3 y = 7
8,数据库索引,原理,怎么用
例如 字典
原理:控制磁盘IO次数的数量级 => 使用 b+树
B+树特点:插入与修改有叫稳定的对数时间复杂度
How do you build a database?
二叉树:最多两个子树、左小于父父小于左、在n个节点中查找,一般需要log(n)次比较
B+树:一个节点可以多个值(指针)、数据满后再增加层、父节点a个值则有按a+1个子节点
数据库事务(transaction)是指批量进行一系列数据库操作,只要有一步不成功,整个操作都不成功。所以需要有一个"操作日志",以便失败时对操作进行回滚。
特点:IO次数取决于b+数的高度h、索引的最左匹配特性
慢查询优化:建立索引:最左前缀匹配、选择区分度高的列作为索引、索引不参与计算、尽量扩展索引
7,Python 多线程 多进程
一个线程就是执行一个子程序
协程: 本质:allowing multiple entry points for suspending and resuming execution at certain locations. 允许多个入口对程序进行挂起、继续执行等操作
最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
线程:I\O阻塞后,进程被挂起,不用切换页表、刷新TLB,只刷新寄存器
6,class method self
5,生成器
4,协程
廖雪峰:
import time def consumer(): r = '' while True: n = yield r if not n: return print ('[CONSUMER] Consuming %s...' & n) time.sleep(1) r = '200 OK'def produce(c): c.next() n = 0 while n <5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) print('[PRODUCER] Consumer return: %s' % r) c.close()if __name__ == '__main__': c = consumer() produce(c)
[PRODUCER] Producing 1...[CONSUMER] Consuming 1...[PRODUCER] Consumer return: 200 OK[PRODUCER] Producing 2...[CONSUMER] Consuming 2...[PRODUCER] Consumer return: 200 OK[PRODUCER] Producing 3...[CONSUMER] Consuming 3...[PRODUCER] Consumer return: 200 OK[PRODUCER] Producing 4...[CONSUMER] Consuming 4...[PRODUCER] Consumer return: 200 OK[PRODUCER] Producing 5...[CONSUMER] Consuming 5...[PRODUCER] Consumer return: 200 OK
注意到consumer函数是一个generator(生成器),把一个consumer传入produce后:
-
首先调用c.next()启动生成器;
-
然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
-
consumer通过yield拿到消息,处理,又通过yield把结果传回;
-
produce拿到consumer处理的结果,继续生产下一条消息;
-
produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
最后套用Donald Knuth的一句话总结协程的特点:
“子程序就是协程的一种特例。”
新建进程时,我们需要分配新的进程描述符,并且分配新的地址空间(和父地址空间的映射保持一致,但是两者同时进入COW状态)。这些过程需要一定的开销。
线程并不产生新的地址空间和资源描述符表,而是复用父进程的。
协程是一种编程组件,可以在不陷入内核的情况进行同一进程中上下文切换
协程与生成器区别:生成器是数据的生产者,协程是数据的消费者
GIL: Global Interpreter Lock 全局排它锁
"协程就是你可以暂停执行的函数"
维基:“协程 是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序”。
3, utf8 unicode gbk
UTF-8就是unicode的实现方式.
Unicode只是字符集,而UTF-X才是编码.
UTF-8 is a character encoding capable of encoding all possible characters, or code points, in Unicode. The encoding is variable-length and uses 8-bit code units.
简单来说,unicode,gbku,是字符集,而utf-8,uft-16之i就是这个值的编码方式.如"汉"的unicode值与gbk就是不一样的,假设unicode为a040,gbk为b030,而uft-8码,就是把那个值表现的形式.utf-8码完全只针对unicode来组织的,如果GBK要转utf-8必须先转unicode码,再转utf-8就OK了.
2,参数传递
*args and **kwargs
*args代表位置参数,它会接收任意多个参数并把这些参数作为元组传递给函数。**kwargs代表的关键字参数,字典,允许你使用没有事先定义的参数名,另外,位置参数一定要放在关键字参数的前面。
1,RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
enumerate(nums)
def enumerate(collection):
'Generates an indexed series: (0,coll[0]), (1,coll[1]) ...' i = 0 it = iter(collection) while 1: yield (i, it.next()) i += 1