-
What
- 什么是Python?(略)
- 什么是Python自省?
简单理解python自省就是解释器的一些辅助功能
dir([obj]):
调用这个方法将返回包含obj大多数属性名的列表(会有一些特殊的属性不包含在内)。obj的默认值是当前的模块对象。
hasattr(obj, attr):
这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值。
getattr(obj, attr):
调用这个方法将返回obj中名为attr值的属性的值,例如如果attr为’bar’,则返回obj.bar。
setattr(obj, attr, val):
调用这个方法将给obj的名为attr的值的属性赋值为val。例如如果attr为’bar’,则相当于obj.bar = val。
或是是访问对象的元数据:如模块的__doc__,__file__,__name__等
或者inspect模块检查函数
- 什么是PEP?
PEP8 是python编码规范
- 什么是pickling和unpick?
python的pickle模块实现了基本的数据序列和反序列化。通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储;通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。
写入2个字符串:
import pickle
with open('date.file', 'wb') as fobj:
pickle.dump('data1', fobj)
pickle.dump('data2', fobj)
读取两个字符:
with open('data.file', 'rb') as fobj:
print pickle.load(fobj)
print pickle.load(fobj)
- 什么是Python装饰器?
让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%s is running" % func.__name__)
return func(*args, **kwargs)
return wrapper
def bar():
print('i am bar')
bar = use_logging(bar)
bar()
相当于下面代码
@use_logging
def bar():
print("i am bar")
- 什么是Python的命名空间?
在一个 Python 程序中的任何一个地方,都存在几个可用的命名空间。
1、每个函数都有着自已的命名空间,叫做局部命名空间,它记录了函数的变量,包括函数的参数和局部定义的变量。
2、每个模块拥有它自已的命名空间,叫做全局命名空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
3、还有就是内置命名空间,任何模块均可访问它,它存放着内置的函数和异常。
当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
1、局部命名空间:特指当前函数或类的方法。如果函数定义了一个局部变量 x,或一个参数 x,Python 将使用它,然后停止搜索。
2、全局命名空间:特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。
3、内置命名空间:对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。
4、如果 Python 在这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined。
- 什么是字典推导式和列表推导式?
1.列表推导式也叫列表解析式,是提供一种方便的列表创建方法,所以,列表解析式返回的是一个列表
格式:用中括号括起来,中间用for语句,后面跟if语句用作判读,满足条件的传到for语句前面用作构建先的列表
[x**2 for item in item_list if item>2]
2.字典解析式,跟列表解析式的用法是差不多
格式:同列表表达,但中括号该改成大括号
{v:k for k,v in item_dict.items()}
3.集合推导式跟列表推导式差不多,都是对一个列表的元素全部执行相同的操作,但集合是一种无重复无序的序列
区别:跟列表推到式的区别在于:1.不使用中括号,使用大括号;2.结果中无重复;3.结果是一个set()集合,集合里面是一个序列
{i*2 for i in [1,1,2]}
- Lambda函数是什么?
lambda简化了函数定义的书写形式。是代码更为简洁,但是使用函数的定义方式更为直观,易理解
>>> f = lambda x:x+1
>>> f(1)
>>> 2
- *args,**kwargs参数是什么?
这两个是python中的可变参数。*args表示任何多个无名参数,它是一个tuple;**kwargs表示关键字参数,它是一个dict。
同时使用*args和**kwargs时,必须*args参数列要在**kwargs前,像foo(a=1, b='2', c=3, a, 1, None, )这样调用的话,会提示语法错误"SyntaxError: non-keyword arg after keyword arg"
- 什么是Pass语句?
pass是空语句,是为了保持程序结构的完整性。pass 不做任何事情,一般用做占位语句
- unittest是什么?
单元测试框架
- 构造器是什么?
__init__为python构造函数
class Simple:
def __init__( self ):
pass
def __del__( self ):
pass
- doc string是什么?
>>> def f():
... '''This sentence for func doc'''
... print 'hello world'
>>> f.__doc__
>>> This sentence for func doc
- 负索引是什么?
表示从后向前搜索
- 模块和包是什么?
把相对稳定、篇幅较长的代码保存在一个纯文本文件中。一般来说,我们把这样扩展名为 .py 的文件称为 Python 脚本。为了提高代码复用率,把一组相关的 Python 相关的定义、声明保存在同一个.py 文件中。此时,这个 Python 脚本就是一个 Python 模块(Module)。可以在解释器中,或者在其他 Python 脚本中,通过 import 载入定义好的 Python 模块。
当 Python 执行脚本,它就会为该脚本赋予一个名字。对于「主程序」来说,这一脚本的 __name__ 被定义为 "__main__";对于被 import 进主程序的模块来说,这一脚本的 __name__ 被定义为脚本的文件名(base filename)。因此if __name__ == "__main__": 可以作为程序的开始
包(package)是 Python 中对模块的更高一级的抽象。Python 允许用户把目录当成模块看待。这样一来,目录中的不同模块文件,就变成了「包」里面的子模块。此外,包目录下还可以有子目录,这些子目录也可以是 Python 包。这种分层,对模块识别、管理,都是非常有好处的。特别地,对于一些大型 Python 工具包,内里可能有成百上千个不同功能的模块。若是逐个模块发布,那简直成了灾难。
Python 要求每一个「包」目录下,都必须有一个名为 __init__.py 的文件。从这个文件的名字上看,首先它有 __ 作为前后缀,我们就知道,这个文件肯定是 Python 内部用来做某种识别用的;其次,它有 init,我们知道它一定和初始化有关;最后,它有 .py 作为后缀名,因此它也是一个 Python 模块,可以完成一些特定的工作。
- 垃圾回收是什么?
Python中的垃圾回收是以引用计数为主,分代收集为辅。引用计数的缺陷是循环引用的问题。
在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存。
class ClassA():
def __init__(self):
print 'object born,id:%s'%str(hex(id(self)))
def __del__(self):
print 'object del,id:%s'%str(hex(id(self)))
def f1():
while True:
c1=ClassA()
del c1
执行f1()会循环输出结果,而且进程占用的内存基本不会变动
问题:
def f2():
while True:
c1=ClassA()
c2=ClassA()
c1.t=c2
c2.t=c1
del c1
del c2
执行f2(),进程占用的内存会不断增大。
创建了c1,c2后,c1对应的内存(记为内存1),c2对应的内存(记为内存2)这两块内存的引用计数都是1,执行c1.t=c2和c2.t=c1后,这两块内存的引用计数变成2
在del c1后,内存1的对象的引用计数变为1,由于不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依然是2,在del c2后,同理,内存1的对象,内存2的对象的引用数都是1
虽然它们两个的对象都是可以被销毁的,但是由于循环引用,导致垃圾回收器都不会回收它们,所以就会导致内存泄露。
解决:
deff3():
c1=ClassA()
c2=ClassA()
c1.t=c2
c2.t=c1
del c1
del c2
print gc.garbage
print gc.collect() #显式执行垃圾回收
print gc.garbage
time.sleep(10)
if __name__ == '__main__':
f3()
有三种情况会触发垃圾回收:
1.调用gc.collect(),
2.当gc模块的计数器达到阀值的时候。
3.程序退出的时候
gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到,采用引用计数的方法管理内存的一个缺陷是循环引用,而gc模块的一个主要功能就是解决循环引用的问题
常用函数:
gc.set_debug(flags)
设置gc的debug日志,一般设置为gc.DEBUG_LEAK
gc.collect([generation])
显式进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一,二代的对象,2代表检查一,二,三代的对象,如果不传参数,执行一个full collection,也就是等于传2。
返回不可达(unreachable objects)对象的数目
gc.set_threshold(threshold0[, threshold1[, threshold2])
设置自动执行垃圾回收的频率。
gc.get_count()
获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
垃圾回收必须要import gc模块,并且is_enable()=True才会启动自动垃圾回收。
这个机制的主要作用就是发现并处理不可达的垃圾对象。
垃圾回收=垃圾检查+垃圾回收
在Python中,采用分代收集的方法。把对象分为三代,一开始,对象在创建的时候,放在一代中,如果在一次一代的垃圾检查中,改对象存活下来,就会被放到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中。
gc模块里面会有一个长度为3的列表的计数器,可以通过gc.get_count()获取。
垃圾回收的应用
1.项目中避免循环引用
2.引入gc模块,启动gc模块的自动清理循环引用的对象机制
3.由于分代收集,所以把需要长期使用的变量集中管理,并尽快移到二代以后,减少GC检查时的消耗
4.gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法,如果一定要使用该方法,同时导致了循环引用,需要代码显式调用gc.garbage里面的对象的__del__来打破僵局
- CSRF是什么?
CSRF(Cross Site Request Forgery)跨站点伪造请求,举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站上了,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造的。
为了避免上面情况的出现,Django引用了CSRF防护机制;Django第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,并把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,这样就能避免被 CSRF 攻击。如果POST请求中没有token随机字符串,则返回403拒绝服务
-
How
- 如何让你的程序更具可读性?
代码关键地方写注释
代码同一函数中也需要按功能进行换行
编写开发文档
- Python是如何被解释的?
当python程序运行时,编译的结果则是保存在位于内存中的PyCodeObject中,当Python程序运行结束时,Python解释器则将PyCodeObject写回到pyc文件中。
当python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复上面的过程。
- 如何在Python中拷贝一个对象?
import copy
浅拷贝copy.copy() 和 深拷贝copy.deepcopy()
copy拷贝一个对象,但是对象的属性还是引用原来的,deepcopy拷贝一个对象,把对象里面的属性也做了拷贝,deepcopy之后完全是另一个对象了
- 如何用Python删除一个文件?
删除文件: os.remove()
删除空目录: os.rmdir()
递归删除空目录: os.removedirs()
递归删除目录和文件
删除非空目录:shutil.rmtree()
如何将一个数字转换成一个字符串?(强制转换str())
Python是如何进行内存管理的?
如何实现duple和list的转换?(强制转换list())
Python里面如何生成随机数?(import random)
如何在一个function里面设置一个全局的变量(global关键字)
Python如何实现单例模式?其他23种设计模式python如何实现?
Deepcopy如何实现?(copy.deepcopy)
算法排序在最坏情况下如何优化?
如何判断单向链表中是否有环?
判断单链表是否存在环
通过两个指针,分别从链表的头节点出发,一个每次向后移动一步,另一个移动两步,两个指针移动速度不一样,如果存在环,那么两个指针一定会在环里相遇
延伸:
找到环的入口点
由上题可知,按照 p2 每次两步,p1 每次一步的方式走,发现 p2 和 p1 重合,确定了单向链表有环路了。接下来,让p2回到链表的头部,重新走,每次步长不是走2了,而是走1,那么当 p1 和 p2 再次相遇的时候,就是环路的入口了。
编程判断两个链表是否相交
1.直接循环判断第一个链表的每个节点是否在第二个链表中。但,这种方法的时间复杂度为O(Length(h1) * Length(h2))。显然,我们得找到一种更为有效的方法,至少不能是O(N^2)的复杂度
2.针对第一个链表直接构造hash表,然后查询hash表,判断第二个链表的每个节点是否在hash表出现,如果所有的第二个链表的节点都能在hash表中找到,即说明第二个链表与第一个链表有相同的节点。时间复杂度为为线性:O(Length(h1) + Length(h2)),同时为了存储第一个链表的所有节点,空间复杂度为O(Length(h1))。是否还有更好的方法呢,既能够以线性时间复杂度解决问题,又能减少存储空间?
3.转换为环的问题。把第二个链表接在第一个链表后面,如果得到的链表有环,则说明两个链表相交。如何判断有环的问题上面已经讨论过了,但这里有更简单的方法。因为如果有环,则第二个链表的表头一定也在环上,即第二个链表会构成一个循环链表,我们只需要遍历第二个链表,看是否会回到起始点就可以判断出来。这个方法的时间复杂度是线性的,空间是常数
4.进一步考虑“如果两个没有环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的”这个特点,我们可以知道,如果它们相交,则最后一个节点一定是共有的。而我们很容易能得到链表的最后一个节点,所以这成了我们简化解法的一个主要突破口。那么,我们只要判断两个链表的尾指针是否相等。相等,则链表相交;否则,链表不相交。
所以,先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了一个时间复杂度,它为O((Length(h1) + Length(h2)),而且只用了一个额外的指针来存储最后一个节点。这个方法时间复杂度为线性O(N),空间复杂度为O(1),显然比解法三更胜一筹。
链表有环,如何判断相交
如果有环且两个链表相交,则两个链表都有共同一个环,即环上的任意一个节点都存在于两个链表上。因此,就可以判断一链表上俩指针相遇的那个节点,在不在另一条链表上
两链表相交的第一个公共节点
采用对齐的思想。计算两个链表的长度 L1 , L2,分别用两个指针 p1 , p2 指向两个链表的头,然后将较长链表的 p1(假设为 p1)向后移动L2 - L1个节点,然后再同时向后移动p1 , p2,直到 p1 = p2。相遇的点就是相交的第一个节点
- 如何遍历一个内部未知的文件夹?
- 数据库如何分区、分表?
水平切分
垂直切分
- 如何对查询命令进行优化?
- 如何理解开源?
- 如何用MVC/MTV的缓存?
- Mys的死锁是如何产生的?
- Sql注入是如何产生的,如何防止?
- xxs如何预防?
- 如何生成共享秘钥? 如何防范中间人攻击?
- 如何管理不同版本的代码?
-
Diff
- 数组和元组之间的区别?
- new和init的区别?
- Python中单下划线和双下划綫的区别?
- 浅拷贝与深拷贝的区别是?
- 使用装饰器的单例和使用其他方法的单例,在后续使用中,有何区别?
- 多进程与多线程的区别?
- select和epoll的区别?
- TCP和UDP的区别?边缘触发和水平触发的区别?
- HTTP连接:get和post的区别?
- varchar与char的区别?
- BTree索引和hash索引的区别?
- 在判断object是否是class的instances时,type和Constance函数的区别?
- primary key和unique的区别?
- ecb和cbc模式有什么区别?
- 对称加密与非对称加密的区别?
- staticmethod和装饰器的区别?
- Xrange和range的区别?
- deepcopy 和 copy的区别?
- os.path和sys.path的区别?
- 生成器(generator)与函数的区别?
- os与sys模块的区别?
- NoSQL和关系数据库的区别?
-
Practice