python面试题 1w道
第一章 python基础
1、下面这段代码的输出结果是什么?请解释。
def extendList(val, list=[]):
list.append(val)
return list
list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')
print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3
怎样修改extendList的定义能够产生以下预期的行为?
上面代码输出结果将是:
list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']
很多人都会误认为list1=[10],list3=[‘a’],因为他们以为每次extendList被调用时,列表参数的默认值都将被设置为[].但实际上的情况是,新的默认列表只在函数被定义的那一刻创建一次。
当extendList被没有指定特定参数list调用时,这组list的值随后将被使用。这是因为带有默认参数的表达式在函数被定义的时候被计算,不是在调用的时候被计算。因此list1和list3是在同一个默认列表上进行操作(计算)的。而list2是在一个分离的列表上进行操作(计算)的。(通过传递一个自有的空列表作为列表参数的数值)。
extendList的定义可以作如下修改。
尽管,创建一个新的列表,没有特定的列表参数。
下面这段代码可能能够产生想要的结果。
def extendList(val, list=None):
if list is None:
list = [] l
ist.append(val) re
turn list
通过上面的修改,输出结果将变成:
list1 = [10]
list2 = [123]
list3 = ['a']
2、下面这段代码的输出结果将是什么?请解释。
def multipliers():
return [lambda x : i * x for i in range(4)]
print [m(2) for m in multipliers()]
你如何修改上面的multipliers的定义产生想要的结果?
上面代码输出的结果是[6, 6, 6, 6] (不是我们想的[0, 2, 4, 6])。
上述问题产生的原因是Python闭包的延迟绑定。这意味着内部函数被调用时,参数的值在闭包内进行查找。因此,当任何由multipliers()返回的函数被调用时,i的值将在附近的范围进行查找。那时,不管返回的函数是否被调用,for循环已经完成,i被赋予了最终的值3。
因此,每次返回的函数乘以传递过来的值3,因为上段代码传过来的值是2,它们最终返回的都是6(3*2)。碰巧的是,《The Hitchhiker’s Guide to Python》也指出,在与lambdas函数相关也有一个被广泛被误解的知识点,不过跟这个case不一样。由lambda表达式创造的函数没有什么特殊的地方,它其实是和def创造的函数式一样的。
下面是解决这一问题的一些方法。
一种解决方法就是用Python生成器。
def multipliers():
for i in range(4): yield lambda x : i * x
另外一个解决方案就是创造一个闭包,利用默认函数立即绑定。
def multipliers():
return [lambda x, i=i : i * x for i in range(4)]
还有种替代的方案是,使用偏函数:
from functools import partial
from operator import mul
def multipliers():
return [partial(mul, i) for i in range(4)]
3、下面这段代码的输出结果将是什么?请解释。
class Parent(object):
x = 1
class Child1(Parent):
pass
class Child2(Parent):
pass
print Parent.x, Child1.x, Child2.x
Child1.x = 2
print Parent.x, Child1.x, Child2.x
Parent.x = 3
print Parent.x, Child1.x, Child2.x
输出结果将是:
1 1 1
1 2 1
3 2 3
让很多人困惑或惊讶的是最后一行输出为什么是3 2 3 而不是 3 2 1.为什么在改变parent.x的同时也改变了child2.x的值?但与此同时没有改变Child1.x的值?
此答案的关键是,在Python中,类变量在内部是以字典的形式进行传递。
如果一个变量名没有在当前类下的字典中发现。则在更高级的类(如它的父类)中尽心搜索直到引用的变量名被找到。(如果引用变量名在自身类和更高级类中没有找到,将会引发一个属性错误。)
因此,在父类中设定x = 1,让变量x类(带有值1)能够在其类和其子类中被引用到。这就是为什么第一个打印语句输出结果是1 1 1
因此,如果它的任何一个子类被覆写了值(例如说,当我们执行语句Child1.x = 2),这个值只在子类中进行了修改。这就是为什么第二个打印语句输出结果是1 2 1
最终,如果这个值在父类中进行了修改,(例如说,当我们执行语句Parent.x = 3),这个改变将会影响那些还没有覆写子类的值(在这个例子中就是Child2)这就是为什么第三打印语句输出结果是3 2 3
4、下面这段代码在Python2下输出结果将是什么?请解释。
def div1(x,y):
print "%s/%s = %s" % (x, y, x/y)
def div2(x,y):
print "%s//%s = %s" % (x, y, x//y)
div1(5,2)
div1(5.,2)
div2(5,2)
div2(5.,2.)
在Python3下结果会有怎样的不同?(当然,假设上述打印语句被转换成Python3的语法)
在Python2中,上述代码输出将是
5/2 = 2
5.0/2 = 2.5
5//2 = 2
5.0//2.0 = 2.0
默认情况下,Python 2 自动执行整形计算如果两者都是整数。因此,5/2 结果是2,而5./2结果是2.5
注意,在Python2中,你可以通过增加以下引用来覆写这个行为。
fromfutureimportdivision
同时要注意的是,//操作符将总是执行整形除法,不管操作符的类型。这就是为什么即使在Python 2中5.0//2.0的结果是2.0。然而在Python3中,没有此类特性,
例如,在两端都是整形的情况下,它不会执行整形除法
因此,在Python3中,将会是如下结果:
5/2 = 2.5
5.0/2 = 2.5
5//2 = 2
5.0//2.0 = 2.0
注: 在 Python 3 中,/ 操作符是做浮点除法,而 // 是做整除(即商没有余数,比如 10 // 3 其结果就为 3,余数会被截除掉,而 (-7) // 3 的结果却是 -3。这个算法与其它很多编程语言不一样,需要注意,它们的整除运算会向0的方向取值。而在 Python 2 中,/ 就是整除,即和 Python 3 中的 // 操作符一样)
5、下面代码的输出结果将是什么?
list = ['a', 'b', 'c', 'd', 'e']
print list[10:]
下面的代码将输出[],不会产生IndexError错误。就像所期望的那样,尝试用超出成员的个数的index来获取某个列表的成员。
例如,尝试获取list[10]和之后的成员,会导致IndexError.
然而,尝试获取列表的切片,开始的index超过了成员个数不会产生IndexError,而是仅仅返回一个空列表。
这成为特别让人恶心的疑难杂症,因为运行的时候没有错误产生,导致bug很难被追踪到。
6、考虑下列代码片段:
list = [ [ ] ] * 5
list # output?
list[0].append(10)
list # output? l
ist[1].append(20) l
ist # output? li
st.append(30) li
st # output?
2,4,6,8行将输出什么结果?试解释。
输出的结果如下:
[[], [], [], [], []]
[[10], [10], [10], [10], [10]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]
解释如下:
第一行的输出结果直觉上很容易理解,例如 list = [ [ ] ] * 5 就是简单的创造了5个空列表。然而,理解表达式list=[ [ ] ] * 5的关键一点是它不是创造一个包含五个独立列表的列表,而是它是一个创建了包含对同一个列表五次引用的列表。只有了解了这一点,我们才能更好的理解接下来的输出结果。
list[0].append(10) 将10附加在第一个列表上。
但由于所有5个列表是引用的同一个列表,所以这个结果将是:
[[10],[10],[10],[10],[10]]
同理,list[1].append(20)将20附加在第二个列表上。但同样由于5个列表是引用的同一个列表,所以输出结果现在是:
[[10,20],[10,20],[10,20],[10,20],[10,20]]
作为对比, list.append(30)是将整个新的元素附加在外列表上,因此产生的结果是: [[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30].
7、Given a list of N numbers。
给定一个含有N个数字的列表。
使用单一的列表生成式来产生一个新的列表,该列表只包含满足以下条件的值:
(a)偶数值
(b)元素为原始列表中偶数切片。
例如,如果list[2]包含的值是偶数。那么这个值应该被包含在新的列表当中。因为这个数字同时在原始列表的偶数序列(2为偶数)上。然而,如果list[3]包含一个偶数,
那个数字不应该被包含在新的列表当中,因为它在原始列表的奇数序列上。
对此问题的简单解决方法如下:
[xforxinlist[::2]ifx%2==0]
例如,给定列表如下:
list=[1,3,5,8,10,13,18,36,78]
列表生成式[x for x in list[::2] if x%2 == 0] 的结果是,
[10,18,78]
这个表达式工作的步骤是,第一步取出偶数切片的数字,
第二步剔除其中所有奇数。
8、给定以下字典的子类,下面的代码能够运行么?为什么?
class DefaultDict(dict):
def __missing__(self, key):
return []
d = DefaultDict()
d['florp'] = 127
能够运行。
当key缺失时,执行DefaultDict类,字典的实例将自动实例化这个数列。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1、Python如何实现单例模式?
Python有两种方式可以实现单例模式,下面两个例子使用了不同的方式实现单例模式:
1.
classSingleton(type):
def__init__(cls,name,bases,dict): supe
r(Singleton,cls).__init__(name,bases,dict) cls.ins
tance=None def__call
__(cls,*args,**kw): ifcls.instan
ceisNone: cls.instance=su
per(Singleton,cls).__call__(*args,**kw) returncls.instance
classMyClass(object)
: __metaclass__=Singlet
on printMyClass() printMyC
lass()
- 使用decorator来实现单例模式
defsingleton(cls):
instances={} de
fgetinstance(): ifc
lsnotininstances: instanc
es[cls]=cls() returnins
tances[cls] returngeti
nstance @singleton
classMyClas
s: …
2:什么是lambda函数?
Python允许你定义一种单行的小函数。定义lambda函数的形式如下:labmda 参数:表达式lambda函数默认返回表达式的值。你也可以将其赋值给一个变量。lambda函数可以接受任意个参数,包括可选参数,但是表达式只有一个:
>>>g=lambdax,y:x*y >>>g(
3,4) 12 >>>
g=l
ambdax,y=0,z=0:x+y+z >>>g(1) 1 >>>
g(3,4,7)
14
也能够直接使用lambda函数,不把它赋值给变量:
>>>(lambdax,y=0,z=0:x+y+z)(3,5,6) 1
4
如果你的函数非常简单,只有一个表达式,不包含命令,可以考虑lambda函数。否则,你还是定义函数才对,毕竟函数没有这么多限制。
3:Python是如何进行类型转换的?
Python提供了将变量或值从一种类型转换成另一种类型的内置函数。int函数能够将符合数学格式数字型字符串转换成整数。否则,返回错误信息。
>>>int(”34″)
34
>>>int(”1234ab”)#不能转换成整数 Va
lueError:invalidliteralforint():1234ab
函数int也能够把浮点数转换成整数,但浮点数的小数部分被截去。
>>>int(34.1234)
34
>>>int(-2.46) -
2
函数°oat将整数和字符串转换成浮点数:
>>>float(”12″)
12.0
>>>float(”1.111111″) 1
.111111
函数str将数字转换成字符:
>>>str(98)
‘98′
>>>str(”76.765″) ‘
76.765′
整数1和浮点数1.0在python中是不同的。虽然它们的值相等的,但却属于不同的类型。这两个数在计算机的存储形式也是不一样。
4:Python如何定义一个函数
函数的定义形式如下:
def<name>(arg1,arg2,…argN): <s
tatements>
函数的名字也必须以字母开头,可以包括下划线“ ”,但不能把Python的关键字定义成函数的名字。函数内的语句数量是任意的,每个语句至少有一个空格的缩进,以表示此语句属于这个函数的。缩进结束的地方,函数自然结束。 下面定义了一个两个数相加的函数:
>>>defadd(p1,p2): pr
intp1,“+”,p2,“=”,p1+p2 >>>add(
1,2) 1+2=3
函数的目的是把一些复杂的操作隐藏,来简化程序的结构,使其容易阅读。函数在调用前,必须先定义。也可以在一个函数内部定义函数,内部函数只有在外部函数调用时才能够被执行。程序调用函数时,转到函数内部执行函数内部的语句,函数执行完毕后,返回到它离开程序的地方,执行程序的下一条语句。
5:Python是如何进行内存管理的?
Python的内存管理是由Python得解释器负责的,开发人员可以从内存管理事务中解放出来,致力于应用程序的开发,这样就使得开发的程序错误更少,程序更健壮,开发周期更短
6:如何反序的迭代一个序列?
how do I iterate over a sequence in reverse order
如果是一个list, 最快的解决方案是:
list.reverse()
try:
forxinlist: “d
osomethingwithx” final
ly: list.
reverse()
如果不是list, 最通用但是稍慢的解决方案是:
foriinrange(len(sequence)-1,-1,-1): x=se
quence[i] <dosom
ethingwithx>
7:Python里面如何实现tuple和list的转换?
函数tuple(seq)可以把所有可迭代的(iterable)序列转换成一个tuple, 元素不变,排序也不变。 例如,tuple([1,2,3])返回(1,2,3), tuple(’abc’)返回(’a’.’b',’c').如果参数已经是一个tuple的话,函数不做任何拷贝而直接返回原来的对象,所以在不确定对象是不是tuple的时候来调用tuple()函数也不是很耗费的。 函数list(seq)可以把所有的序列和可迭代的对象转换成一个list,元素不变,排序也不变。 例如 list([1,2,3])返回(1,2,3), list(’abc’)返回['a', 'b', 'c']。如果参数是一个list, 她会像set[:]一样做一个拷贝
8:Python面试题:请写出一段Python代码实现删除一个list里面的重复元素
可以先把list重新排序,然后从list的最后开始扫描,代码如下:
ifList:
List.sort()
last=List[-1] fo
riinrange(len(List)-2,-1,-1): iflast=
=List[i]:delList[i] else:last=
List[i]
9:Python文件操作的面试题
- 如何用Python删除一个文件? 使用os.remove(filename)或者os.unlink(filename);
- Python如何copy一个文件? shutil模块有一个copyfile函数可以实现文件拷贝
10:Python里面如何生成随机数?
标准库random实现了一个随机数生成器,实例代码如下:
importrandom
random.random()
它会返回一个随机的0和1之间的浮点数
11:如何用Python来发送邮件?
可以使用smtplib标准库。 以下代码可以在支持SMTP监听器的服务器上执行。
importsys,smtplib f
romaddr=raw_input(”From:“) toad
drs=raw_input(”To:“).split(’,') print“E
ntermessage,endwith^D:” msg=” while1
: line=s
ys.stdin.
readline() ifnotline: break
msg=msg+line
#发送邮件部
分 server=smtplib.
SMTP(’loc
alhost’) server.sendmail(fromaddr,t
oaddrs,msg) server.quit()
12:Python里面如何拷贝一个对象?
一般来说可以使用copy.copy()方法或者copy.deepcopy()方法,几乎所有的对象都可以被拷贝 一些对象可以更容易的拷贝,Dictionaries有一个copy方法:
newdict=olddict.copy()
13:有没有一个工具可以帮助查找python的bug和进行静态的代码分析?
有,PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告 Pylint是另外一个工具可以进行coding standard检查。
14:如何在一个function里面设置一个全局的变量?
解决方法是在function的开始插入一个global声明:
deff()
globalx
15:用Python匹配HTML tag的时候,<.>和<.?>有什么区别?
当重复匹配一个正则表达式时候, 例如<.*>, 当程序执行匹配的时候,会返回最大的匹配值 例如:
importre
s=‘<html><head><title>Title</title>’ pr
int(re.match(’<.*>’,s).group())
会返回一个匹配而不是 而
importre
s=‘<html><head><title>Title</title>’ pr
int(re.match(’<.*?>’,s).group())
则会返回 <.>这种匹配称作贪心匹配 <.?>称作非贪心匹配
16:Python里面search()和match()的区别?
match()函数只检测RE是不是在string的开始位置匹配, search()会扫描整个string查找匹配, 也就是说match()只有在0位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回none 例如:
print(re.match(’super’,’superstition’).span())
会返回(0, 5) 而
print(re.match(’super’,‘insuperable’))
则返回None search()会扫描整个字符串并返回第一个成功的匹配 例如:
print(re.search(’super’,’superstition’).span())
返回(0, 5)
print(re.search(’super’,‘insuperable’).span())
返回(2, 7)
17:如何用Python来进行查询和替换一个文本字符串?
可以使用sub()方法来进行查询和替换,sub方法的格式为:sub(replacement, string[, count=0]) replacement是被替换成的文本 string是需要被替换的文本 count是一个可选参数,指最大被替换的数量 例子:
importre
p=re.compile(’(blue|white|red)’) pr
int(p.sub(’colour’,'bluesocksandredshoes’)) print(
p.sub(’colour’,'bluesocksandredshoes’,count=1))
输出:
coloursocksandcolourshoes col
oursocksandredshoes
subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新的字符串和总共替换的数量 例如:
importre
p=re.compile(’(blue|white|red)’) pr
int(p.subn(’colour’,'bluesocksandredshoes’)) print(
p.subn(’colour’,'bluesocksandredshoes’,count=1))
输出
(’coloursocksandcolourshoes’,2) (’co
loursocksandredshoes’,1)
18:介绍一下except的用法和作用?
Python的except用来捕获所有异常, 因为Python里面的每次错误都会抛出 一个异常,所以每个程序的错误都被当作一个运行时错误。 一下是使用except的一个例子:
try:
foo=opne(”file”)#open被错写为opne ex
cept: sy
s.exit(”couldnotopenfile!”)
因为这个错误是由于open被拼写成opne而造成的,然后被except捕获,所以debug程序的时候很容易不知道出了什么问题 下面这个例子更好点:
try:
foo=opne(”file”)#这时候except只捕获IOError exc
eptIOError: sys.
exit(”couldnotopenfile”)
19:Python中pass语句的作用是什么?
pass语句什么也不做,一般作为占位符或者创建占位程序,pass语句不会执行任何操作,比如:
whileFalse:
pass
pass通常用来创建一个最简单的类:
classMyEmptyClass:
pass
pass在软件设计阶段也经常用来作为TODO,提醒实现相应的实现,比如:
definitlog(*args):
pass#pleaseimplementthis
20:介绍一下Python下range()函数的用法?
如果需要迭代一个数字序列的话,可以使用range()函数,range()函数可以生成等差级数。 如例:
foriinrange(5) pr
int(i)
这段代码将输出0, 1, 2, 3, 4五个数字 range(10)会产生10个值, 也可以让range()从另外一个数字开始,或者定义一个不同的增量,甚至是负数增量 range(5, 10)从5到9的五个数字 range(0, 10, 3) 增量为三, 包括0,3,6,9四个数字 range(-10, -100, -30) 增量为-30, 包括-10, -40, -70 可以一起使用range()和len()来迭代一个索引序列 例如:
a=['Nina','Jim','Rainman','Hello'] fori
inrange(len(a)): print(i
,a[i])
21:有两个序列a,b,大小都为n,序列元素的值任意整形数,
无序;要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。 将两序列合并为一个序列,并排序,为序列Source
拿出最大元素Big,次大的元素Small
在余下的序列S[:-2]进行平分,得到序列max,min
将Small加到max序列,将Big加大min序列,重新计算新序列和,和大的为max,小的为min。
Python代码
defmean(sorted_list): if
notsorted_list: retu
rn(([],[])) big=s
orted_list[-1] small=s
orted_list[-2] big_list,
small_list=mean(sorted_list[:-2]) big_list.app
end(small) small_list.a
ppend(big) big_list_sum
=sum(big_list) small_list_sum
=sum(small_list) ifbig_list_sum>s
mall_list_sum: return((big_list,sm
all_list)) else: return((small_li
st,big
_list)) tests=[[1,2,3,4,5,6,700,8
00], [10001,10000,100,90,50,1], ran
ge(1,11), [12312,12311,232,
210,30,29,3,2,
1,1] ] forlintests: l.sort() print print“Sour
ce
List:”,l l1,l2=m
ean(l) pr
int“Re
sultList:”,l1,l2 print“Dista
nce:”,abs(sum(l1
)-sum(l2)) print‘-*’*40
输出结果
SourceList:[1,2,3,4,5,6,700,800] ResultList:
[1,4,5,800][2,3,6,700] Distance:99 -*-*-*-*-*-
*-*-*-*-*-*-*-*-*-*
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* SourceList:[1,50,90,100,10000,
10001] ResultList:[50,90,10000][1,100,10001] Di
stance:38 -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-*-*-*-*-*-*-*-*-*-
*-*-*-*-*-*-*-*-*-*-*-* SourceList:[1,2,3,4,5,6,7,8,9,10] ResultList:[2,3,6,7,10]
[1,4,5,8,9] Distance:1 -*-*-*-*-*-*-*-*-*-*-*-*-
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-*-*-* SourceList:
[1,1,2,3,29,30,210,232,12311,12312] ResultList:[1,3,29,232,12311][1,2,30,210,1231
2] Distance:21 -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
1.如何反向迭代一个序列
#如果是一个list,最快的方法使用reverse
tempList = [1,2,3,4]
tempList.reverse()
for x in tempList:
print x
#如果不是list,需要手动重排
templist = (1,2,3,4)
for i in range(len(templist)-1,-1,-1):
print templist[i]
2.如何查询和替换一个文本中的字符串
#最简单的方法使用replace()
tempstr = "hello you hello python are you ok"
print tempstr.replace("you","python")
#还可以使用正则,有个sub()
tempstr = "hello you hello python are you ok"
import re
rex = r'(hello|Use)'
print re.sub(rex,"Bye",tempstr)
3.使用python实现单例模式
#方法一:可以使用__new__方法
#在__new__方法中把类实例绑定到类变量_instance上,如果cls._instance为None表示该类还没有实例化过,实例化该类并返回。如果cls_instance不为None表示该类已实例化,直接返回cls_instance
class SingleTon(object):
def __new__(cls,*args,**kwargs):
if not hasattr(cls,'_instance'):
cls._instance = object.__new__(cls,*args,**kwargs) return
cls._instance class TestClass
(SingleTon): a = 1
test1 = TestClass()
test2 = TestClass()
print test1.a,test2.a
test1.a=2
print test1.a,test2.a
print id(test1),id(test2)
#方法二:使用装饰器,建立过实例的就放到instances里面,下次建立的时候先检查里面有没有
def SingleTon(cls,*args,**kwargs):
instances = {}
print instances d
ef _singleton(): i
f cls not in instances: ins
tances[cls] = cls(*args,**kwargs) print insta
nces return instance
s[cls] return _singleton
@SingleTon
class LastClass(object):
a = 1 t
est1 = LastClass() p
rint test1.a t
est2 = LastClass() p
rint test2.a
#方法三:使用__metaclass__(元类)关于元类看看这个吧;http://blog.jobbole.com/21351/
class SignalTon(type):
def __init__(cls,name,bases,dict):
super(SignalTon, cls).__init__(name,bases,dict) c
ls._instance = None
def __call__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(SignalTon,cls).__call__(*args,**kwargs) return
cls._instance
class TestClass(object):
__metaclass__ = SignalTon
test1 = TestClass()
test2 = TestClass()
test1.a = 2
print test1.a,test2.a
print id(test1),id(test2)
#方法四:共享属性 所谓单例就是所有的引用(实例,对象)拥有相同的属性和方法,同一个类的实例天生都会有相同的方法,那我们只需要保证同一个类所产生的实例都具有相同的属性。所有实例共享属性最简单直接的方法就是共享__dict__属性指向。
class SingleTon(object):
_state = {}
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls,*args,**kwargs) obj
.__dict__ = cls._state return
obj
class TestClass(SingleTon):
a = 1
test1 = TestClass()
test2 = TestClass()
print test1.a,test2.a
test1.a = 2
print test1.a,test2.a
print id(test1),id(test2)
#方法五:使用同一个模版
#写在mysingleton.py中
class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()
#写在要使用这个实例的py文件里面,在不同的引用的地方都引用相同的实例,以此实现单例模式
from mysingleton import my_singleton
my_singleton.foo()
4.重新实现str.strip()
def rightStrip(tempStr,splitStr):
endindex = tempStr.rfind(splitStr)
while endindex != -1 and endindex == len(tempStr) - 1:
tempStr = tempStr[:endindex] en
dindex = tempStr.rfind(splitStr) return te
mpStr
def leftStrip(tempStr,splitStr):
startindex = tempStr.find(splitStr)
while startindex == 0:
tempStr = tempStr[startindex+1:] sta
rtindex = tempStr.find(splitStr) return te
mpStr
str = " H " p
rint str p
rint leftStrip(str,' ') p
rint rightStrip(str,' ') #
输出
H H
H
5.super的原理
#阅读下面的代码,它的输出结果是什么?
class A(object):
def __init__(self):
print "enter A"
super(A, self).__init__() # new p
rint "leave A"
class B(object):
def __init__(self):
print "enter B"
super(B, self).__init__() # new p
rint "leave B"
class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"
class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D" cl
ass E(B, C): de
f __init__(self): pr
int "enter E" sup
er(E, self).__init__() # change print
"leave E"
class F(E, D):
def __init__(self):
print "enter F"
super(F, self).__init__() # change p
rint "leave F"
#输出
enter F
enter E
enter B
enter C
enter D
enter A
leave A
leave D
leave C
leave B
leave E
leave F
非常棒的讲解:
http://www.cnblogs.com/lovemo1314/archive/2011/05/03/2035005.html
6.闭包
常用的装饰器就是闭包的一种
def make_adder(addend):
def adder(addend):
return addend+addend retur
n adder
P1 = make_adder(5)
P2= make_adder(4)
print p1(10)
#输出15
print p2(10)
#输出14
闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外 http://www.cnblogs.com/ma6174/archive/2013/04/15/3022548.html
https://foofish.net/python-closure.html
7.给列表中的字典排序
list 对象 alist [{“name”:”a”,”age”:20},{“name”:”b”,”age”:30},{“name”:”c”,”age”:25}]按照 age 从大到小排序
alist = [{"name":"a","age":20},{"name":"b","age":30},{"name":"c","age":25}]
alist.sort(key=lambda:x:-x.get("age"))
print alist
8.合并两个列表排除重复元素
用简洁的方法合并alist = [‘a’,’b’,’c’,’d’,’e’,’f’] blist = [‘x’,’y’,’z’,’e’,’f’]并且元素不能重复
alist = ['a','b','c','d','e','f']
blist = ['x','y','z','e','f']
def merge_list(*args):
s = set()
for i in args:
s = s.union(i) print
(s) return
s
merge_list(alist,blist)
9.打乱一个排好序的列表
from random import shuffle
alist = range(10)
print(alist)
shuffle(alist)
print(alist)
10.简单的实现一个栈结构 stack
class Stack(object):
def __init__(self):
self.value = []
def push(self,x):
self.value.append(x)
def pop(self):
self.value.pop()
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.value)
stack.pop()
print(stack.value)
11.输入一个日期,返回时一年中的哪一天
from datetime import datetime
def which_day(year,month,day):
return (datetime(year,month,day)-datetime(year,1,1)).days+1
print(which_day(2017,1,15))
12.把字符串”k1:1|k2:2|k3:3”处理成 python 字典的形式:{k1:1,k2:2,k3:3}
def string_to_dict(string):
d = {}
for kv in string.split("|"):
k,v = kv.split(":") if
v.isdigit(): v=int
(v) d[k]=v retu
rn d
print(string_to_dict("k1:1|k2:2|k3:3"))
13.判断输入的值是否在矩阵之中(杨氏矩阵)
在一个二维数组之中,每一行都按照从走到右递增的顺序排序,每一列到按照从上到下的顺序排序.请完成一个函数,输入这样的一个二维手术和一个整数,判断数组中是否含有该整数
#处理数组矩阵
arr = [[1,4,7,10,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]]
def get_num(num,data=None):
while data:
if num > data[0][-1]:
del data[0] elif nu
m<data[0][-1]: data = li
st(zip(*data)) del data[-1]
data = list(zip(*data
)) else: return True
data.clear()
return False print (ge
t_num(18,arr))
不处理数组矩阵 使用 step-wise 线性搜索
def getvalue(data,value):
m = len(data)-1
n = len(data[0])-1 r
= 0 c =
n while
c>=0 and r<=m: if va
lue == data[r][c]: return
True elif value>data
[r][c]: r = r+1 else:
c = c-1 ret
urn False
14.获取最大公约数(欧几里得算法)
a= 25
b=15
def max_common(a,b):
while b:
a,b=b,a%b ret
urn a
详解:
https://blog.csdn.net/franktan2010/article/details/38229641
15.求两个数的最小公倍数(公式法)
两个数的乘积等于这两个数的 最大公约数与最小公倍数的积
a=25
b=15
def min_common(a,b):
c= a*b
while b:
a,b=b,a%b retur
n c//a
详情:
https://zhidao.baidu.com/question/90232880
16.获取中位数
如果总数个数是奇数,按从小到大的顺序,取中间的那个数;如果总数个数是偶数个的话,按从小到大的顺序,取中间那两个数的平均数。
#计算中位数
def mediannum(num):
listnum = [num[i] for i in range(len(num))]
listnum.sort() l
num = len(num) if
lnum % 2 == 1: i =
int((lnum + 1) / 2)-1 return
listnum[i] else: i =
int(lnum
/ 2)-1 return (listnum[i
] + listnum[i + 1]) / 2
详情:
https://blog.csdn.net/qq_33363973/article/details/78773144
def medin(data):
data.sort()
half = len(data)//2 r
eturn (data[half]+data[~half])/2 l = [
1,3,4,53,2,46,8,42,82] print
(median(l))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Python 是一种解释型,交互式,面向对象的高级编程语言。和别的一些使用标点符号的语言不同,Python使用了大量的英语单词作为关键字,因而具有很好的可读性。而且跟其他编程语言相比,它有更少的语法结构。
- Python 是一种解释性语言:这意味着你的程序在执行之前不需要编译,而是由解释器在运行时处理。这个特点跟PERL,PHP很像。
- Python是可交互的:这意味着你可以使用一个Python终端在写程序时和解释器直接交互。
- Python是面向对象的:它支持面向对象风格和将代码封装成类的各种技术。
- Python非常适合编程初级人员:Python非常容易入门而且应用领域也非常广泛,从简单的文字处理到网络浏览和游戏开发。
1) 什么是Python?使用Python有什么好处?
Python是一种编程语言,它有对象,模块,线程,异常处理和自动内存管理。它简洁,简单,方便,容易扩展,有许多自带的数据结构,而且它开源。
2) 什么是PEP8?
PEP8 是一个编程规范,一些关于如何让你的程序更具有可读性的建议。
3) 什么是pickling和unpickling?
Pickle模块读入任何Python对象,将它们转换成字符串,然后使用dump函数将其转储到一个文件中——这个过程叫做pickling。反之从存储的字符串文件中提取原始Python对象的过程叫做unpickling。
4) Python 是如何被解释的?
Python是一种解释性语言。Python的源代码可以直接运行。Python解释器会将源代码转换成中间语言,之后再翻译成机器码再执行。
5) Python是怎样管理内存的?
Python的内存管理是由私有heap空间管理的。所有的Python对象和数据结构都在一个私有heap中。程序员没有访问该heap的权限,只有解释器才能对它进行操作。
为Python的heap空间分配内存是由Python的内存管理模块进行的。其核心API提供一些访问该模块的方法供程序员使用。
Python有自带的垃圾回收系统,它回收并释放没有被使用的内存让它们能够被其他程序使用。
6) 有哪些工具可以帮助debug或做静态分析?
PyChecker 是一个静态分析工具,它报告源代码中的错误并且会报告错误类型和复杂度。Pylint是检验模块是否达到代码标准的另一个工具。
7) 什么是Python装饰器?
Python装饰器是Python中的特有变动,可以使修改函数变得更容易。
8) 数组和元组之间的区别是什么?
数组和元组之间的区别是数组内容是可以被修改的而元组内容是只读的。元组可以被哈希比如作为字典的关键字。
9) 参数按值传递和引用传递是怎样实现的?
Python中的一切都是类,所有的变量都是一个对象的引用。引用的值是由函数确定的,因此无法被改变。但是如果一个对象是可以被修改的你可以改动对象。
10) 字典推导式和列表推导式是什么?
他们是可以轻松创建字典和列表的语法结构。
11) Python都有那些自带的数据结构?
Python自带的数据结构分为可变的和不可变的。
可变的有:
- 数组
- 集合
- 字典
不可变的有:
- 字符串
- 元组
- 数
12) 什么是Python的命名空间?
在 Python 中,所有的名字都存在于一个空间中,它们在该空间中存在和被操作——这就是命名空间。它就好像一个盒子,每一个变量名字都对应装着一个对象。当查询变量的时候,会从该盒子里面寻找相应的对象。
13) Python 中的lambda 是什么?
这是一个常被用于代码中的单个表达式的匿名函数。
14) 为什么 lambda 没有语句?
匿名函数lambda没有语句的原因是它被用于在代码被执行的时候构建新的函数对象并且返回。
15) Python中的pass是什么?
Pass是一个在Python中不会被执行的语句。在复杂语句中,如果一个地方需要暂时被留白,它常常被用于占位符。
16) Python中什么是遍历器?
遍历器用于遍历一组元素,比如列表这样的容器。、
17) Python中的unittest是什么?
在Python中,unittest是Python中的单元测试框架。它支持共享搭建,自动测试,在测试中暂停代码,将不同测试迭代成一组等等功能。
18) 在Python中什么是slicing?
Slicing是一种在有序的对象类型中(数组,元组,字符串)节选某一段的语法。
19)在Python中什么是生成器?
生成器是实现迭代器的一种机制。它功能的实现依赖于yield表达式,除此之外它跟普通的函数没有两样。
20) Python 中docstring什么?
在 Python 中文档字符串被称为docstring,它被用于在Python中为函数,模块和类注释生成文档。
21) 在Python中如何拷贝一个对象?
如果要在Python中拷贝一个对象,大多时候你可以用copy.copy () 或者copy.deepcopy()。并不是所有的对象都可以被拷贝。
22) Python中的负索引是什么?
Python中的序列索引可以是正也可以是负。如果是正索引,0是序列中的第一个索引,1是第二个索引。如果是负索引,(-1)是最后一个索引而(-2)是倒数第二个索引。
23) 如何将一个数字转换成一个字符串?
你可以使用自带函数str()将一个数字转换为字符串。如果你想要八进制或者十六进制数,可以用oct() 或 hex()。
24) Xrange和range的区别是什么?
Xrange返回一个xrange对象,而range返回一个数组。不管那个范围多大,Xrange使用同样的内存。
25) Python中的模块和包是什么?
在Python中,模块是搭建程序的一种方式。每一个Python代码文件都是一个模块,并可以引用其他的模块比如对象和属性。
一个包含许多Python代码的文件夹是一个包。一个包可以包含模块和子文件夹。
1,为什么学习python
2,通过甚途径学习的python
3,公司线上和开发环境用的什么系统
线上用的centos6,开发环境用的Ubuntu
4,简述python和java,php,c,c#,c++的对比
个人觉得,python简单易懂,代码工整美观,可读性强,有成熟的框架,丰富的第三方模块,开发效率非常高
5,简述解释性和编译型语言,只是翻译的时间不同
解释型:读一行翻译一行
编译型:一次性翻译成机器语言
6,python解释器种类以及特点
cpython,官方版本
jyhton 把python编译成java字节码在jvm上运行
ipython,把python代码编译成C#字节码在CLR上运行
pypy Python实现的Python,将Python的字节码字节码再编译成机器码
7,位和字节的关系—8位一个字节
8,b,B,KB,MB,GB的关系 —bit就是位,8位一个字节,后面的关系都是1024
9,pep8规范例举
对齐----换行对齐
行最大长度79
空行----类之间空两行,类里面的方法空一行
import导入----模块注释之后,顶部导入,分行导入,导入顺序(标准库,第三方库,本地库,每组库加空行)
注释----# 块注释(与同级代码对齐,#后面一个空格)
----# 行内注释(与代码至少两个空格,节制使用)
变量命名----类名(大写字母开头)
----函数名(小写,可下划线分割)
函数和方法参数
始终要将 self 作为实例方法的的第一个参数。
始终要将 cls 作为类静态方法的第一个参数。
如果函数的参数名和已有的关键词冲突,加下划线,例class_
11,ascii,unicode,utf8,gbk区别
ascii 是最早美国用的标准信息交换码,把所有的字母的大小写,各种符号用 二进制来表示,共有256中,加入些拉丁文等字符,1bytes代表一个字符
Unicode是为了统一世界各国语言的不用,统一用2个bytes代表一个字符,可以表达2**16=65556个,称为万国语言,特点:速度快,但浪费空间,可以用在内存处理中,兼容了utf-8,gbk,ASCII,
utf-8 为了改变Unicode的这种缺点,规定1个英文字符用1个字节表示,1个中文字符用3个字节表示,特点;节省空间,速度慢,用在硬盘数据传输,网络数据传输,相比硬盘和网络速度,体现不出来的
gbk 是中文的字符编码,用2个字节代表一个字符
12,字节码和机器码区别
机器码是电脑的CPU可直接解读的数据
字节码是二进制文件,一种中间码
13,三元运算编写格式
c=a if a==1 else b
14,python2和python3区别
python2 | python3 |
---|---|
print ‘1’ | print(‘1’) |
range( 0, 4 ) 结果 是 列表 [0,1,2,3 ] | list( range(0,4) ) |
xrange( 0, 4 ) 适用于 for 循环的变量控制 | range(0,4) |
原: file( … )或 open(…) | open(…) |
raw_input() | input() |
15,py2项目迁移py3
2to3 example.py
16 python2 python3 int long
python2有非浮点数准备的int和long类型
在python3里,只有一种整数类型int,
17、用一行代码实现数值交换:
a = 1
b = 2
###########
a, b = b, a
print(a, b)
18、Python3和Python2中 int 和 long的区别?
py3中没有long整型,统一使用int,大小和py2的long类似。
py2中int最大不能超过sys.maxint,根据不同平台大小不同;
在int类型数字后加L定义成长整型,范围比int更大。
19、xrange和range的区别?
#range产生的是一个列表,xrange产生的是生成器。
#数据较大时xrange比range好。
#Range一下把数据都返回,xrange通过yield每次返回一个。
20、文件操作时:xreadlines和readlines的区别?
# Readlines:读取文件的所有行,返回一个列表,包含所有行的结束符
# Xreadlines:返回一个生成器,循环使用和readlines基本一致 。(py2有,py3没有)
21、列举布尔值为False的常见值?
# []、{}、None、’’、()、0、False
22、字符串、列表、元组、字典每个常用的5个方法?
#Str:
Split:分割
Strip:去掉两边的空格
Startwith:以什么开头
Endwith:以什么结尾
Lower:小写
Upper:大写
#List:
Append:追加
Insert:插入
Reverse:反转
Index:索引
Copy:拷贝
Pop:删除指定索引处的值,不指定索引默认删除最后一个。
#Tuple:
Count:查看某个元素出现的次数
Index:索引
#Dict:
Get:根据key取value
Items:用于循环,取出所有key和value
Keys:取出所有key
Values:取出所有的value
Clear:清空字典
Pop:删除指定键对应的值,有返回值;
show
23、lambda表达式格式以及应用场景?
# 格式:
匿名函数:res = lambda x:i*x print(res(2))
# 应用场景:
Filter(),map(),reduce(),sorted()函数中经常用到,它们都需要函数形参数;
一般定义调用一次。
(reduce()对参数序列中元素进行累积)
24、pass的作用?
# Pass一般用于站位语句,保持代码的完整性,不会做任何操作。
25、*arg和**kwarg作用
# 他们是一种动态传参,一般不确定需要传入几个参数时,可以使用其定义参数,然后从中取参
'*args':按照位置传参,将传入参数打包成一个‘元组’(打印参数为元组-- tuple)
'**kwargs':按照关键字传参,将传入参数打包成一个‘字典’(打印参数为字典-- dict)
26、is和==的区别
==:判断某些值是否一样,比较的是值
is:比较的是内存地址(引用的内存地址不一样,唯一标识:id)
27、简述Python的深浅拷贝以及应用场景?
#浅拷贝:
不管多么复杂的数据结构,只copy对象最外层本身,该对象引用的其他对象不copy,
内存里两个变量的地址是一样的,一个改变另一个也改变。
#深拷贝:
完全复制原变量的所有数据,内存中生成一套完全一样的内容;只是值一样,内存地址不一样,一方修改另一方不受影响
28、Python垃圾回收机制?
# Python垃圾回收机制
Python垃圾回收机制,主要使用'引用计数'来跟踪和回收垃圾。
在'引用计数'的基础上,通过'标记-清除'(mark and sweep)解决容器对象可能产生的循环引用问题.
通过'分代回收'以空间换时间的方法提高垃圾回收效率。
'引用计数'
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。
当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,
它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。
\优点:1.简单 2.实时性
\缺点:1.维护引用计数消耗资源 2.循环引用
'标记-清楚机制'
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,
遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,
然后清扫一遍内存空间,把所有没标记的对象释放。
'分代技术'
分代回收的整体思想是:
将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,
垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。
python垃圾回收机制
29、Python的可变类型和不可变类型?
# 可变类型:列表、字典、集合
# 不可变类型:数字、字符串、元祖
(可变与否指内存中那块内容value)
30、求结果:
v = dict.fromkeys(['k1', 'k2'], [])
# 内存中k1和k2都指向同一个[](内存地址相同),只要指向的[]发生变化,k1和k2都要改变(保持一致)
v['k1'].append(666)
print(v) # {'k1': [666], 'k2': [666]}
v['k1'] = 777
print(v) # {'k1': 777, 'k2': [666]}
31、求结果:
def num():
return [lambda x: i * x for i in range(4)] #返回一个列表,里面是四个函数对象 i=3
print([m(2) for m in num()])
32、列举常见的内置函数
# map:遍历序列,为每一个序列进行操作,返回一个结果列表
l = [1, 2, 3, 4, 5, 6, 7]
def pow2(x):
return x * x
res = map(pow2, l)
print(list(res)) #[1, 4, 9, 16, 25, 36, 49]
--------------------------------------------------------------
# reduce:对于序列里面的所有内容进行累计操作
from functools import reduce
def add(x, y):
return x+y
print(reduce(add, [1,2,3,4])) #10
--------------------------------------------------------------
# filter:对序列里面的元素进行筛选,最终获取符合条件的序列。
l = [1, 2, 3, 4, 5]
def is_odd(x): # 求奇数
return x % 2 == 1
print(list(filter(is_odd, l))) #[1, 3, 5]
--------------------------------------------------------------
#zip用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表
a = [1,2,3]
b=[4,5,6]
c=[4,5,6,7,8]
ziped1 = zip(a,b)
print('ziped1>>>',list(ziped1)) #[(1, 4), (2, 5), (3, 6)]
ziped2 = zip(a,c)
print('ziped2>>>',list(ziped2)) #[(1, 4), (2, 5), (3, 6)],以短的为基准
33、filter、map、reduce的作用?
# map:遍历序列,为每一个序列进行操作,获取一个新的序列
# reduce:对于序列里面的所有内容进行累计操作
# filter:对序列里面的元素进行筛选,最终获取符合条件的序列。
34、一行代码实现9*9乘法表
print('\n'.join([' '.join(['%s*%s=%2s' % (j, i, i * j) for j in range(1, i + 1)]) for i in range(1, 10)]))
35、如何安装第三方模块?以及用过哪些第三方模块?
# a、可以在pycharm的settings里面手动下载添加第三方模块
# b、可以在cmd终端下用pip insatll 安装
# 用过的第三方模块:requests、pymysql、DBUtils等
36、至少列举8个常用模块都有那些?
re:正则
os:提供了一种方便的使用操作系统函数的方法。
sys:可供访问由解释器使用或维护的变量和与解释器进行交互的函数。
random:随机数
json:序列化
time:时间
37、re的match和search区别?
# match:从字符串起始位置开始匹配,如果没有就返回None
# serch:从字符串的起始位置开始匹配,匹配到第一个符合的就不会再去匹配了
38、什么是正则的贪婪匹配?
# 匹配一个字符串没有节制,能匹配多少就匹配多少,直到匹配完为止
39、求结果:
a. [ i % 2 for i in range(10) ]
b. ( i % 2 for i in range(10) )
# a结果是一个列表生成式,结果是一个列表(i % 2为生成的元素):
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
# b结果是一个生成器
40、求结果:
a. 1 or 2
b. 1 and 2
c. 1 < (2==2)
d. 1 < 2 == 2
a=1 or 2 #1
b=1 and 2 #2
c=1 < (2==2) #False
d=1 < 2 == 2 #True
41、def func(a,b=[]) 这种写法有什么坑?
# 函数传参为列表陷阱,列表是可变数据类型,可能会在过程中修改里面的值
42、如何实现 “1,2,3” 变成 [‘1’,’2’,’3’] ?
a = '1,2,3'
a=a.replace(',','')
res = [i for i in a]
print(res)
43、如何实现[‘1’,’2’,’3’]变成[1,2,3] ?
l = ['1','2','3']
res = [int(i) for i in l]
print(res)
44、比较: a = [1,2,3] 和 b = [(1),(2),(3) ] 以及 b = [(1,),(2,),(3,) ] 的区别?
前两个列表内是int
最后一个列表内是元组
45、如何用一行代码生成[1,4,9,16,25,36,49,64,81,100] ?
l = [i*i for i in range(1,11)]
print(l)
46、一行代码实现删除列表中重复的值 ?
l = [1,1,1,2,2,3,3,3,4,4]
print(list(set(l))) # [1, 2, 3, 4]
47、如何在函数中设置一个全局变量 ?
通过global指定变量,该变量会变成全局变量
48、logging模块的作用?以及应用场景?
# 作用:
管理我们程序的执行日志,省去用print记录操作日志的操作,并且可以将标准输入输出保存到日志文件
# 场景:
爬虫爬取数据时,对爬取进行日志记录,方便分析、排错。
49、请用代码简单实现stack 。
class Stack(object):
# 初始化栈
def __init__(self):
self.items = []
# 判断栈是否为空
def is_empty(self):
return self.items == []
# 返回栈顶
def peek(self):
return self.items[len(self.items) - 1]
# 返回栈大小
def size(self):
return len(self.items)
# 压栈
def push(self, item):
self.items.append(item)
# 出栈
def pop(self):
return self.items.pop()
50、常用字符串格式化哪几种?
# %占位符
s = 'I am %s' %'zhugaochao'
print(s) #I am zhugaochao
# format格式化输出
i = "i am {}".format('zhugaochao')
print(i) #i am zhugaochao
51、简述 生成器、迭代器、可迭代对象 以及应用场景?
# 装饰器:
能够在不修改原函数代码的基础上,在执行前后进行定制操作,闭包函数的一种应用
场景:
- flask路由系统
- flask before_request
- csrf
- django内置认证
- django缓存
# 手写装饰器;
import functools
def wrapper(func):
@functools.wraps(func) #不改变原函数属性
def inner(*args, **kwargs):
执行函数前
return func(*args, **kwargs)
执行函数后
return inner
1. 执行wapper函数,并将被装饰的函数当做参数。 wapper(index)
2. 将第一步的返回值,重新赋值给 新index = wapper(老index)
@wrapper #index=wrapper(index)
def index(x):
return x+100
# ---------------------------------------------------------------
# 生成器:
一个函数内部存在yield关键字
应用场景:
- rang/xrange
- redis获取值
- conn = Redis(......)
- v=conn.hscan_iter() # 内部通过yield 来返回值
- stark组件中
- 前端调用后端的yield
# ---------------------------------------------------------------
# 迭代器:
内部有__next__和__iter__方法的对象,帮助我们向后一个一个取值,迭代器不一定是生成器
应用场景:
- wtforms里面对form对象进行循环时,显示form中包含的所有字段
- 列表、字典、元组
(可以让一个对象被for循环)
show
52、用Python实现一个二分查找的函数。
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def search(zhi,li,start=0,end=None):
end = len(li) if end is None else end
zj = (end - start) // 2+start
if start<=end:
if zhi>li[zj]:
return search(3,li,start=zj+1,end=end)
elif zhi<li[zj]:
return search(3,li,start=start,end=zj-1)
else:
return zj
return '找不到这个值'
print(search(2,li))
53、谈谈你对闭包的理解?
# 闭包函数就是内部的函数调用外部函数的变量,常用于装饰器。
# 判断闭包函数的方法:__closure__,输出的__closure__有cell元素说明是闭包函数
# 闭包的意义与应用:延迟计算:
54、os和sys模块的作用?
# os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;
# sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python运行环境
55、如何生成一个随机数?
import random
print(random.random())
print(random.randint(1, 10))
56、如何使用python删除一个文件?
import os
os.remove('文件名以及路径')
57、谈谈你对面向对象的理解?
#封装:
其实就是将很多数据封装到一个对象中,类似于把很多东西放到一个箱子中,
如:一个函数如果好多参数,起始就可以把参数封装到一个对象再传递。
#继承:
如果多个类中都有共同的方法,那么为了避免反复编写,就可以将方法提取到基类中实现,
让所有派生类去继承即可。
#多态:
指基类的同一个方法在不同派生类中有着不同功能。python天生支持多态。
58、Python面向对象中的继承有什么特点?
#Python3的继承机制
# 子类在调用某个方法或变量的时候,首先在自己内部查找,如果没有找到,则开始根据继承机制在父类里查找。
# 根据父类定义中的顺序,以深度优先的方式逐一查找父类!
继承参数的书写有先后顺序,写在前面的被优先继承。
59、面向对象深度优先和广度优先是什么?
# Python的类可以继承多个类,Python的类如果继承了多个类,那么其寻找方法的方式有两种
当类是经典类时,多继承情况下,会按照深度优先方式查找
当类是新式类时,多继承情况下,会按照广度优先方式查找
简单点说就是:经典类是纵向查找,新式类是横向查找
经典类和新式类的区别就是,在声明类的时候,新式类需要加上object关键字。在python3中默认全是新式类
60、面向对象中super的作用?
主要在子类继承父类的所有属性和方法时来使用
61、是否使用过functools中的函数?其作用是什么?
在装饰器中,会用到;functools.wraps()主要在装饰器中用来装饰函数
Stark上下文管理源码中,走到视图阶段时有用到functools中的偏函数,request = LocalProxy(partial(_lookup_req_object, 'request'))
62、列举面向对象中带爽下划线的特殊方法,如:new、init
# __getattr__
CBV
django配置文件
wtforms中的Form()示例化中 将"_fields中的数据封装到From类中"
# __mro__
wtform中 FormMeta中继承类的优先级
# __dict__
是用来存储对象属性的一个字典,其键为属性名,值为属性的值
# __new__
实例化但是没有给当前对象
wtforms,字段实例化时返回:不是StringField,而是UnboundField
est frawork many=Turn 中的序列化
# __call__
flask 请求的入口app.run()
字段生成标签时:字段.__str__ => 字段.__call__ => 插件.__call__
# __iter__
循环对象是,自定义__iter__
wtforms中BaseForm中循环所有字段时定义了__iter__
# -metaclass
作用:用于指定当前类使用哪个类来创建
场景:在类创建之前定制操作
示例:wtforms中,对字段进行排序。
63、如何判断是函数还是方法
# 看他的调用者是谁,如果是类,需要传入参数self,这时就是一个函数;
# 如果调用者是对象,不需要传入参数值self,这时是一个方法。
(FunctionType/MethodType)
64、静态方法和类方法区别?
Classmethod必须有一个指向类对象的引用作为第一个参数;
@classmethod
def class_func(cls):
""" 定义类方法,至少有一个cls参数 """
print('类方法')
---------------------------------------------------------
Staticmethod可以没有任何参数。
@staticmethod
def static_func():
""" 定义静态方法 ,无默认参数"""
print('静态方法')
65、列举面向对象中的特殊成员以及应用场景
1. __doc__:表示类的描述信息。
2.__module__:表示当前操作的对象在那个模块;
3.__class__:表示当前操作的对象的类是什么。
4.__init__:构造方法,通过类创建对象时,自动触发执行。
5.__call__:对象后面加括号,触发执行。
6.__dict__:类或对象中的所有成员。
7.__str__:如果一个类中定义了__str__方法,那么在打印对象时,默认输出该方法的返回值。
class Foo:
def __str__(self):
return 'aaa'
obj = Foo()
print(obj)
# 输出:aaa
8.__getitem__、__setitem__、__delitem__:用于索引操作,如字典。以上分别表示获取、设置、删除数据。
9.__iter__:用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__。
66、1、2、3、4、5 能组成多少个互不相同且无重复的三位数
import itertools
print(len(list(itertools.permutations('12345',3))))
#60个
67、什么是反射?以及应用场景?
反射就是以字符串的方式导入模块,以字符串的方式执行函数
# 应用场景:
rest framework里面的CBV
68、metaclass作用?以及应用场景?
类的metaclass
默认是type。我们也可以指定类的metaclass值。
69、用尽量多的方法实现单例模式。
# 单例模式
# 1、使用__new__方法
class Singleton(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls, *args, **kw)
return cls._instance
class MyClass(Singleton):
a = 1
# 2、共享属性
# 创建实例时把所有实例的__dict__指向同一个字典,这样它们具有相同的属性和方法.
class Borg(object):
_state = {}
def __new__(cls, *args, **kw):
ob = super(Borg, cls).__new__(cls, *args, **kw)
ob.__dict__ = cls._state
return ob
class MyClass2(Borg):
a = 1
# 3、装饰器版本
def singleton(cls, *args, **kw):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance
@singleton
class MyClass:
...
# 4、import方法
# 作为python的模块是天然的单例模式
# mysingleton.py
class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()
# to use
from mysingleton import my_singleton
my_singleton.foo()
name="t69">70、装饰器的写法以及应用场景。
import functools
def wrapper(func):
@functools.wraps(func)
def inner(*args, **kwargs):
print('我是装饰器')
return func
return inner
@wrapper
def index():
print('我是被装饰函数')
return None
index()
# 应用场景
- 高阶函数
- 闭包
- 装饰器
- functools.wraps(func)
71、异常处理写法以及如何主动抛出异常(应用场景)
while True:
try:
x = int(input("Please enter a number: "))
break
except ValueError:
print("Oops! That was no valid number. Try again ")
# raise主动抛出一个异常
参考:点击查看
72、什么是面向对象的mro
MRO:方法解析顺序
它定义了 Python 中多继承存在的情况下,解释器查找继承关系的具体顺序
73、isinstance作用以及应用场景?
# 来判断一个对象是否是一个已知的类型。
# 使用isinstance函数还可以来判断'类实例变量'属于哪一个类产生的。
75、json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型?
# 可序列化数据类型:
字典、列表、数字、字符串、元组;如果是元组,自动转成列表(再转回去的话也是列表)
# 自定义时间序列化转换器
import json
from json import JSONEncoder
from datetime import datetime
class ComplexEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
else:
return super(ComplexEncoder,self).default(obj)
d = { 'name':'alex','data':datetime.now()}
print(json.dumps(d,cls=ComplexEncoder))
# {"name": "alex", "data": "2018-05-18 19:52:05"}
76、json序列化时,默认遇到中文会转换成unicode,如果想要保留中文怎么办?
import json
a=json.dumps({"xxx":"你好"},ensure_ascii=False)
print(a) #{"xxx": "你好"}
77、什么是断言?应用场景?
#条件成立则继续往下,否则抛出异常;
#一般用于:满足某个条件之后,才能执行,否则应该抛出异常。
'应用场景':rest framework中GenericAPIView类里,要设置queryset,否则断言错误
78、有用过with statement吗?它的好处是什么?
with语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,
释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。
79、使用代码实现查看列举目录下的所有文件。
import os
path = os.listdir('.') #查看列举目录下的所有文件。
# path = os.listdir(os.getcwd())
print(path)
80、简述 yield和yield from关键字。
1、yield使用
1)函数中使用yield,可以使函数变成生成器。一个函数如果是生成一个数组,就必须把数据存储在内存中,如果使用生成器,则在调用的时候才生成数据,可以节省内存。
2)生成器方法调用时,不会立即执行。需要调用next()或者使用for循环来执行。
2、yield from的使用
1)为了让生成器(带yield函数),能简易的在其他函数中直接调用,就产生了yield from。
第二章 网络编程和并发
81. 简述 OSI 七层协议。
物理层:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0
数据链路层:定义了电信号的分组方式
网路层:引入一套新的地址用来区分不同的广播域/子网,这套地址即网络地址
传输层:建立端口到端口的通信
会话层:建立客户端与服务端连接
表示层:对来自应用层的命令和数据进行解释,按照一定格式传给会话层。如编码、数据格式转换、加密解密、压缩解压 应用层:规定应用程序的数据格式
82. 什么是C/S和B/S架构?
c/s架构,就是client(客户端)与server(服务端)即:客户端与服务端的架构。
b/s架构,就是brosver(浏览器端)与sever(服务端)即:浏览器端与服务端架构
优点:统一了所有应用程序的入口、方便、轻量级
83. 简述 三次握手、四次挥手的流程。
三次握手:
第一次握手
1:客户端先向服务端发起一次询问建立连接的请求,并随机生成一个值作为标识
第二次握手
2:服务端向客户端先回应第一个标识,再重新发一个确认标识
第三次握手
3:客户端确认标识,建立连接,开始传输数据
四次挥手 ---> 断开连接
第一次挥手
客户端向服务端发起请求断开连接的请求
第二次挥手
服务端向客户端确认请求
第三次挥手
服务端向客户端发起断开连接请求
第四次挥手
客户端向服务端确认断开请求
84. TCP和UDP的区别?
TCP/UDP区别
TCP协议是面向连接,保证高可靠性传输层协议
UDP:数据丢失,无秩序的传输层协议(qq基于udp协议)
85. 为何基于tcp协议的通信比基于udp协议的通信更可靠?
tcp:可靠,因为只要对方回了确认收到信息,才发下一个,如果没收到确认信息就重发
UDP:不可靠,它是一直发数据,不需要对方回应
流式协议: TCP协议,可靠传输
数据报协议: UDP协议,不可传输
86. 什么是socket?简述基于tcp协议的套接字通信流程。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,
对用户来说,一组简单的接口就是全部。
服务端:
创建socket对象,
绑定ip端口bind(),
设置最大链接数listen(),
accept()与客户端的connect()创建双向管道,等到联接,
send(), recv(), 收发数据
close()
客户端:
创建socket对象,
connect()与服务端accept()创建双向管道 ,
send(),
recv(),
close()
87. 什么是粘包? socket 中造成粘包的原因是什什么? 哪些情况会发生粘包现象?
只有TCP有粘包现象,UDP永远不会粘包
粘包:在获取数据时,出现数据的内容不是本应该接收的数据,如:对方第一次发送hello,第二次发送world,
我方接收时,应该收两次,一次是hello,一次是world,但事实上是一次收到helloworld,一次收到空,这种现象叫粘包
原因
粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
什么情况会发生:
1、发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
2、接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,
服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
88. IO多路复的作用?
socketserver,多个客户端连接,单线程下实现并发效果,就叫多路复用。
与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
89.select、poll、epoll 模型的区别?(属于多路复用IO的模型)
都是i/o多路复用的机制,监视多个socket是否发生变化,本质上都是同步i/o
select,poll实现需要自己不断轮询所有监测对象,直到对象发生变化,在这个阶段中,
可能要睡眠和唤醒多次交替,而epoll也需要调用epoll_wait不断轮询就绪链表,但是当对象发生变化时,
会调用回调函数,将变化的对象放入就绪链接表中,并唤醒在epoll_wait中进入睡眠的进程。
虽然都会睡眠和唤醒,但是select和poll在被唤醒的时候要遍历整个监测对象集合,
而epoll只要判断就绪链表是否为空即可,节省了大量cpu的时间
select、poll、epoll都是IO多路复用的机制,但select,poll,epoll本质上都是同步I/O,
因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的.
FD(文件描述符)
select模型
优点:
1:可移植性好,在某些Unix系统不支持poll()
2:对于超时值提供了更好的精度:微妙,而poll是毫秒
缺点:
1:最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Select 模型的最大并发数就被相应限制了。
2:效率问题,select每次调用都会线性扫描全部的FD集合,所以将FD_SETSIZE 改大,会越慢
3:需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
poll本质上和select 没有区别,它将用户传入的数组拷贝到内核空间,
它没有最大连接数的限制,原因是它基于链表来存储的但是同样有一个缺点:
大量的fd的数组被整体复制于用户态和内核地址空间,而不管这样的复制是不是有意义
90. 什么是防火墙以及作用?
防火墙是一个分离器、一个限制器,也是一个分析器,有效地监控了内部网和Internet之间的任何活动,保证了内部网络的安全
作用
防火墙是网络安全的屏障
可以强化网络安全策略
对网络存取和访问进行监控审计
防止内部信息的外泄
除了安全作用,防火墙还支持具有Internet服务特性的企业内部网络技术体系VPN(虚拟专用网)。
91. 简述 进程、线程、协程的区别 以及应用场景?
线程是指进程内的一个执行单元,
# 进程
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
# 线程
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度
# 协程和线程
协程避免了无意义的调度,由此可以提高性能;但同时协程也失去了线程使用多CPU的能力
进程与线程的区别
(1)地址空间:线程是进程内的一个执行单位,进程内至少有一个线程,他们共享进程的地址空间,而进程有自己独立的地址空间
(2)资源拥有:进程是资源分配和拥有的单位,同一个进程内线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是
(4)二者均可并发执行
(5)每个独立的线程有一个程序运行的入口
协程与线程
(1)一个线程可以有多个协程,一个进程也可以单独拥有多个协程,这样Python中则能使用多核CPU
(2)线程进程都是同步机制,而协程是异步
(3)协程能保留上一次调用时的状态
92. GIL锁是什么?
GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
GIL保护的是解释器级的数据,保护用户自己的数据则需要自己加锁处理
应用(总结):
多线程用于IO密集型,如socket,爬虫,web
多进程用于计算密集型,如金融分析
1. 每个cpython进程内都有一个GIL
2. GIL导致同一进程内多个进程同一时间只能有一个运行
3. 之所以有GIL,是因为Cpython的内存管理不是线程安全的
4. 对于计算密集型用多进程,多IO密集型用多线程
93. Python中如何使用线程池和进程池?
94. threading.local的作用?
实现线程局部变量的传递。
ThreadLocal 最常用的地方:
为每个线程绑定一个资源(数据库连接,HTTP请求,用户身份信息等),这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
95. 进程之间如何进行通信?
用消息队列 (Queue)
96. 什么是并发和并行?
# 并发:同一时刻只能处理一个任务,但一个时段内可以对多个任务进行交替处理(一个处理器同时处理多个任务)
# 并行:同一时刻可以处理多个任务(多个处理器或者是多核的处理器同时处理多个不同的任务)
# 类比:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
97. 进程锁和线程锁的作用?
保证数据安全
98. 解释什么是异步非阻塞?
非阻塞:不等待
即:遇到IO阻塞不等待(setblooking=False),(可能会报错->捕捉异常)
- sk=socket.socket()
- sk.setblooking(False)
异步:回调,当达到某个指定的状态之后,自动调用特定函数
实例
nb_async.py 实现异步非阻塞的模块
异步体现在回调上,回调就是有消息返回时告知一声儿进程进行处理。非阻塞就是不等待,不需要进程等待下去,继续执行其他操作,不管其他进程的状态。
99. 路由器和交换机的区别
1:交换机:是负责内网里面的数据传递(arp协议)根据MAC地址寻址
路由器:在网络层,路由器根据路由表,寻找该ip的网段
2:路由器可以处理TCP/IP协议
3:路由器可以把一个IP分配给很多个主机使用,这些主机对外只表现出一个IP。
交换机可以把很多主机连起来,这些主机对外各有各的IP。
4:交换机是做端口扩展的,也就是让局域网可以连进来更多的电脑。
路由器是用来做网络连接,也就是;连接不同的网络
100.什么是域名解析?
在互联网上,所有的地址都是ip地址,现阶段主要是IPv4(比如:110.110.110.110)。
但是这些ip地址太难记了,所以就出现了域名(比如http://baidu.com)。
域名解析就是将域名,转换为ip地址的这样一种行为。
101.如何修改本地hosts件?
Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,
当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,
一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。
浏览器访问网站,要首先通过DNS服务器把要访问的网站域名解析成一个唯一的IP地址,之后,浏览器才能对此网站进行定位并且访问其数据。
文件路径:C:\WINDOWS\system32\drivers\etc。
将127.0.0.1 www.163.com 添加在最下面
修改后用浏览器访问“www.163.com”会被解析到127.0.0.1,导致无法显示该网页。
102.生产者消费者模型应用场景及优势?
生产者与消费者模式是通过一个容器来解决生产者与消费者的强耦合关系,生产者与消费者之间不直接进行通讯,
而是利用阻塞队列来进行通讯,生产者生成数据后直接丢给阻塞队列,消费者需要数据则从阻塞队列获取,
实际应用中,生产者与消费者模式则主要解决生产者与消费者生产与消费的速率不一致的问题,达到平衡生产者与消费者的处理能力,而阻塞队列则相当于缓冲区。
应用场景:用户提交订单,订单进入引擎的阻塞队列中,由专门的线程从阻塞队列中获取数据并处理
优势:
1;解耦
假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。
将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
2:支持并发
生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只能一直等着
而使用这个模型,生产者把制造出来的数据只需要放在缓冲区即可,不需要等待消费者来取
3:支持忙闲不均
缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。
当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
103.什么是cdn?
目的是使用户可以就近到服务器取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
cdn 即内容分发网络
104.LVS是什么及作用?
LVS :Linux虚拟服务器
作用:LVS主要用于多服务器的负载均衡。
它工作在网络层,可以实现高性能,高可用的服务器集群技术。
它廉价,可把许多低性能的服务器组合在一起形成一个超级服务器。
它易用,配置非常简单,且有多种负载均衡的方法。
它稳定可靠,即使在集群的服务器中某台服务器无法正常工作,也不影响整体效果。另外可扩展性也非常好。
105.Nginx是什么及作用?
106.keepalived是什么及作用?
107.haproxy是什么以及作用?
108.什么是负载均衡?
109.什么是rpc及应用场景?
110.简述 asynio模块的作用和应用场景。
111.简述 gevent模块的作用和应用场景。
112.twisted框架的使用和应用
第六章 数据库和缓存
加强数据库的效率
i 缓存,已查过的放内存,没查过的才查
ii 索引,合理利用索引,甚至构造自己的索引表
ii 并行,可以多线程/多进程甚至分布式
iV 分离读写,使用数据仓库,专门面对查询
V 预处理,我先查,等你来问的时候,我只是把结果告诉你,能不快么?
最后一个方针:与其榨出每条查询语句的效率,不如从架构上总体设计,这才是正确的展开方式。
1.数据库
关系型:Oracle、DB2、Microsoft SQL Server、Microsoft Access、MySQL
非关系型:NoSql、Cloudant、MongoDb、redis、HBase
2.mysql常见数据库引擎及区别
innodb,支持事务以及外部键,行级锁,对于多删改的程序保持数据一致性有更好的性能
myisam,不支持事务,性能好,执行速度快,表级锁 查询和插入都非常快 不适合多删改 高并发 对事务一致性要求高的程序
memoory 内存级别的存储引擎 更多的用来做缓存
blackhole 用来做大量数据的分流的
3.事务以及特性
- 原子性(Atomicity)—要么全部成功,要么全部失败回滚
- 一致性(Consistency)----从一个一致性状态变换到另一个一致性状态
- 隔离性(Isolation)----当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰
- 持久性(Durability)----一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的
4.触发器,函数,视图的储存过程(不会)
- 触发器----insert,update,delete 时自动执行的代码块
- 函数----内置函数和自定义函数
- 视图:视图是由查询结果形成的一张虚拟表,是表通过某种运算得到的一个投影
5.mysql索引种类
普通索引INDEX:加速查找
唯一索引:
-主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)
-唯一索引UNIQUE:加速查找+约束(不能重复)
联合索引:
-PRIMARY KEY(id,name):联合主键索引
-UNIQUE(id,name):联合唯一索引
-INDEX(id,name):联合普通索引
6.索引在什么情况下遵循最左前缀的规则
联合索引
7.MySQL常见函数
concat() #字符串连接
contan_Ws() #利用指定的字符将多个字符串进行连接
format() # 数字格式化
lower()
upper()
left() #从左侧提取字符
right() #从右侧提取字符
length() #获取字符串长度
ltrin() #删除前导空格
rtrim() #删除后续空格
trim() #删除前导和后续空格
substring() #字符串截取
8.创建索引但是无法命中索引的情况
- 条件中有or
- 多列索引,不是使用第一部分,则不会使用索引
- like查询以%开头
- 如果类型是 字符串,要在条件中将数据使用引号引起来,否则不使用索引
9.数据库导入导出命令
导出
导出规则 | sql语句 |
---|---|
导出整个数据库(包括数据库中的数据 | mysqldump -u username -ppassword dbname > dbname.sql; |
导出数据库中的数据表(包括数据表中的数据) | mysqldump -u username -ppassword dbname tablename > tablename.sql; |
导出数据库结构(不包括数据,只有创建数据表语句) | mysqldump -u username -ppassword -d dbname > dbname.sql; |
导出数据库中数据表的表结构(不包括数据,只有创建数据表语句) | mysqldump -u username -ppassword -d dbname tablename > tablename.sql |
导入
导入规则 | sql语句 |
---|---|
导入数据到数据库 | mysql -uroot -D数据库名 |
导入数据到数据库中某个表 | mysql -uroot -D数据库名 表名 |
10,数据库优化方案
- 表的设计合理化(符合3NF)
- 添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
- 分表技术(水平分割、垂直分割)
- d: 读写[写: update/delete/add]分离
- 存储过程 [模块化编程,可以提高速度]
- 对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ]
- mysql服务器硬件升级
- 定时的去清除不需要的数据,定时进行碎片整理(MyISAM)
11,char和varchar的区别
char
定长,占用固定空间,查询效率高varchar
变长,可变空间,查询效率低
12,MySQL的执行计划的作用及使用方法
13,10000w条数据,使用limit offset分页时,为什么越往后越慢,如何解决
- 给出查询范围
SELECT c1,c2,cn... FROM table WHERE id>=20000 LIMIT 10;
- 给出查询范围
select name from talbe where id between 20000 and 3000 limit 10;
14,索引合并
# 索引合并就是使用mysql 的 index merge功能
# 简单理解索引合并
对多个索引分别进行条件扫描,然后将它们各自的结果进行合并(intersect/union)。
15,覆盖索引
覆盖索引的意思是说如果一个查询语句只需要从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。也就是说查询可以直接从索引中取得相应的值,不需要再通过主键索引去查询,这样的好处是显而易见的,由于减少了一次索引过渡,所以可以极大的增快查询速度。也就是平时所说的不需要回表操作
MySql中的常见的两个索引概念:辅助索引和聚集索引。
聚集索引:
聚集索引也就是我们说的主键索引,在聚集索引的叶子节点中存储的是数据页,也就是上面我们说的表数据的集合。而且要另外强调的是,在MySql中不同的索引类型对于索引类型的支持是不同的
辅助索引:
在辅助索引中,存储的是键值以及指向主键索引的书签。
我们在查询中,如果使用了辅助索引,MySql会首先查找到键值,然后根据书签找到主键索引,再从表中拿到数据,然后进行返回,所以辅助索引也被称为是二级索引。
16,数据库读写分离
基本原理就是往主库处理写操作(insert, update, delete), 从库处理读操作(select), 数据库的一致性通过主从复制来实现, 所以说主从复制是读写分离的基础
17,数据库分库分表,水平和垂直
18,数据库锁的作用
# 分为行级锁和表级锁
# 作用
保证数据的原子性,完整性,一致性。 只有加锁者释放了锁,别人才能改变数据
# 缺点
增加了系统开销,有可能产生锁等待,造成数据库运行异常。这都是不正常的使用锁带来的问题。
19.简述项目中优化sql语句执行效率的方法
# 缩小查询的范围
1. 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
2. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null, 最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.
3. 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
4. 应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
5. in 和 not in 也要慎用,否则会导致全表扫描
20. mysql 半同步复制原理
异步复制(Asynchronous replication)
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果crash掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
全同步复制(Fully synchronous replication)
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
半同步复制(Semisynchronous replication)
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
21.sql查询
存在的表有:
- products(商品表) columns为id,nane, price
- orders(商城订单表) columns为id, reservation_id, product id,quentity(购买数量)
- reservations(酒店订单表) columns为id,user_id, price, created
需要查询的
1.各个商品的售卖情况,需要字段商品名购买总量商品收入
2.所有用户在2018-01-01至2018-82-01下单次数,下单金额,商城下单次
数,商城下单金额
3.历月下单用户数:下单一次用户数,下单两次用户数,下单三次及以上用户
数
22.考虑如下表结构,写出建表语句
id (自增主键) | name(非空) | Balance(非空) |
---|---|---|
1 | A | 19 |
2 | A | 20 |
3 | A | 100 |
23.假设学生 Students和教师 Teachers关系模型如下所示:
- Student:(学号,姓名,性别,类别,身份证号)
- Teacher:(教师号,姓名,性别,身份证号,工资)
其中,学生关系中的类别分别为”本科生”和”研究生两类”,性别分为”男”和”女”
两类
查询研究生教师平均工资(显示为平均工资),最高工资与最低工资之间的差值
(显示为差值)的SQL语句
select(1)as平均工资,(2)as差值 from Students, Teacher where(3);
查询工资少于10900元的女研究生教师的身份证号和姓名的50L语句(非激套查
面方式);
select 身份证号,姓名 from Students where (4) (5)
select 身份证号,姓名 from Teachers where (6)
24. mysql怎么创建索引
# 创建普通索引
create index 索引名 on 表名(列名);
# 创建主键索引
在MySQL中主键字段默认就有主键索引
# 创建唯一索引
create unique index 索引名 on 表名(列名);
# 创建联合索引
create index 索引名 on 表名(列名1,列名2);
# 查看已经创建的索引
show index from 表名;
25. 请简述sql注入的攻击原理以及如何在代码层面防止sql注入
# 原理
通过前端的表单提交的数据中携带sql语句, 欺骗服务器, 在后端对数据进行存储时, 执行恶意的sql语句
# 出现在哪里
比如在使用pymysql操作数据库是使用字符串拼接生产sql语句, 就会出现sql注入漏洞
# 如何防止
1. 使用pymysql时, 不要使用字符串拼接, 而是使用execute方法, pymysql模块已经对提交的数据做了处理, 不会出现sql注入
2. 不要相信前端提交的任何数据, 要严格校验
3. 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
4. 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息
5. 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装
6. 使用辅助软件对网站进行检测, 软件一般采用sql注入检测工具jsky, 网站平台就有亿思网站安全平台检测
26. 使用python实现将数据库的student表中的数据提取并写入db.txt
import json
import pymysql
conn = pymysql.connect(host="39.106.15.4", user='root', password="594504110",
database="owenr", port=3306, cursorclass=pymysql.cursors.DictCursor)
cursor = conn.cursor()
cursor.execute('select * from student')
res = cursor.fetchall()
f = open('db.txt', 'w', encoding='utf-8')
json.dump(res, fp=f, ensure_ascii=False)
cursor.close()
conn.close()
27. 简述left join和right join的区别
# left join
左连表, 以左表为基础, 显示所有和左表匹配的行数据
# right join
有连表, 以右表为基础, 显示所有和右表匹配的行数据
28. 索引是有什么作用 有哪些分类, 有什么好处和坏处?
# 作用:
数据库索引是为了增加查询速度
# 分类
1. 普通索引
2. 唯一索引
3. 主键索引
4. 联合索引
# 好处
增加查询速度
# 坏处
索引也是一种数据结构, 建立索引会占用额外的存储空间
29.写sql语句
TableA
id | name | kechen | fenshu |
---|---|---|---|
1 | 张三 | 语文 | 81 |
2 | 张三 | 数学 | 75 |
3 | 李四 | 语文 | 76 |
4 | 李四 | 数学 | 90 |
5 | 王五 | 语文 | 81 |
6 | 王五 | 数学 | 100 |
7 | 王五 | 英语 | 90 |
TableB
id | name |
---|---|
1 | 张三 |
2 | 李四 |
3 | 王五 |
4 | 赵六 |
查询:
1.查询出每门课程都大于86分的学生姓名
2.查询出语文成绩醉的大学生姓名
3.查询没有成绩的学生姓名
30.试列出至少三种目前流行的大型关系型数据库的名称
试列出至少三种目前流行的大型关系型数据库的名称
其中您最熟悉的是
什么时候开始使用
31.数据库查询
有表List,共有字段a,b,c,类型都是整数,表中有如下几条记录
1 | a | b | c |
---|---|---|---|
2 | 2 | 7 | 9 |
3 | 5 | 6 | 4 |
4 | 3 | 11 | 9 |
现在对该表依次完成一下操作
1.查询出b和c列的值,要求按b列的升序排列
2.写入一条新的记录,值为(7,9,8)
3.查询c列,要求消除重复的值,按降序排列
32.用一条sql语句查询出每门课程都大于80分的学生姓名表 score如下
name | kecheng | fenshu |
---|---|---|
张三 | 语文 | 81 |
张三 | 数学 | 75 |
李四 | 语文 | 76 |
33.设计表关系如下:教师班级学生,科室科室与教师为一对多关系,教师与班级为多对多关系,班级与学生为一对多关系,科室中需体现层级关系
1,写出各张表的逻辑字段
2,根据上述关系表
-
查询教师id=1的学生数
-
查询科室id=3的下级部门数
-
查询所带学生最多的教师的1d
34.有Staff表,字段为主键Sid姓名 Sname性别Sex(值为男”或”女")课程表Course,字段为主键Cid课程名称 Cname,关系表 SC_ Relation,字段为 Student
表主键Sd和 Course表主键Cid组成联合主键请用SQL查询语句写出查
询所有选"计算机"课程的男士的姓名
35.什么是 MySQL慢日志
记录在MySQL中响应时间超过阀值的语句,比如运行超过10s的 sql语句
下面是如何开启
修改配置文件
slow_query_log = OFF #是否开启慢日志记录
long_query_time = 2 #时间限制,超过此时间,则记录
slow_query_log_file = /usr/slow.log #日志文件
log_queries_not_using_indexes = OFF #为使用索引的搜索是否记录
下面是开启
slow_query_log = ON
long_query_time = 2
log_queries_not_using_indexes = OFF
log_queries_not_using_indexes = ON
注:查看当前配置信息:
show variables like '%query%'
修改当前配置:
set global 变量名 = 值
36.数据库题(要求写出逻辑字段)
- 写出建表语句完成如下操作,列名自由定义(id自增):
新建A,B,C三张表
A 表自关联:
A、B表为1:n表:
B、C表为m:n表 - 写出插入语句完成如下操作
在C表中插入记录c1,并使其关联B表中id为1和2的两条记录 - 写出删除语句完成如下操作:
删除A表中的记录a1(id=1),并级联删除A、B、C表中的其他相关记录 - 写出查询语句完成如下操作(3条SQ)
A表中存在记录a2(id=2),分别查询A、B、C表中与a2相关默的记录数
37.在对name做了唯一索引前提下,简述以下区别:
select * fron tb where name 'oldboy-wupeiqi'
select * from tb where nane ='o1dbay-wupeiqi' limit 1
是这样的的,用where条件过滤出符合条件的数据的同时,进行计数,
比如limit 1,那么在where过滤出第1条数据后,他就会直接把结果select出来返回给你,整个过程就结束了。
38.redis和 memcached的区别?
观点一:
1、Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等;
2、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储;
3、虚拟内存–Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘;
4、过期策略–memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10;
5、分布式–设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从;
6、存储数据安全–memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化);
7、灾难恢复–memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复;
8、Redis支持数据的备份,即master-slave模式的数据备份;
39.如何高效的找到 redis中所有以 oldboy开头的key
redis 有一个keys命令。
# 语法:KEYS pattern
# 说明:返回与指定模式相匹配的所用的keys。
该命令所支持的匹配模式如下:
1、?:用于匹配单个字符。例如,h?llo可以匹配hello、hallo和hxllo等;
2、*:用于匹配零个或者多个字符。例如,h*llo可以匹配hllo和heeeello等;
2、[]:可以用来指定模式的选择区间。例如h[ae]llo可以匹配hello和hallo,但是不能匹配hillo。同时,可以使用“/”符号来转义特殊的字符
# 注意
KEYS 的速度非常快,但如果数据太大,内存可能会崩掉,
如果需要从一个数据集中查找特定的key,最好还是用Redis的集合结构(set)来代替。
40.什么是一致性哈希?
一致性哈希
一致性hash算法(DHT)可以通过减少影响范围的方式,解决增减服务器导致的数据散列问题,从而解决了分布式环境下负载均衡问题;
如果存在热点数据,可以通过增添节点的方式,对热点区间进行划分,将压力分配至其他服务器,重新达到负载均衡的状态。
Python模块--hash_ring,即Python中的一致性hash
41.redis是单进程单线程的吗?
redis内部是单进程、单线程,是数据安全的
42.redis中数据库默认是多少个db及作用?
Redis默认支持16个数据库,可以通过配置databases来修改这一数字。客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库
Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。
43.如果redis中的某个列表中的数据量非常大,如果实现循环显示每一个值?
# 如果一个列表在redis中保存了10w个值,我需要将所有值全部循环并显示,请问如何实现?
# 一个一个取值,列表没有iter方法,但能自定义
def list_scan_iter(name,count=3):
start = 0
while True:
result = conn.lrange(name, start, start+count-1) # Lrange 返回列表中指定区间内的元素
start += count
if not result:
break
for item in result:
yield item
for val in list_scan_iter('num_list'):
print(val)
# 场景:投票系统,script-redis
44.redis如何实现主从复制?以及数据同步机制?
优势:
- 高可用
- 分担主压力
注意:
- slave设置只读
从的配置文件添加以下记录,即可:
`slaveof 1.1.1.1 3306`
45 redis中的 sentinel的作用?
帮助我们自动在主从之间进行切换
检测主从中 主是否挂掉,且超过一半的sentinel检测到挂了之后才进行进行切换。
如果主修复好了,再次启动时候,会变成从。
启动主redis:
redis-server /etc/redis-6379.conf 启动主redis
redis-server /etc/redis-6380.conf 启动从redis
在linux中:
找到 /etc/redis-sentinel-8001.conf 配置文件,在内部:
- 哨兵的端口 port = 8001
- 主redis的IP,哨兵个数的一半/1
找到 /etc/redis-sentinel-8002.conf 配置文件,在内部:
- 哨兵的端口 port = 8002
- 主redis的IP, 1
启动两个哨兵
46.如何实现 redis集群
redis集群、分片、分布式redis
redis-py-cluster
集群方案:
- redis cluster 官方提供的集群方案。
- codis,豌豆荚技术团队。
- tweproxy,Twiter技术团队。
redis cluster的原理?
- 基于分片来完成。
- redis将所有能放置数据的地方创建了 16384 个哈希槽。
- 如果设置集群的话,就可以为每个实例分配哈希槽:
- 192.168.1.20【0-5000】
- 192.168.1.21【5001-10000】
- 192.168.1.22【10001-16384】
- 以后想要在redis中写值时,
set k1 123
将k1通过crc16的算法,将k1转换成一个数字。然后再将该数字和16384求余,如果得到的余数 3000,那么就将该值写入到 192.168.1.20 实例中。
47.redis中默认有多少个哈希槽?
1384
48.简述 redis的有哪几种持久化策略及比较?
RDB:每隔一段时间对redis进行一次持久化。
- 缺点:数据不完整
- 优点:速度快
AOF:把所有命令保存起来,如果想到重新生成到redis,那么就要把命令重新执行一次。
- 缺点:速度慢,文件比较大
- 优点:数据完整
49.列举redis支持的过期策略
voltile-lru: 从已设置过期时间的数据集(server.db[i].expires)中挑选最近频率最少数据淘汰
volatile-ttl: 从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru: 从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random: 从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
50.MySQL里有2000w数据, redis中只存20w的数据,如何保证 redis中都是热点数据?
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
51.写代码,基于 redis的列表实现先进先出、后进先出队列、优先级队列。
# 参看script—redis源码
from scrapy.utils.reqser import request_to_dict, request_from_dict
from . import picklecompat
class Base(object):
"""Per-spider base queue class"""
def __init__(self, server, spider, key, serializer=None):
"""Initialize per-spider redis queue.
Parameters
----------
server : StrictRedis
Redis client instance.
spider : Spider
Scrapy spider instance.
key: str
Redis key where to put and get messages.
serializer : object
Serializer object with ``loads`` and ``dumps`` methods.
"""
if serializer is None:
# Backward compatibility.
# TODO: deprecate pickle.
serializer = picklecompat
if not hasattr(serializer, 'loads'):
raise TypeError("serializer does not implement 'loads' function: %r"
% serializer)
if not hasattr(serializer, 'dumps'):
raise TypeError("serializer '%s' does not implement 'dumps' function: %r"
% serializer)
self.server = server
self.spider = spider
self.key = key % {'spider': spider.name}
self.serializer = serializer
def _encode_request(self, request):
"""Encode a request object"""
obj = request_to_dict(request, self.spider)
return self.serializer.dumps(obj)
def _decode_request(self, encoded_request):
"""Decode an request previously encoded"""
obj = self.serializer.loads(encoded_request)
return request_from_dict(obj, self.spider)
def __len__(self):
"""Return the length of the queue"""
raise NotImplementedError
def push(self, request):
"""Push a request"""
raise NotImplementedError
def pop(self, timeout=0):
"""Pop a request"""
raise NotImplementedError
def clear(self):
"""Clear queue/stack"""
self.server.delete(self.key)
class FifoQueue(Base):
"""Per-spider FIFO queue"""
def __len__(self):
"""Return the length of the queue"""
return self.server.llen(self.key)
def push(self, request):
"""Push a request"""
self.server.lpush(self.key, self._encode_request(request))
def pop(self, timeout=0):
"""Pop a request"""
if timeout > 0:
data = self.server.brpop(self.key, timeout)
if isinstance(data, tuple):
data = data[1]
else:
data = self.server.rpop(self.key)
if data:
return self._decode_request(data)
class PriorityQueue(Base):
"""Per-spider priority queue abstraction using redis' sorted set"""
def __len__(self):
"""Return the length of the queue"""
return self.server.zcard(self.key)
def push(self, request):
"""Push a request"""
data = self._encode_request(request)
score = -request.priority
# We don't use zadd method as the order of arguments change depending on
# whether the class is Redis or StrictRedis, and the option of using
# kwargs only accepts strings, not bytes.
self.server.execute_command('ZADD', self.key, score, data)
def pop(self, timeout=0):
"""
Pop a request
timeout not support in this queue class
"""
# use atomic range/remove using multi/exec
pipe = self.server.pipeline()
pipe.multi()
pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0)
results, count = pipe.execute()
if results:
return self._decode_request(results[0])
class LifoQueue(Base):
"""Per-spider LIFO queue."""
def __len__(self):
"""Return the length of the stack"""
return self.server.llen(self.key)
def push(self, request):
"""Push a request"""
self.server.lpush(self.key, self._encode_request(request))
def pop(self, timeout=0):
"""Pop a request"""
if timeout > 0:
data = self.server.blpop(self.key, timeout)
if isinstance(data, tuple):
data = data[1]
else:
data = self.server.lpop(self.key)
if data:
return self._decode_request(data)
# TODO: Deprecate the use of these names.
SpiderQueue = FifoQueue
SpiderStack = LifoQueue
SpiderPriorityQueue = PriorityQueue
52.如何基于 redis实现消息队列?
# 通过发布订阅模式的PUB、SUB实现消息队列
# 发布者发布消息到频道了,频道就是一个消息队列。
# 发布者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
conn.publish('104.9MH', "hahahahahaha")
# 订阅者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
pub = conn.pubsub()
pub.subscribe('104.9MH')
while True:
msg= pub.parse_response()
print(msg)
#对了,redis 做消息队列不合适
#业务上避免过度复用一个redis,用它做缓存、做计算,还做任务队列,压力太大,不好。
53.如何基于 redis实现发布和订阅?
#发布和订阅,只要有任务就给所有订阅者没人一份
#发布者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
conn.publish('104.9MH', "hahaha")
#订阅者:
import redis
conn = redis.Redis(host='127.0.0.1',port=6379)
pub = conn.pubsub()
pub.subscribe('104.9MH')
while True:
msg= pub.parse_response()
print(msg)
54.什么是 codis?
Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别
(不支持的命令列表), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作,
所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务.
55.什么是 twemproxy
是 Twtter 开源的一个 Redis 和 Memcache 代理服务器,主要用于管理 Redis 和 Memcached 集群,
减少与Cache 服务器直接连接的数量。
56.redis如何实现事务。
import redis
pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
conn = redis.Redis(connection_pool=pool)
# pipe = r.pipeline(transaction=False)
pipe = conn.pipeline(transaction=True)
# 开始事务
pipe.multi()
pipe.set('name', 'bendere')
pipe.set('role', 'sb')
# 提交
pipe.execute()
#注意:咨询是否当前分布式redis是否支持事务
57.redis中的 watch的命令的作用?
# 在Redis的事务中,WATCH命令可用于提供CAS(check - and -set)
# 功能。
# 假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,
# EXEC命令执行的事务都将被放弃,同时返回Null
# multi - bulk应答以通知调用者事务执行失败。
#
# 面试题:你如何控制剩余的数量不会出问题?
# 方式一:- 通过redis的watch实现
import redis
conn = redis.Redis(host='127.0.0.1', port=6379)
# conn.set('count',1000)
val = conn.get('count')
print(val)
with conn.pipeline(transaction=True) as pipe:
# 先监视,自己的值没有被修改过
conn.watch('count')
# 事务开始
pipe.multi()
old_count = conn.get('count')
count = int(old_count)
print('现在剩余的商品有:%s', count)
input("问媳妇让不让买?")
pipe.set('count', count - 1)
# 执行,把所有命令一次性推送过去
pipe.execute()
#方式二 - 数据库的锁
58.简述reds分布式锁和 medlock的实现机制。
'''
在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段。
有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大
,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性。
用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock。
实现
- 写值并设置超时时间
- 超过一半的redis实例设置成功,就表示加锁完成。
- 使用:安装redlock-py
'''
from redlock import Redlock
dlm = Redlock(
[
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
]
)
# 加锁,acquire
my_lock = dlm.lock("my_resource_name",10000)
if my_lock:
# J进行操作
# 解锁,release
dlm.unlock(my_lock)
else:
print('获取锁失败')
# redis分布式锁?
# 不是单机操作,又多了一/多台机器
# redis内部是单进程、单线程,是数据安全的(只有自己的线程在操作数据)
'''A、B、C,三个实例(主)
1、来了一个'隔壁老王'要操作,且不想让别人操作,so,加锁;
加锁:'隔壁老王'自己生成一个随机字符串,设置到A、B、C里(xxx=666)
2、来了一个'邻居老李'要操作A、B、C,一读发现里面有字符串,擦,被加锁了,不能操作了,等着吧~
3、'隔壁老王'解决完问题,不用锁了,把A、B、C里的key:'xxx'删掉;完成解锁
4、'邻居老李'现在可以访问,可以加锁了
# 问题:
1、如果'隔壁老王'加锁后突然挂了,就没人解锁,就死锁了,其他人干看着没法用咋办?
2、如果'隔壁老王'去给A、B、C加锁的过程中,刚加到A,'邻居老李'就去操作C了,加锁成功or失败?
3、如果'隔壁老王'去给A、B、C加锁时,C突然挂了,这次加锁是成功还是失败?
4、如果'隔壁老王'去给A、B、C加锁时,超时时间为5秒,加一个锁耗时3秒,此次加锁能成功吗?
# 解决
1、安全起见,让'隔壁老王'加锁时设置超时时间,超时的话就会自动解锁(删除key:'xxx')
2、加锁程度达到(1/2)+1个就表示加锁成功,即使没有给全部实例加锁;
3、加锁程度达到(1/2)+1个就表示加锁成功,即使没有给全部实例加锁;
4、不能成功,锁还没加完就过期,没有意义了,应该合理设置过期时间
# 注意
使用需要安装redlock-py'''
from redlock import Redlock
dlm = Redlock(
[
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
{"host": "localhost", "port": 6379, "db": 0},
]
)
# 加锁,acquire
my_lock = dlm.lock("my_resource_name",10000)
if my_lock:
# 进行操作
# 解锁,release
dlm.unlock(my_lock)
else:
print('获取锁失败')
# \通过sever.eval(self.unlock_script)执行一个lua脚本,用来删除加锁时的key
59.请设计一个商城商品计数器的实现方案?
基于redis
import redis
conn = redis.Redis(host='192.168.1.41',port=6379)
conn.set('count',1000)
with conn.pipeline() as pipe:
# 先监视,自己的值没有被修改过
conn.watch('count')
# 事务开始
pipe.multi()
old_count = conn.get('count')
count = int(old_count)
if count > 0: # 有库存
pipe.set('count', count - 1)
# 执行,把所有命令一次性推送过去
pipe.execute()
60.了解过 Hbase、DB2、 SQLServer、 Access吗?
关系型数据库:mysql,db2,sqlserver,hbase,access
61. 常用sql
where 字段 is null -- 判断一个字段是都为null
-- 内置函数
-- max 最大值, min 最小值, avg 平均值, sum 求和, count 计数
-- 关键字的使用顺序
-- select > from > where > group by > having > distinct > order by > limit
-- 排序
select * from 表 order by 字段1 desc,字段2 asc;
-- 去重
select distinct name from 表;
62. 试写出至少三种目前流行的大型关系型数据库
# 大型数据库
mysql Oracle, SQLserver db2
# 最熟悉的
mysql
第八章 django
1. 简述http协议和常用请求头
http协议是超文本传输协议
常用请求头:
协议头 | 说明 |
---|---|
Accept | 可接受的响应内容类型 |
Accept-Charset | 可接受的字符集 |
Accept-Encoding | 可接受的响应内容的编码方式。 |
Accept-Language | 可接受的响应内容语言列表。 |
Accept-Datetime | 可接受的按照时间来表示的响应内容版本 |
Authorization | 用于表示HTTP协议中需要认证资源的认证信息 |
Cache-Control | 用来指定当前的请求/回复中的,是否使用缓存机制。 |
Connection | 客户端(浏览器)想要优先使用的连接类型 |
Cookie | 由之前服务器通过Set-Cookie (见下文)设置的一个HTTP协议Cookie |
Content-Length | 以8进制表示的请求体的长度 |
Content-MD5 | 请求体的内容的二进制 MD5 散列值(数字签名),以 Base64 编码的结果 |
Content-Type | 请求体的MIME类型 (用于POST和PUT请求中) |
Date | 发送该消息的日期和时间(以RFC 7231中定义的"HTTP日期"格式来发送) |
Expect | 表示客户端要求服务器做出特定的行为 |
From | 发起此请求的用户的邮件地址 |
Host | 表示服务器的域名以及服务器所监听的端口号。如果所请求的端口是对应的服务的标准端口(80),则端口号可以省略。 |
If-Match | 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要用于像 PUT 这样的方法中,仅当从用户上次更新某个资源后,该资源未被修改的情况下,才更新该资源。 |
If-Modified-Since | 允许在对应的资源未被修改的情况下返回304未修改 |
If-None-Match | 允许在对应的内容未被修改的情况下返回304未修改( 304 Not Modified ),参考 超文本传输协议 的实体标记 |
If-Range | 如果该实体未被修改过,则向返回所缺少的那一个或多个部分。否则,返回整个新的实体 |
If-Unmodified-Since | 仅当该实体自某个特定时间以来未被修改的情况下,才发送回应。 |
Max-Forwards | 限制该消息可被代理及网关转发的次数。 |
Origin | 发起一个针对跨域资源共享的请求(该请求要求服务器在响应中加入一个Access-Control-Allow-Origin 的消息头,表示访问控制所允许的来源)。 |
Pragma | 与具体的实现相关,这些字段可能在请求/回应链中的任何时候产生。 |
Proxy-Authorization | 用于向代理进行认证的认证信息。 |
Range | 表示请求某个实体的一部分,字节偏移以0开始。 |
Referer | 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面。Referer 其实是Referrer 这个单词,但RFC制作标准时给拼错了,后来也就将错就错使用Referer 了。 |
TE | 浏览器预期接受的传输时的编码方式:可使用回应协议头Transfer-Encoding 中的值(还可以使用"trailers"表示数据传输时的分块方式)用来表示浏览器希望在最后一个大小为0的块之后还接收到一些额外的字段。 |
User-Agent | 浏览器的身份标识字符串 |
Upgrade | 要求服务器升级到一个高版本协议。 |
Via | 告诉服务器,这个请求是由哪些代理发出的。 |
Warning | 一个一般性的警告,表示在实体内容体中可能存在错误。 |
2.常用请求方法
方法 | 说明 | 支持的HTTP协议版本 |
---|---|---|
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1.1 |
DELETE | 获得报文首部 | 1.0、1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
3. 常见状态码
状态码 | 说明 |
---|---|
200 | 请求成功 |
201 | 请求完成,结果是创建了新资源 |
202 | 请求被接受,但处理尚未完成 |
204 | 服务器端已经实现了请求,但是没有返回新的信息 |
301 | 永久重定向 |
302 | 重定向 |
304 | 资源未更新 |
400 | 非法请求 |
401 | 未授权 |
403 | 禁止 |
404 | 没找到 |
500 | 服务器内部错误 |
501 | 服务器无法识别 |
502 | 错误网关 |
503 | 服务出错 |
4.http和https区别
http:
- 超文本传输协议,明文传输
- 80端口
- 连接简单且是无状态的
https:
- 需要到ca申请证书,要费用
- 具有安全性的ssl加密传输协议
- 端口是443
- https协议是有ssl+http协议构建的可进行加密传输,身份认证的网络协议,安全
5.websocket协议以及原理
- WebSocket*是一种在单个TCP连接上进行全双工通信的协议
- 原理不知道,太复杂,不想记
6.django 如何实现websocket
两种方式:
- django-channel点击跳转 ---- django官方推荐
- dwebsocket ---- 简单方便
7.python web开发中跨域问题的解决思路
线上环境不存在跨域问题,nginx转发
解决思路:
1.什么是跨域
在浏览器窗口中,和某个服务端通过某个 “协议+域名+端口号” 建立了会话的前提下,去使用与这三个属性任意一个不同的源提交了请求,那么浏览器就认为你是跨域了违反了浏览器的同源策略
2.如何解决:3种方法
方法1:安装django-cors-headers
-
下载django-cors-header
pip install django-cors-header
-
配置settings.py文件
INSTALLED_APPS = [ ... 'corsheaders', ... ] MIDDLEWARE_CLASSES = ( ... 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # 注意顺序 ... ) #跨域增加忽略 CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = ( '*' ) CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'XMLHttpRequest', 'X_FILENAME', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'Pragma', )
方法2:使用JSONP
使用Ajax获取json数据时,存在跨域的限制。不过,在Web页面上调用js的script脚本文件时却不受跨域的影响,JSONP就是利用这个来实现跨域的传输。因此,我们需要将Ajax调用中的dataType从JSON改为JSONP(相应的API也需要支持JSONP)格式。jsonp只能用于get请求
方案3.直接修改Django中的views.py文件,原理是修改请求头
#修改views.py中对应API的实现函数,允许其他域通过Ajax请求数据:
def myview(_request):
response = HttpResponse(json.dumps({“key”: “value”, “key2”: “value”}))
response[“Access-Control-Allow-Origin”] = “*”
response[“Access-Control-Allow-Methods”] = “POST, GET, OPTIONS”
response[“Access-Control-Max-Age”] = “1000”
response[“Access-Control-Allow-Headers”] = “*”
return response
8.简述http缓存机制
- 强制缓存,服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略。
- 比较缓存,将缓存信息中的Etag和Last-Modified通过请求发送给服务器,由服务器校验,返回304状态码时,浏览器直接使用缓存。
9.例举python web框架
django flask tornado sanic web2py
10.http和https的区别
见第四题
11.django,flask,tornado框架的比较
django | flask | tornado | |
---|---|---|---|
socket | 无 | 无 | |
中间件 | 无 | ||
路由系统 | |||
视图 | |||
模板引擎 | 有,uimethod,uimodule | ||
模板语言 | |||
cookie | |||
session | 有,但使用的是加密cookie,保存在客户端 | 无 | |
csrf | |||
xss | |||
缓存 | 无 | ||
信号 | 无,有扩展插件WTForms | 无 | |
Form | 无 | 无 | |
Admin | 无 | 无 | |
ORM | 无 | 无 |
12.什么是Wsgi
WSGI是Web Server Gateway Interface的缩写,Python web服务器网关接口,实际上就是一种协议
工作流程:HTTP 客户端 — web 服务器 — Wsgi — Flask
作用:
- 让 web 服务器知道如何调用 web 应用,传递用户的请求给应用
- 让应用知道用户的请求内容,以及如何返回消息给 web 服务器
13.Django内置组件
- 分页器
- forms组件
- model form
- 缓存机制
- 信号
- 序列化组件
- 中间件
- ContentTypes
- 用户认证组件—auth模块
14.django内建的缓存机制
正常情况下访问流程:
接收请求 -> url路由 -> 视图处理 -> 数据库读写 -> 视图处理 -> 模版渲染 -> 返回请求
django内建缓存机制:
缓存的思路是,既然已经处理过一次,得到了结果,就把当前结果缓存下来。下次再请求时,把缓存的处理结果直接返回。这样,可以极大地减少重复工作,降低数据库负载
下面是缓存思路的伪代码:
给定一个URL, 试图在缓存中查询对应的页面
如果缓存中有该页面:
返回这个缓存的页面
否则:
生成页面
将生成的页面保存到缓存中(用作以后)
返回这个生成的页面
15. django中model的SlugField类型字段有什么用途
SlugField字段是将输入的内容中的空格都替换成‘-’之后保存
17.Django常见的线上部署方式
Nginx+uwsgi
- nginx作为服务器最前端,负责接收client的所有请求,统一管理。静态请求由Nginx自己处理。
- 非静态请求通过uwsgi传递给Django,由Django来进行处理,从而完成一次WEB请求
18.django对数据查询结果排序怎么做,降序怎么做
user = Users.objects.order_by(‘id’)
user = Users.objects.order_by(‘id’)[0:1]
# 如果需要逆序 在字段前加负号 例 (‘-id’)
20.django中使用memcached作为缓存的具体方法,优缺点说明
**优点:**Memcached是Django原生支持的缓存系统,速度快,效率高
**缺点:**基于内存的缓存系统有个明显的缺点就是断电数据丢失
21.django中orm如何查询id不等于5 的元素
User.objects.filter().exclude(id=5) # 查询id不为5的用户
22.把sql语句转化成python代码
select * from company where title like "%abc%" or mecount>999 order by createtime desc;
python orm操作代码
objects.filter(Q(title__contains='abc')|Q(mecount__gh=000)).order_by(-createtime)
23.从输入http://www.baidu.com到页面返回,中间都是发生了什么?
- 浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;
- 解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接;
- 浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器;
- 服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器;
- 释放 TCP连接;
- 浏览器将该 html 文本并显示内容;
24.django请求的生命周期
wsgi—中间件—路由----视图—中间件—wsgi
25.django中如何在 model保存前做一定的固定操作,比如写一句日志?
用django信号实现
from django.shortcuts import HttpResponse
import time
import django.dispatch
from django.dispatch import receiver
# 定义一个信号
work_done = django.dispatch.Signal(providing_args=['path', 'time'])
def create_signal(request):
url_path = request.path
print("我已经做完了工作。现在我发送一个信号出去,给那些指定的接收器。")
# 发送信号,将请求的url地址和时间一并传递过去
work_done.send(create_signal, path=url_path, time=time.strftime("%Y-%m-%d %H:%M:%S"))
return HttpResponse("200,ok")
#接收信号
@receiver(work_done, sender=create_signal)
def my_callback(sender, **kwargs):
print("我在%s时间收到来自%s的信号,请求url为%s" % (kwargs['time'], sender, kwargs["path"]))
26.简述 django中间件及其应用场景?
- Django项目中默认启用了csrf保护,每次请求时通过CSRF中间件检查请求中是否有正确token值
- 当用户在页面上发送请求时,通过自定义的认证中间件,判断用户是否已经登陆,未登陆就去登陆。
- 当有用户请求过来时,判断用户是否在白名单或者在黑名单里
27.简述 django FBV和CBV?
- FBV—function base view 函数处理请求
- CBV—class base view 类处理请求
28.如何给 django CBV的函数设置添加装饰器?
29.django如何连接多个数据库并实现读写分离?
-
手动
model.object.using(‘指定数据库’).all()
-
自动
创建一个py文件,创建一个类,实现以下两个方法,函数名固定写法
class Router: def db_for_read(): # 读操作 return 'slave' def db_for_write(): #写操作 return 'default'
30.列举 django orm中你了解的所有方法?
- 返回QuerySet对象
- all
- filter
- exclude
- order_by
- reverse
- distinct—去重
- values
- values_list
- 返回对象
- get
- first
- last
- 返回数字
- count
- 返回布尔值
- exists
ORM连表查询 – 正向查询和反向查询
神奇的双下划线
多对多的关系的三种方式
聚合和分组
F查询和Q查询
事务
from django.db import transaction
with transaction.atomic():
# 事务操作
ORM行级锁
select_for_update()
执行原声SQL语句的方式
.extra()
.raw()
性能优化
bulk_create
select_related
/prefetch_related
如何在单独的脚本里面执行ORM操作
only和defer
31.django中的F的作用?
操作数据表中的某列值
例(单个更新):
# 普通方式
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed += 1 # 放到内存中,使用python计算,然后通过save方法保存
reporter.save()
# 使用F表达式 (简单使用)
from django.db.models import F
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
虽然看上去和上面的内存Python操作相似,但事实上这是一个描述数据库操作的sql概念
当django遇到F()实例,它覆盖了标准的Python运算符创建一个封装的SQL表达式。在这个例子中,reporter.stories_filed就代表了一个指示数据库对该字段进行增量的命令。
无论reporter.stories_filed的值是或曾是什么,Python一无所知--这完全是由数据库去处理的。所有的Python,通过Django的F() 类,只是去创建SQL语法参考字段和描述操作
例(多个更新):
Reporter.objects.all().update(stories_filed=F('stories_filed') + 1)
32.django中的Q的作用?
对对象进行复杂查询,并支持&(and),|(or),~(not)操作符
例:
from django.db.models import Q
from login.models import New #models对象
news=New.objects.filter(Q(question__startswith='What')) #例1
news=New.objects.filter(Q(question__startswith='Who') | Q(question__startswith='What')) #例2
news=New.objects.filter(Q(question__startswith='Who') | ~Q(pub_date__year=2005)) #例3 ~表示取反
news=Poll.objects.get(Q(question__startswith='Who'),Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))) #例4
33.django中如何执行原生SQL?
在Django中使用原生Sql主要有以下几种方式:
- extra:结果集修改器,一种提供额外查询参数的机制(依赖model)
- raw:执行原始sql并返回模型实例(依赖model)
- 直接执行自定义sql(不依赖model)
1.使用extra
Book.objects.filter(publisher__name='广东人员出版社').extra(where=['price>50'])
Book.objects.filter(publisher__name='广东人员出版社',price__gt=50)
Book.objects.extra(select={'count':'select count(*) from hello_Book'})
2.使用raw:
Book.objects.raw('select * from hello_Book')
Book.objects.raw("insert into hello_author(name) values('测试')")
rawQuerySet为惰性查询,只有在使用时生会真正执行
3.自定义sql
执行自定义sql:
from django.db import connection
cursor=connection.cursor()
#插入操作
cursor.execute("insert into hello_author(name) values('郭敬明')")
#更新操作
cursor.execute('update hello_author set name='abc' where name='bcd'')
#删除操作
cursor.execute('delete from hello_author where name='abc'')
#查询操作
cursor.execute('select * from hello_author')
raw=cursor.fetchone() #返回结果行游标直读向前,读取一条
cursor.fetchall() #读取所有
34.only和 defer的区别?
1.只取id/name/age字段
models.User.objects.all().only("id", "name", "age")
2.除了name字段
models.User.objects.all().defer("name")
35.select_related和 prefetch_related的区别?
36.django中fiter和 exclude的区别
filter 过滤内容
exclude 反向搜索
37.django中 values和 values list的区别?
- values 字典列表,ValuesQuerySet查询集
<QuerySet [{'id': 2, 'name': '作者2', 'age': 24}, {'id': 3, 'name': '作者3', 'age': 35}]>
- values_list 返回元祖列表,字段值
<QuerySet [(2, '作者2', 24), (3, '作者3', 35)]>
38.如何使用django orm批量创建数据?
objs_list = []
for i in range(100):
obj = People('name%s'%i,18) #models里面的People表
objs_list.append(obj) #添加对象到列表
People.objects.bulk_create(objs_list,100) #更新操作
39.django的Form和 Mode Form的作用?
- Form 自动生成表单,校验表单(表单跟model关联不大)
- Model Form 利用 Django 的 ORM 模型model创建Form(表单跟model关系密切)
40.django的Fom组件中,如果字段中包含 choices参数,请使用两种方式 实现数据源实时更新。
- 重写构造函数
def__init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["city"].widget.choices = models.City.objects.all().values_list("id", "name")
- 利用ModelChoiceField字段,参数为queryset对象
authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
41.django的 Model I中的 ForeignKey字段中的 on_delete参数有什么作用?
#models.py
class Author(models.Model):
author = models.CharField(max_length=250)
class Books(models.Model):
book = models.ForeignKey(Author,on_delete=models.CASCADE)
- CASCADE:删除作者信息一并删除作者名下的所有书的信息;
- PROTECT:删除作者的信息时,采取保护机制,抛出错误:即不删除Books的内容;
- SET_NULL:只有当null=True才将关联的内容置空;
- SET_DEFAULT:设置为默认值;
- SET( ):括号里可以是函数,设置为自己定义的东西;
- DO_NOTHING:字面的意思,啥也不干,你删除你的干我毛线
42.django中csrf的实现机制?
- 第一步:django第一次响应来自某个客户端的请求时,后端随机产生一个token值,把这个token保存在SESSION状态中;同时,后端把这个token放到cookie中交给前端页面;
- 第二步:下次前端需要发起请求(比如发帖)的时候把这个token值加入到请求数据或者头信息中,一起传给后端;Cookies:{csrftoken:xxxxx}
- 第三步:后端校验前端请求带过来的token和SESSION里的token是否一致。
43.django如何实现 websocket?
channel 或者 dewebsocket
44.基于 django使用ajax发送post请求时,有哪种方法携带 csrf token?
- 1.后端将csrftoken传到前端,发送post请求时携带这个值发送
data: {
csrfmiddlewaretoken: '{{ csrf_token }}'
},
- 2.获取form中隐藏标签的csrftoken值,加入到请求数据中传给后端
data: {
csrfmiddlewaretoken:$('[name="csrfmiddlewaretoken"]').val()
},
- 3.cookie中存在csrftoken,将csrftoken值放到请求头中
headers:{ "X-CSRFtoken":$.cookie("csrftoken")}
45.django缓存如何设置?
46.django的缓存能使用redis吗?如果可以的话,如何配置?
- 准备软件:redis数据库、django-redis模块
- 开始使用:
- 安装:pip install django-redis
- 配置:settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
47.django路由系统中name的作用?
反向解析用
例:
urls.py 文件中
url(r'^home', views.home, name='home'), # 给我的url匹配模式起名为 home
url(r'^index/(\d*)', views.index, name='index'), # 给我的url匹配模式起名为index
在模板里面可以这样引用:
{% url 'home' %}
在views函数中可以这样引用:
from django.urls import reverse
reverse("index", args=("2018", ))
48.django的模板中 filter、 simpletag, inclusiontag的区别?
- filter:自定义过滤器只是带有一个或两个参数的Python函数
- simpletag:和自定义filter类似,只不过接收更灵活的参数。
- inclusiontag:多用于返回html代码片段
49.django- debug-toolbar的作用?
调试用
50.django中如何实现单元测试?
test.py文件中
from django.test import TestCase
from app01.models import People #导入people Model类
# Create your tests here.
#创建测试类
class PeopleTestCase(TestCase):
def setUp(self): #setUp 固定写法
People.objects.create(name='name1',age=12) #创建一条数据
def test_people_models(self): #函数名非固定写法
res = People.objects.get(name='name1') #查询数据库
self.assertEqual(res.age,12) #断言查询结果是否是12
执行测试
python manage.py test
51.解释orm中 db first和 code first的含义?
db first: 先创建数据库,再更新表模型
code first:先写表模型,再更新数据库
52.django中如何根据数据库表生成 model类?
1、修改seting文件,在setting里面设置要连接的数据库类型和名称、地址
2、运行下面代码可以自动生成models模型文件
- python manage.py inspectdb
3、创建一个app执行下下面代码:
- python manage.py inspectdb > app/models.py
53.使用orm和原生sql的优缺点?
SQL:
# 优点:
执行速度快
# 缺点:
编写复杂,开发效率不高
----------------------------------------------------------------
ORM:
# 优点:
让用户不再写SQL语句,提高开发效率
可以很方便地引入数据缓存之类的附加功能
# 缺点:
在处理多表联查、where条件复杂查询时,ORM的语法会变得复杂。
没有原生SQL速度快
54.简述MVC和MTV
MVC:model、view(模块)、controller(视图)
MTV:model、tempalte、view
55.django的 contenttype组件的作用?
contenttype是django的一个组件(app),它可以将django下所有app下的表记录下来
可以使用他再加上表中的两个字段,实现一张表和N张表动态创建FK关系。
- 字段:表名称
- 字段:数据行ID
应用:路飞表结构优惠券和专题课和学位课关联
第九章 flask
1.Flask框架的优势?
Flask自由、灵活,可扩展性强,透明可控,第三方库的选择面广,
开发时可以结合最流行最强大的Python库,
2.Flask框架依赖组件
# 依赖jinja2模板引擎
# 依赖werkzurg协议
3.Flask蓝图的作用
# blueprint把实现不同功能的module分开.也就是把一个大的App分割成各自实现不同功能的module.
# 在一个blueprint中可以调用另一个blueprint的视图函数, 但要加相应的blueprint名.
4.列举使用的Flask第三方组件?
# Flask组件
flask-session session放在redis
flask-SQLAlchemy 如django里的ORM操作
flask-migrate 数据库迁移
flask-script 自定义命令
blinker 信号-触发信号
# 第三方组件
Wtforms 快速创建前端标签、文本校验
dbutile 创建数据库连接池
gevnet-websocket 实现websocket
# 自定义Flask组件
自定义auth认证
参考flask-login组件
5.简述Flask上下文管理流程?
# a、简单来说,falsk上下文管理可以分为三个阶段:
1、'请求进来时':将请求相关的数据放入上下问管理中
2、'在视图函数中':要去上下文管理中取值
3、'请求响应':要将上下文管理中的数据清除
# b、详细点来说:
1、'请求刚进来':
将request,session封装在RequestContext类中
app,g封装在AppContext类中
并通过LocalStack将requestcontext和appcontext放入Local类中
2、'视图函数中':
通过localproxy--->偏函数--->localstack--->local取值
3、'请求响应时':
先执行save.session()再各自执行pop(),将local中的数据清除
6.Flask中的g的作用?
# g是贯穿于一次请求的全局变量,当请求进来将g和current_app封装为一个APPContext类,
# 再通过LocalStack将Appcontext放入Local中,取值时通过偏函数在LocalStack、local中取值;
# 响应时将local中的g数据删除:
7.Flask中上下文管理主要涉及到了那些相关的类?并描述类主要作用?
RequestContext #封装进来的请求(赋值给ctx)
AppContext #封装app_ctx
LocalStack #将local对象中的数据维护成一个栈(先进后出)
Local #保存请求上下文对象和app上下文对象
8.为什么要Flask把Local对象中的的值stack 维护成一个列表?
# 因为通过维护成列表,可以实现一个栈的数据结构,进栈出栈时只取一个数据,巧妙的简化了问题。
# 还有,在多app应用时,可以实现数据隔离;列表里不会加数据,而是会生成一个新的列表
# local是一个字典,字典里key(stack)是唯一标识,value是一个列表
9.Flask中多app应用是怎么完成?
请求进来时,可以根据URL的不同,交给不同的APP处理。蓝图也可以实现。
#app1 = Flask('app01')
#app2 = Flask('app02')
#@app1.route('/index')
#@app2.route('/index2')
源码中在DispatcherMiddleware类里调用app2.__call__,
原理其实就是URL分割,然后将请求分发给指定的app。
之后app也按单app的流程走。就是从app.__call__走。
10.在Flask中实现WebSocket需要什么组件?
gevent-websocket
11.wtforms组件的作用?
#快速创建前端标签、文本校验;如django的ModelForm
12.Flask框架默认session处理机制?
# 前提:
不熟的话:记不太清了,应该是……分两个阶段吧
# 创建:
当请求刚进来的时候,会将request和session封装成一个RequestContext()对象,
接下来把这个对象通过LocalStack()放入内部的一个Local()对象中;
因为刚开始 Local 的ctx中session是空的;
所以,接着执行open_session,将cookie 里面的值拿过来,重新赋值到ctx中
(Local实现对数据隔离,类似threading.local)
# 销毁:
最后返回时执行 save_session() 将ctx 中的session读出来进行序列化,写到cookie
然后给用户,接着把 ctx pop掉
13.解释Flask框架中的Local对象和threading.local对象的区别?
# a.threading.local
作用:为每个线程开辟一块空间进行数据存储(数据隔离)。
问题:自己通过字典创建一个类似于threading.local的东西。
storage = {
4740: {val: 0},
4732: {val: 1},
4731: {val: 3},
}
# b.自定义Local对象
作用:为每个线程(协程)开辟一块空间进行数据存储(数据隔离)。
class Local(object):
def __init__(self):
object.__setattr__(self, 'storage', {})
def __setattr__(self, k, v):
ident = get_ident()
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
def task(arg):
obj.val = arg
obj.xxx = arg
print(obj.val)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
14.Flask中 blinker 是什么?
# flask中的信号blinker
信号主要是让开发者可是在flask请求过程中定制一些行为。
或者说flask在列表里面预留了几个空列表,在里面存东西。
简言之,信号允许某个'发送者'通知'接收者'有事情发生了
15.SQLAlchemy中的 session和scoped_session 的区别?
# Session:
由于无法提供线程共享功能,开发时要给每个线程都创建自己的session
打印sesion可知他是sqlalchemy.orm.session.Session的对象
# scoped_session:
为每个线程都创建一个session,实现支持线程安全
在整个程序运行的过程当中,只存在唯一的一个session对象。
创建方式:
通过本地线程Threading.Local()
# session=scoped_session(Session)
创建唯一标识的方法(参考flask请求源码)
16.SQLAlchemy如何执行原生SQL?
# 使用execute方法直接操作SQL语句(导入create_engin、sessionmaker)
engine=create_engine('mysql://root:*****@127.0.0.1/database?charset=utf8')
DB_Session = sessionmaker(bind=engine)
session = DB_Session()
session.execute('alter table mytablename drop column mycolumn ;')
17.ORM的实现原理?
# ORM的实现基于一下三点
映射类:描述数据库表结构,
映射文件:指定数据库表和映射类之间的关系
数据库配置文件:指定与数据库连接时需要的连接信息(数据库、登录用户名、密码or连接字符串)
18.DBUtils模块的作用?
# 数据库连接池
使用模式:
1、为每个线程创建一个连接,连接不可控,需要控制线程数
2、创建指定数量的连接在连接池,当线程访问的时候去取,不够了线程排队,直到有人释放(推荐)
---------------------------------------------------------------------------
两种写法:
1、用静态方法装饰器,通过直接执行类的方法来连接使用数据库
2、通过实例化对象,通过对象来调用方法执行语句
https://www.cnblogs.com/ArmoredTitan/p/Flask.html
以下SQLAlchemy的字段是否正确?如果不正确请更正:
fromdatetime importdatetime
fromsqlalchemy.ext.declarative
importdeclarative_base
fromsqlalchemy importColumn, Integer, String, DateTime
Base =declarative_base()
classUserInfo(Base):
__tablename__ ='userinfo'
id=Column(Integer, primary_key=True, autoincrement=True)
name =Column(String(64), unique=True)
ctime =Column(DateTime, default=datetime.now())
from datetime import datetime
from sqlalchemy.ext.declarative
import declarative_base
from sqlalchemy import Column, Integer, String, DateTime
Base = declarative_base()
class UserInfo(Base):
__tablename__ = 'userinfo'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(64), unique=True)
ctime = Column(DateTime, default=datetime.now())
-----------------------------------------------------------------------
不正确:
Ctime字段中参数应为’default=datetime.now’
now 后面不应该加括号,加了的话,字段不会实时更新。
19.SQLAchemy中如何为表设置引擎和字符编码?
sqlalchemy设置编码字符集,一定要在数据库访问的URL上增加'charset=utf8'
否则数据库的连接就不是'utf8'的编码格式
eng=create_engine('mysql://root:root@localhost:3306/test2?charset=utf8',echo=True)
1. 设置引擎编码方式为utf8。
engine = create_engine("mysql+pymysql://root:123456@127.0.0.1:3306/sqldb01?charset=utf8")
2. 设置数据库表编码方式为utf8
class UserType(Base):
__tablename__ = 'usertype'
id = Column(Integer, primary_key=True)
caption = Column(String(50), default='管理员')
# 添加配置设置编码
__table_args__ = {
'mysql_charset':'utf8'
}
这样生成的SQL语句就自动设置数据表编码为utf8了,__table_args__还可设置存储引擎、外键约束等等信息。
20.SQLAchemy中如何设置联合唯一索引?
通过'UniqueConstraint'字段来设置联合唯一索引
__table_args=(UniqueConstraint('h_id','username',name='_h_username_uc'))
#h_id和username组成联合唯一约束
第十章 tornado
1.简述Tornado框架的特点。
异步非阻塞+websocket
2.简述Tornado框架中Future对象的作用?
# 实现异步非阻塞
视图函数yield一个futrue对象,futrue对象默认:
self._done = False ,请求未完成
self._result = None ,请求完成后返回值,用于传递给回调函数使用。
tornado就会一直去检测futrue对象的_done是否已经变成True。
如果IO请求执行完毕,自动会调用future的set_result方法:
self._result = result
self._done = True
参考:http://www.cnblogs.com/wupeiqi/p/6536518.html(自定义异步非阻塞web框架)
3.Tornado框架中如何编写WebSocket程序?
Tornado在websocket模块中提供了一个WebSocketHandler类。
这个类提供了和已连接的客户端通信的WebSocket事件和方法的钩子。
当一个新的WebSocket连接打开时,open方法被调用,
而on_message和on_close方法,分别在连接、接收到新的消息和客户端关闭时被调用。
此外,WebSocketHandler类还提供了write_message方法用于向客户端发送消息,close方法用于关闭连接。
4.Tornado中静态文件是如何处理的?
如: <link href="{{static_url(“commons.css”)}}" rel=“stylesheet” />
# settings.py
settings = {
"static_path": os.path.join(os.path.dirname(__file__), "static"),
# 指定了静态文件的位置在当前目录中的"static"目录下
"cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
"login_url": "/login",
"xsrf_cookies": True,
}
经上面配置后
static_url()自动去配置的路径下找'commons.css'文件
5.Tornado操作MySQL使用的模块?
torndb
torndb是基于mysqldb的再封装,所以使用时要先安装myqldb
6.Tornado操作redis使用的模块?
tornado-redis
7.简述Tornado框架的适用场景?
web聊天室,在线投票
第十一章 api
1.什么是webservice
WebService就是一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过 Web来调用这个应用程序。我们把调用这个WebService的应用程序叫做客户端,而把提供这个WebService的应用程序叫做服务端。
2.什么是rpc
'远程过程调用协议' 是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 进化的顺序: 现有的RPC,然后有的RESTful规范
3.简述MVC和MTV
MVC:model、view(模块)、controller(视图)
MTV:model、tempalte、view
4.django的contenttype组件的作用?
contenttype是django的一个组件(app),它可以将django下所有app下的表记录下来
可以使用他再加上表中的两个字段,实现一张表和N张表动态创建FK关系。
- 字段:表名称
- 字段:数据行ID
应用:路飞表结构优惠券和专题课和学位课关联
5.谈谈你对restfull 规范的认识?
restful其实就是一套编写接口的'协议',规定如何编写以及如何设置返回值、状态码等信息。
# 最显著的特点:
# 用restful:
给用户一个url,根据method不同在后端做不同的处理
比如:post创建数据、get获取数据、put和patch修改数据、delete删除数据。
# 不用restful:
给调用者很多url,每个url代表一个功能,比如:add_user/delte_user/edit_user/
# 当然,还有协议其他的,比如:
'版本'来控制让程序有多个版本共存的情况,版本可以放在 url、请求头(accept/自定义)、GET参数
'状态码'200/300/400/500
'url中尽量使用名词'restful也可以称为“面向资源编程”
'api标示'
api.luffycity.com
www.luffycity.com/api/
6.接口的幂等性是什么意思?
'一个接口通过1次相同的访问,再对该接口进行N次相同的访问时,对资源不造影响就认为接口具有幂等性。'
GET, #第一次获取结果、第二次也是获取结果对资源都不会造成影响,幂等。
POST, #第一次新增数据,第二次也会再次新增,非幂等。
PUT, #第一次更新数据,第二次不会再次更新,幂等。
PATCH,#第一次更新数据,第二次不会再次更新,非幂等。
DELTE,#第一次删除数据,第二次不在再删除,幂等。
7.什么是RPC?
'远程过程调用协议' 是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。 进化的顺序: 现有的RPC,然后有的RESTful规范
8.Http和Https的区别?
#Http: 80端口
#https: 443端口
# http信息是明文传输,https则是具有安全性的ssl加密传输协议。
#- 自定义证书
- 服务端:创建一对证书
- 客户端:必须携带证书
#- 购买证书
- 服务端: 创建一对证书,。。。。
- 客户端: 去机构获取证书,数据加密后发给咱们的服务单
- 证书机构:公钥给改机构
9.为什么要使用django rest framework框架?
# 在编写接口时可以不使用django rest framework框架,
# 不使用:也可以做,可以用django的CBV来实现,开发者编写的代码会更多一些。
# 使用:内部帮助我们提供了很多方便的组件,我们通过配置就可以完成相应操作,如:
'序列化'可以做用户请求数据校验+queryset对象的序列化称为json
'解析器'获取用户请求数据request.data,会自动根据content-type请求头的不能对数据进行解析
'分页'将从数据库获取到的数据在页面进行分页显示。
# 还有其他组件:
'认证'、'权限'、'访问频率控制
10.django rest framework框架中都有那些组件?
#- 路由,自动帮助开发者快速为一个视图创建4个url
www.oldboyedu.com/api/v1/student/$
www.oldboyedu.com/api/v1/student(?P<format>\w+)$
www.oldboyedu.com/api/v1/student/(?P<pk>\d+)/$
www.oldboyedu.com/api/v1/student/(?P<pk>\d+)(?P<format>\w+)$
#- 版本处理
- 问题:版本都可以放在那里?
- url
- GET
- 请求头
#- 认证
- 问题:认证流程?
#- 权限
- 权限是否可以放在中间件中?以及为什么?
#- 访问频率的控制
匿名用户可以真正的防止?无法做到真正的访问频率控制,只能把小白拒之门外。
如果要封IP,使用防火墙来做。
登录用户可以通过用户名作为唯一标示进行控制,如果有人注册很多账号,则无法防止。
#- 视图
#- 解析器 ,根据Content-Type请求头对请求体中的数据格式进行处理。request.data
#- 分页
#- 序列化
- 序列化
- source
- 定义方法
- 请求数据格式校验
#- 渲染器
11.django rest framework框架中的视图都可以继承哪些类
a. 继承APIView(最原始)但定制性比较强
这个类属于rest framework中的顶层类,内部帮助我们实现了只是基本功能:认证、权限、频率控制,
但凡是数据库、分页等操作都需要手动去完成,比较原始。
class GenericAPIView(APIView)
def post(...):
pass
b.继承GenericViewSet(ViewSetMixin,generics.GenericAPIView)
首先他的路由就发生变化
如果继承它之后,路由中的as_view需要填写对应关系
在内部也帮助我们提供了一些方便的方法:
get_queryset
get_object
get_serializer
get_serializer_class
get_serializer_context
filter_queryset
注意:要设置queryset字段,否则会抛出断言的异常。
代码
只提供增加功能 只继承GenericViewSet
class TestView(GenericViewSet):
serialazer_class = xxx
def creat(self,*args,**kwargs):
pass # 获取数据并对数据
c. 继承 modelviewset --> 快速快发
-ModelViewSet(增删改查全有+数据库操作)
-mixins.CreateModelMixin(只有增),GenericViewSet
-mixins.CreateModelMixin,DestroyModelMixin,GenericViewSet
对数据库和分页等操作不用我们在编写,只需要继承相关类即可。
示例:只提供增加功能
class TestView(mixins.CreateModelMixin,GenericViewSet):
serializer_class = XXXXXXX
modelviewset --> 快速开发,复杂点的genericview、apiview
12.简述 django rest framework框架的认证流程。
13.django rest framework如何实现的用户访问频率控制?(throttle(访问频率)组件)
第十二章 git
1.git常见命令作用:
# git init
初始化,当前所在的文件夹可以被管理且以后版本相关的数据都会存储到.git文件中
# git status
查看当前文件夹以及子目录中文件是否发生变化:
内容修改/新增文件/删除,已经变化的文件会变成红色,已经add的文件会变成绿色
# git add .
给发生变化的文件(贴上一个标签)或 将发生变化的文件放到某个地方,
只写一个句点符就代表把git status中红色的文件全部打上标签
# git commit -m
新增用户登录认证功能以及xxx功能将“绿色”文件添加到版本中
# git log
查看所有版本提交记录,可以获取版本号
# git reset --hard 版本号
将最新的版本回退到更早的版本
# git reflog
回退到之前版本后悔了,再更新到最新或者最新之前的版本
# git reset --hard 版本 回退
2.简述以下git中stash命令作用以及相关其他命令。
'git stash':将当前工作区所有修改过的内容存储到“某个地方”,将工作区还原到当前版本未修改过的状态
'git stash list':查看“某个地方”存储的所有记录
'git stash clear':清空“某个地方”
'git stash pop':将第一个记录从“某个地方”重新拿到工作区(可能有冲突)
'git stash apply':编号, 将指定编号记录从“某个地方”重新拿到工作区(可能有冲突)
'git stash drop':编号,删除指定编号的记录
3.git 中 merge 和 rebase命令 的区别。
merge:
会将不同分支的提交合并成一个新的节点,之前的提交分开显示,
注重历史信息、可以看出每个分支信息,基于时间点,遇到冲突,手动解决,再次提交
rebase:
将两个分支的提交结果融合成线性,不会产生新的节点;
注重开发过程,遇到冲突,手动解决,继续操作
4.公司如何基于git做的协同开发?
1、你们公司的代码review分支怎么做?谁来做?
答:组长创建review分支,我们小功能开发完之后,合并到review分支交给老大(小组长)来看,
1.1、你组长不开发代码吗?
他开发代码,但是它只开发核心的东西,任务比较少。
或者抽出时间,我们一起做这个事情
2、你们公司协同开发是怎么协同开发的?
每个人都有自己的分支,阶段性代码完成之后,合并到review,然后交给老大看
--------------------------------------------------------------------------
# 大致工作流程
公司:
下载代码
git clone https://gitee.com/wupeiqi/xianglong.git
或创建目录
cd 目录
git init
git remote add origin https://gitee.com/wupeiqi/xianglong.git
git pull origin maste
创建dev分支
git checkout dev
git pull origin dev
继续写代码
git add .
git commit -m '提交记录'
git push origin dev
回家:
拉代码:
git pull origin dev
继续写:
继续写代码
git add .
git commit -m '提交记录'
git push origin dev
5.如何基于git实现代码review?
6.git如何实现v1.0 、v2.0 等版本的管理?
在命令行中,使用“git tag –a tagname –m “comment”可以快速创建一个标签。
需要注意,命令行创建的标签只存在本地Git库中,还需要使用Git push –tags指令发布到服务器的Git库中
7.什么是gitlab
gitlab是公司自己搭建的项目代码托管平台
8.github和gitlab的区别?
1、gitHub是一个面向开源及私有软件项目的托管平台
(创建私有的话,需要购买,最低级的付费为每月7刀,支持5个私有项目)
2、gitlab是公司自己搭建的项目托管平台
9.如何为github上牛逼的开源项目贡献代码?
1、fork需要协作项目
2、克隆/关联fork的项目到本地
3、新建分支(branch)并检出(checkout)新分支
4、在新分支上完成代码开发
5、开发完成后将你的代码合并到master分支
6、添加原作者的仓库地址作为一个新的仓库地址
7、合并原作者的master分支到你自己的master分支,用于和作者仓库代码同步
8、push你的本地仓库到GitHub
9、在Github上提交 pull requests
10、等待管理员(你需要贡献的开源项目管理员)处理
10.git中 .gitignore文件的作用
一般来说每个Git项目中都需要一个“.gitignore”文件,
这个文件的作用就是告诉Git哪些文件不需要添加到版本管理中。
实际项目中,很多文件都是不需要版本管理的,比如Python的.pyc文件和一些包含密码的配置文件等等。
11.什么是敏捷开发?
'敏捷开发':是一种以人为核心、迭代、循序渐进的开发方式。
它并不是一门技术,而是一种开发方式,也就是一种软件开发的流程。
它会指导我们用规定的环节去一步一步完成项目的开发。
因为它采用的是迭代式开发,所以这种开发方式的主要驱动核心是人