整理的一些python面试题(1)
# 1.什么是GIL
Python代码的执行由Python 虚拟机(也叫解释器主循环,CPython版本)来控制,Python 在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即在任意时刻,只有一个线程在解释器中运行。对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python 虚拟机按以下方式执行:
1. 设置GIL
2. 切换到一个线程去运行
3. 运行:
a. 指定数量的字节码指令,或者
b. 线程主动让出控制(可以调用time.sleep(0))
4. 把线程设置为睡眠状态
5. 解锁GIL
6. 再次重复以上所有步骤
# 2.Python中的@staticmethod和@classmethod的区别 (**)
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
既然@staticmethod和@classmethod都可以直接类名.方法名()来调用,那他们有什么区别呢
从它们的使用上来看,
@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
class A(object):
bar = 1
def foo(self):
print ('foo')
@staticmethod
def static_foo():
print ('static_foo')
print (A.bar)
@classmethod
def class_foo(cls):
print ('class_foo')
print (cls.bar)
cls().foo()
A.static_foo()
A.class_foo()
# 3.Python里面如何拷贝一个对象,并解析深浅拷贝
#
# 4.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)
# 5.简述迭代器生成器以及他们之间的区别
def power(values):
for value in values:
print("powing %s" % value)
yield value
def add(values):
for value in values:
if value % 2 == 0:
yield value + 3
else:
yield value + 2
elements = [1, 4, 7, 9, 12, 19]
add(power(elements))
for i in add(power(elements)):
print(i)
power这时已经不再是一个函数了,它是一个生成器,注意power在调用过程中并没有执行print,
但是在用for进行遍历时它执行了print,所以你首先要清楚这是生成器和函数的区别.
接下来才说生成器和迭代器的区别:
1、语法方面来讲:
生成器是用函数中yield语句来创建的。迭代器的创建首先跟函数无关,可以用iter([1,2])来创建。
2、使用方面来讲:
由于生成器是使用函数的方式创建的,所以生成器里面的所有过程都会被执行,但请注意生成器里面的过程只有在被next()调用或者for循环调用时,
里面的过程才会被执行,如同上面的例子只是单纯调用add这个对象时,add里面的过程没有被执行哦
迭代器同样可以被for和next调用但是由于没有其他过程,在被调用时只会返回值,不会有其他动作
# 6.什么是协程,Python中的协程是如何实现的
线程是系统级别的,它们是由操作系统调度;协程是程序级别的,由程序员根据需要自己调度。
我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序,这就是协程。
也就是说同一线程下的一段代码<1>执行着执行着就可以中断,然后跳去执行另一段代码,当再次回来执行代码块<1>的时候,接着从之前中断的地方开始执行
def simple_coroutine():
print('-> start')
x = yield
print('-> recived', x)
sc = simple_coroutine()
next(sc)
sc.send('zhexiao')
1. 协程使用生成器函数定义:定义体中有 yield 关键字。
2. yield 在表达式中使用;如果协程只需从客户那里接收数据,那么产出的值是 None —— 这个值是隐式指定的,因为 yield 关键字右边没有表达式。
3. 首先要调用 next(…) 函数,因为生成器还没启动,没在 yield 语句处暂停,所以一开始无法发送数据。
4. 调用send方法,把值传给 yield 的变量,然后协程恢复,继续执行下面的代码,直到运行到下一个 yield 表达式,或者终止。
# 7.什么是装饰器,请使用装饰器实现singletion。
装饰器可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用,为已经存在的对象添加额外的功能
在不改变函数代码、功能结果的前提下,为函数增添某些功能。通过python的闭包及一切皆对象,函数也为对象的特性。将函数对象传入wrapper函数,返回一个装饰后的函数。
可以极大简化代码,不必重复写代码。
def single_decorator(cls):
single = {}
def wrapper(*args, **kwargs):
if not single.get(cls.__name__):
single[cls.__name__] = cls(*args, **kwargs)
return single[cls.__name__]
return wrapper
@single_decorator
class School:
def __init__(self, name, addr, capital, represent):
self.__name = name #学校名
self.__addr = addr #地址
self.__registered_capital = capital #注册资金
self.__represent = represent #法人代表
def get_info(self):
return "校名: {} 地址: {} 法人代表: {}".format(self.__name, self.__addr, self.__represent)
@staticmethod
def show():
print(School._isxx)
ss= School('古天乐学校', '大凉山', '100.000', '古天乐')
ss2= School('施明德', '台湾?', '1.000.000', '瞎写的')
print('实例一id: %s'%(id(ss)))
print('实例二id: %s'%(id(ss2)))
print(ss.get_info())
print(ss2.get_info())
# 8.请使用Python实现快速排序
def QuickSort(arr,firstIndex,lastIndex):
if firstIndex<lastIndex:
divIndex=Partition(arr,firstIndex,lastIndex)
QuickSort(arr,firstIndex,divIndex)
QuickSort(arr,divIndex+1,lastIndex)
else:
return
def Partition(arr,firstIndex,lastIndex):
i=firstIndex-1
for j in range(firstIndex,lastIndex):
if arr[j]<=arr[lastIndex]:
i=i+1
arr[i],arr[j]=arr[j],arr[i]
arr[i+1],arr[lastIndex]=arr[lastIndex],arr[i+1]
return i
arr=[1,4,7,1,5,5,3,85,34,75,23,75,2,0]
print("initial array:\n",arr)
QuickSort(arr,0,len(arr)-1)
print("result array:\n",arr)
# 9.简述select和epoll的原理和区别
#当用户进程发起一个read请求时,实际是调用系统接口,让内核去读取数据到内核空间,再copy到用户空间,
系统给进程return 一个OK,然后用户进程去取,所以在用户发起请求后socket是阻塞状态,而select和epoll这两个函数的功能就是,
将socket设置为不阻塞后,让操作系统帮忙监控他们所维护的所有socket,只有有socket准备就绪就返回,
这两函数通过不断的轮询自己所维护的socket,将准备好的socket返回用户进程。select与epoll的区别就是,select有最大维护链接限制,Linux下一班为1024,可以通过修改宏定义改变,但是太大了依然会影响速度,而epoll没有限制
# 10.简述Python的垃圾回收机制
python采用引用计数的方式。来回收不被使用的对象。每隔一段时间检查一次,当对象的引用计数为0是就执行del 析构函数,来释放该内存,到预先申请的内存空间中去,防止内存碎片的产生。
分代回收:存活时间越久的对象,越不可能在后面的程序中变成垃圾。出于信任和效率,对于这样一些“长寿”对象,我们相信它们的用处,所以减少在垃圾回收中扫描它们的频率。Python将所有的对象分为0,1,2三代。所有的新建对象都是0代对象。当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。垃圾回收启动时,一定会扫描所有的0代对象。如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。
这两个次数即上面get_threshold()返回的(700, 10, 10)返回的两个10。也就是说,每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收。
# 11.写一个简单的python socket编程
import socketserver
class PersonalTcpHandle(socketserver.BaseRequestHandler): #继承父类请求处理类
""" 每来一个请求都会实例化一次这个类"""
def handle(self): #重写handle 方法,客户端来的所有请求都在这被处理响应
"""该类接受客户端发送的数据,并在处理后返回"""
self.data = self.request.recv(1024).strip() #这儿的 self.request 就是socket里面的 sock ,addr = server.accept() 里面的 sock
""" Do someing with self.data and then return the result to client"""
self.request.sendall('处理后的数据') #注意必须为bytes 类型
if __name__ == "__main__":
host, port = "localhost", 3309
server = socketserver.TCPServer((host, port), PersonalTcpHandle) #单线程
# server = socketserver.ThreadignTCPServer((host,port), PersonalTcpHandle) #多线程服务器, 建议使用
server.serve_forver(poll_interval= 1) #poll_interval 参数为每多少时间检查一次连接是否断开
# 12.用python将'123456'反转成'654321'。
反转列表_12.py
# 13.请列出你了解的Web服务器负载架构。
# 14.请实现一个装饰器,限制该函数被调用的频率,如10秒一次。
import time
def deco_limit(s, foo= None):
"""
这是个函数装饰器,可以控制访问频率
:param s: 该参数为访问频率,每多少s一次
:param foo: 可选参数,用户自定制装饰器逻辑(比如cookie或状态验证等,执行返回值为False则不执行函数)
:return:
"""
def wrpper(func):
"""
该函数接受被修饰函数作为参数,返回装饰器,变量func_identify为变量标识,
存储被修饰函数的name为key,value为调用成功的时间戳。key-second存储为允许调用时间频率
:param func: 参数为被修饰函数
:return:
"""
name = func.__name__
func_identify ={name: 0,'foo': foo, 'second': s}
def inner(*args, **kwargs):
"""
执行函数
:param args: 将接收的参数,打包
:param kwargs:
:return:
"""
useable_time = func_identify[name] + func_identify['second'] #上次执行时间,加时间频率
time_now = time.time()
if time_now > useable_time:
func_identify[name] = time_now #设置调用时间
res = func(*args,**kwargs)
else:
remain_time = useable_time - time_now
print('\033[32;1mFunction \033[31;1m{0} \033[0m'\
.format(name)+'\033[32;1mcan be used after {0:.2f} seconds later\033[0m'\
.format(remain_time))
res = None
return res
return inner
return wrpper
@deco_limit(5) #五秒允许调用一次
def foo1(*args, **kwargs):
""" do something with args and kwargs"""
print('执行foo1--everything is ok')
return 'result'
@deco_limit(3) #三秒调用一次
def func1(*args, **kwargs):
""" do something with args and kwargs"""
print('执行func1---呱呱呱')
return 'result'
# 15.请描述一下,tuple,list,dict,set 的特点。
#图片
# 16.请说一声对迭代器与生成器的理解。
对于list、string、tuple、dict等这些容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数。iter()是python内置函数。
iter()函数会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内的元素。next()也是python内置函数。在没有后续元素时,next()会抛出一个StopIteration异常,通知for语句循环结束。
迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的_next_方法(Python3中是对象的_next_方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的_next_方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现_iter_方法,而_iter_方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的_iter_方法返回自身self即可。
1,迭代器协议:对象需要提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。
2,可迭代对象:实现了迭代器协议对象。list、tuple、dict都是Iterable(可迭代对象),但不是Iterator(迭代器对象)。但可以使用内建函数iter(),把这些都变成Iterable(可迭代器对象)。
3,for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束
>生成器是只能遍历一次的。
>生成器是一类特殊的迭代器。
# 17.请用python实现单例模式,至少两种方式。
class Singleton(object):
def __init__(self):
pass
@classmethod
def get(cls, *args, **kwargs):
if not hasattr(Singleton, "_single"):
Singleton._single = Singleton(*args, **kwargs)
return Singleton._single
# 18.请说一说lambda函数的作用,请使用lambda和reduce实现1到100的累加。(**)
def sum(x, y):
return x + y
from functools import reduce
print(reduce(sum, range(1,101)))
#
# 19.用正则实现匹配手机号(包含手机号码前带86和+86的情况)。
import re
# 验证手机号是否正确
phone_pat = re.compile('^((\\+86)|(86))?[1][3456789][0-9]{9}$')
while True:
phone = input('请输入您的手机号:')
res = re.search(phone_pat, phone)
if res:
print('正常手机号')
else:
print('不是手机号')
# 20.现有字典d={'a':26,'g':20,'e':22,'c':24,'d':23,'f':21,'b':25}请按照字段中的value进行排序。
d = {'a':26,'g':20,'e':22,'c':24,'d':23,'f':21,'b':25}
# 第一种方法,key使用lambda匿名函数取value进行排序
a = sorted(d.items(), key=lambda x: x[1])
a1 = sorted(d.items(), key=lambda x: x[1], reverse=True)
# key使用lambda匿名函数按键进行排序
a2 = sorted(d.items(), key=lambda x: x[0])
# 第二种方法使用operator的itemgetter进行排序
import operator
b = sorted(d.items(), key=operator.itemgetter(1))
# 第三种方法讲key和value分装成元祖,在进行排序
f = zip(d.keys(), d.values())
c = sorted(f)
print(a)
print(a1)
print(a2)
print(b)
print(c)
#
# 21.mysql高可用方案有哪些,备份方案有哪些,有什么优缺点?
# 22.画出TCP三次握手,四次挥手断开示意图。
# 23.叙述mysql半同步复制原理。
异步复制(Asynchronous replication)
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主键如果crash掉了,此时主键上已经提交的事务可能并没有传到从键上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
全同步复制(Fully synchronous replication)
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
半同步复制(Semisynchronous replication)
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。
相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
半同步复制的潜在问题
客户端事务在存储引擎层提交后,在得到从库确认的过程中,主库宕机了,此时,可能的情况有两种:
事务还没发送到从库上
此时,客户端会收到事务提交失败的信息,客户端会重新提交该事务到新的主上,当宕机的主库重新启动后,以从库的身份重新加入到该主从结构中,会发现,该事务在从库中被提交了两次,一次是之前作为主的时候,一次是被新主同步过来的。
事务已经发送到从库上
此时,从库已经收到并应用了该事务,但是客户端仍然会收到事务提交失败的信息,重新提交该事务到新的主上。
#
# 24.Python是如何进行类型转换的。
#图片
# 25.请写出一段python代码实现删除一个list里面的重复元素。
def distFunc2():
a=[1,2,4,2,4,5,7,10,5,5,7,8,9,0,3]
a=list(set(a)) # set是非重复的,无序集合。可以用list来的排队对set进行排序,list()转换为列表,a.sort来排序
print (a)
if __name__ == '__main__':
print(distFunc2())
#
# 26.python中类方法,类实例方法,静态方法有何区别?
实例方法只能被实例对象调用,静态方法(由@staticmethod装饰的方法)、类方法(由@classmethod装饰的方法),可以被类或类的实例对象调用。实例方法,第一个参数必须要默认传实例对象,一般习惯用self。静态方法,参数没有要求。类方法,第一个参数必须要默认传类,一般习惯用cls。
#
# 27.python中pass语句作用是什么?
# 当你在写一个函数的时候,函数主体暂时不知道写什么,可以用pass来进行占位,也可以提醒自己这个函数没写完。
def icpython():
pass
# 如果你想要一个while语句的无线循环,每次迭代的时候不做任何操作,可以用pass定义。下面只是一个例子,实际开发中不推荐这么使用,python会进入死循环
while True:
pass
# 28.介绍一下python中range()和xrange()函数的用法。
range
函数说明:range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列。
xrange
函数说明:用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器。
要生成很大的数字序列的时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间,这两个基本上都是在循环的时候用。
xrange做循环的性能比range好,尤其是返回很大的时候,尽量用xrange吧,除非你是要返回一个列表。
# 29.用python匹配 HTML Tag 的时候,<.*>和<.*?>有什么区别?
当重复匹配一个正则表达式时候, 例如<.*>, 当程序执行匹配的时候,会返回最大的匹配值
例如:
import re
s = "<html><head><title>Title</title>"
print(re.match("<.*>",s).group())
会返回一个匹配<html><head><title>Title</title>而不是<html>
而
import re
s = "<html><head><title>Title</title>"
print(re.match("<.*?>", s).group())
则会返回<html>
<.*>这种匹配称作贪心匹配 <.*?>称作非贪心匹配
#
# 30.python中 如何拷贝一个对象?
1.浅拷贝 :
使用copy.copy,它可以进行对象的浅拷贝(shallow copy),它复制了对象,但对于对象中的元素,依然使用引用(换句话说修改拷贝对象元素,则被拷贝对象元素也被修改)
2.深拷贝 :
使用copy.deepcopy,它可以进行深拷贝,不仅拷贝了对象,同时也拷贝了对象中的元素,获得了全新的对象,与被拷贝对象完全独立,但这需要牺牲一定的时间和空间。
3.特殊拷贝:
如要复制列表L,使用list(L),要复制一个字典d,使用dict(d),要复制一个集合s,使用set(s)。
总结一下的话:如果你要复制某个对象object, 它属于python内建的类型type,那么你可以使用type(object)来 获得一个拷贝。
#
# 31.如何用python查询和替换一个文本字符串?
可以使用sub()方法来进行查询和替换,sub方法的格式为:sub(replacement, string[, count=0])
replacement是被替换成的文本
string是需要被替换的文本
count是一个可选参数,指最大被替换的数量
import re
p = re.compile('(blue|white|red)')
print(p.sub('colour','blue socks and red shoes'))
print(p.sub('olour','blue socks and red shoes', count=1))
subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新的字符串和总共替换的数量
import re
p = re.compile('(blue|white|red)')
print(p.subn('colour','blue socks and red shoes'))
print(p.subn('colour','blue socks and red shoes', count=1))
#
# 32.python函数中经常有*args和**kwargs这两个参数,它们是什么意思,为什么使用它们?
可变参数
如果我们不确定要往函数中传入多少个参数,或者我们想往函数中以列表和元组的形式传参数时,那就使要用*args;
如果我们不知道要往函数中传入多少个关键词参数,或者想传入字典的值作为关键词参数时,那就要使用**kwargs。
# 33.python中变量的作用域,变量的查找顺序。
python解释器在解引用一个变量时遵循所谓‘legb’原则。
即,首先在local即局部作用域中查找变量声明和值,如果没有找到,在函数的closure属性中查找变量(只有闭包函数要考虑)即enclosing,
如果还没有找到则在全局作用域中查找变量即global,如果还是没有找到则在built-in的变量中查找,也就是python的关键字和默认的全局函数(e.g. list tuple open print)。
#
# 36.mysql有哪些存储引擎,优化mysql数据库的方法有哪些。
#MyISAM:这种引擎是mysql最早提供的。这种引擎又可以分为静态MyISAM、动态MyISAM 和压缩MyISAM三种: 静态MyISAM:如果数据表中的各数据列的长度都是预先固定好的,服务器将自动选择这种表类型。因为数据表中每一条记录所占用的空间都是一样的,所以这种表存取和更新的效率非常高。当数据受损时,恢复工作也比较容易做。 动态MyISAM:如果数据表中出现varchar、xxxtext或xxxBLOB字段时,服务器将自动选择这种表类型。相对于静态MyISAM,这种表存储空间比较小,但由于每条记录的长度不一,所以多次修改数据后,数据表中的数据就可能离散的存储在内存中,进而导致执行效率下降。同时,内存中也可能会出现很多碎片。因此,这种类型的表要经常用optimize
table 命令或优化工具来进行碎片整理。 压缩MyISAM:以上说到的两种类型的表都可以用myisamchk工具压缩。这种类型的表进一步减小了占用的存储,但是这种表压缩之后不能再被修改。另外,因为是压缩数据,所以这种表在读取的时候要先时行解压缩。 但是,不管是何种MyISAM表,目前它都不支持事务,行级锁和外键约束的功能。 2.MyISAM Merge引擎:这种类型是MyISAM类型的一种变种。合并表是将几个相同的MyISAM表合并为一个虚表。常应用于日志和数据仓库。 3.InnoDB:InnoDB表类型可以看作是对MyISAM的进一步更新产品,它提供了事务、行级锁机制和外键约束的功能。 4. memory(heap):这种类型的数据表只存在于内存中。它使用散列索引,所以数据的存取速度非常快。因为是存在于内存中,所以这种类型常应用于临时表中。 5. archive:这种类型只支持select 和 insert语句,而且不支持索引。常应用于日志记录和聚合分析方面。
优化数据库:
Mysql的优化,大体可以分为三部分:索引的优化,sql语句的优化,表的优化
索引的优化:1、主键就是聚集索引
2、只要建立索引就能显著提高查询速度
3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度
注意:
1、用聚合索引比用不是聚合索引的主键速度快
2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下
3、使用聚合索引内的时间段,搜索时间会按数据占整个数据表的百分比成比例减少,而无论聚合索引使用了多少个
4 、日期列不会因为有分秒的输入而减慢查询速度
Sql语句的优化:
1、Like语句是否属于SARG取决于所使用的通配符的类型
2、or 会引起全表扫描
3、非操作符、函数引起的不满足SARG形式的语句
4、IN 的作用相当与OR
5、尽量少用NOT
6、exists 和 in 的执行效率是一样的
7、用函数charindex()和前面加通配符%的LIKE执行效率一样
8、union并不绝对比or的执行效率高
9、字段提取要按照“需多少、提多少”的原则,避免“select *”
10、count(*)不比count(字段)慢
11、order by按聚集索引列排序效率最高
12、高效的TO
# 37.Web开发中,session和cookie的作用与区别。
1、cookie数据存放在客户的浏览器上,session数据放在服务器上.
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)
session很容易失效,用户体验很差;
虽然cookie不安全,但是可以加密 ;
cookie也分为永久 和暂时 存在的;
浏览器 有禁止cookie功能 ,但一般用户都不会设置;
一定要设置失效时间,要不然浏览器关闭就消失了;
# 38.Web开发中有哪些技术手段防止SQL注入?
1、检查变量数据类型和格式
如果你的SQL语句是类似where id={$id}这种形式,数据库里所有的id都是数字,那么就应该在SQL被执行前,检查确保变量id是int类型;如果是接受邮箱,那就应该检查并严格确保变量一定是邮箱的格式,其他的类型比如日期、时间等也是一个道理。总结起来:只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。
比如,我们前面接受username参数例子中,我们的产品设计应该是在用户注册的一开始,就有一个用户名的规则,比如5-20个字符,只能由大小写字母、数字以及一些安全的符号组成,不包含特殊字符。此时我们应该有一个check_username的函数来进行统一的检查。不过,仍然有很多例外情况并不能应用到这一准则,比如文章发布系统,评论系统等必须要允许用户提交任意字符串的场景,这就需要采用过滤等其他方案了。
2、过滤特殊符号
对于无法确定固定格式的变量,一定要进行特殊符号过滤或转义处理。
3、绑定变量,使用预编译语句
MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法
实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式,使用预编译的SQL语句语义不会发生改变,
在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变SQL语句的结构
# 39.编写快速排序或者冒泡排序。
#!/usr/bin/python
# coding=utf-8
#
# 1 输入五个数字
print("=" * 50)
a = int(input("请输入第一个数字:"))
b = int(input("请输入第二个数字:"))
c = int(input("请输入第三个数字:"))
d = int(input("请输入第四个数字:"))
e = int(input("请输入第五个数字:"))
# 2 将5个数字输入到数组中
nums = [a, b, c, d, e]
print("你输入的5个数字为:%s" % nums)
# 3 两次使用for循环,一次是起始点选择,第二次为每个数字于后一个数字匹配
j = 0
q
for j in range(0, len(nums) - 1):
i = 0 + j
for i in range(0, len(nums)):
if i < len(nums) - 1:
if nums[i] > nums[i + 1]:
nums[i], nums[i + 1] = nums[i + 1], nums[i]
i += 1
j += 1
print("冒泡排序调整%s次" % (i))
print("冒泡排序调整后的序列为:%s" % nums)
print("=" * 50)
# 40.Python是 如何进行内存管理的?
python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创建了一个引用计数,当对象不再需要时,这个对象的引用计数为0时,它被垃圾回收。
总结一下对象会在一下情况下引用计数加1:
1.对象被创建:x=4
2.另外的别人被创建:y=x
3.被作为参数传递给函数:foo(x)
4.作为容器对象的一个元素:a=[1,x,'33']
引用计数减少情况
1.一个本地引用离开了它的作用域。比如上面的foo(x)函数结束时,x指向的对象引用减1。
2.对象的别名被显式的销毁:del x ;或者del y
3.对象的一个别名被赋值给其他对象:x=789
4.对象从一个窗口对象中移除:myList.remove(x)
5.窗口对象本身被销毁:del myList,或者窗口对象本身离开了作用域。
#
# 41.介绍一下python的异常处理机制和自己开发过程中的体会。
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
在Python当中,若一个程序在运行的时候出错,Python解释器会自动的在出错的地方生成一个异常对象,
而后Python解释器会自动的在出错地方的附近寻找有没有对这个异常对象处理的代码,所谓异常处理代码就是try……except语句,
如果没有,Python解释器会自动的将这个异常对象抛给其调用函数,就这样层层抛出,如果在main当中也没有对这个异常对象处理的代码,Python解释器(实际上是操作系统)最后会做一个简单粗暴的处理,将整个程序给终止掉,并将错误的信息在显示屏上输出。
#
# 42.python中怎么有效读取一个20G大小的文件。
def readInChunks(fileObj, chunkSize=4096):
"""
Lazy function to read a file piece by piece.
Default chunk size: 4kB.
"""
while 1:
data = fileObj.read(chunkSize)
if not data:
break
yield data
f = open('bigFile')
for chuck in readInChunks(f):
#do_something(chunk)
f.close()
# 45、Python里面如何实现tuple和list的转换?
#(1)、函数tuple(seq)可以把所有可迭代的(iterable)序列转换成一个tuple, 元素不变,排序也不变。
例如,tuple([1,2,3])返回(1,2,3), tuple(‘abc’)返回(‘a’.'b’,'c’).如果参数已经是一个tuple的话,函数不做任何拷贝而直接返回原来的对象,所以在不确定对象是不是tuple的时候来调用tuple()函数也不是很耗费的。
(2)、函数list(seq)可以把所有的序列和可迭代的对象转换成一个list,元素不变,排序也不变。
例如 list([1,2,3])返回(1,2,3), list(‘abc’)返回['a', 'b', 'c']。如果参数是一个list, 她会像set[:]一样做一个拷贝。
# 46、pyhon中,单引号,双引号,三引号的区别?
#
#
#