还得看:正则,数据库
牛客读取数据
import sys
file = open("input.in")
sys.stdin = file
arr = []
while True:
line = sys.stdin.readline().strip().split()
if not line: break
arr.append(list(map(int, line)))
print(arr)
a = [1,2,3]
b = [4,5,6]
a += b #这样操作是在原地址上进行拼接
a = a+b #这样表示 把 a + b拼接好的列表,赋值给a,此时会产生新的地址。
1:闭包操作
在函数体内部使用函数体外部的变量时(未传参的情况),想要修改外部变量会报错,但是如果只是使用外部变量的值(比如:赋值给内部变量、打印等操作时)不会报错.
a = 1
def func():
print(a)
func()
print(a)
# 输出都是 1
**********************************
a = 1
def func():
a = a + 1
print(a)
func()
print(a)
#报错
*********************
a = 1
def func():
global a
a = a + 1
print(a)
func()
print(a)
#输出都是2
要在函数体内用函数体外的变量,一般是将外部变量通过传参的形式,传递给函数。
nonlocal关键字 好像和global 关键字一样???
def out_func(name):
print('out_func name is '+ name)
def in_func():
nonlocal name
name = name + 'in'
print('in_func name is '+name)
return in_func
lambda函数
???????????????????
深浅拷贝
在浅拷贝时,拷贝出来的新对象的地址和原对象是不一样的,但是新对象里面的可变元素(如列表)的地址和原对象里的可变元素的地址是相同的,也就是说浅拷贝它拷贝的是浅层次的数据结构(不可变元素),对象里的可变元素作为深层次的数据结构并没有被拷贝到新地址里面去,而是和原对象里的可变元素指向同一个地址,所以在新对象或原对象里对这个可变元素做修改时,两个对象是同时改变的,但是深拷贝不会这样,这个是浅拷贝相对于深拷贝最根本的区别。
也就是说:在做浅拷贝时,原来变量里的可变元素和新变量里的可变元素对应同一块地址,不可变元素则开辟了新的地址,两者从此阴阳两隔再无关系。在深拷贝时,会只拷贝所有元素的值,包括可变对象,也仅拷贝对象中的值,而不是地址。
import copy
a = [1,2,3,4,['a','b','c']]
b = a # 赋值(引用传递)
c = copy.copy(a)# 浅拷贝
d = copy.deepcopy(a) # 深拷贝
a.append(5)
a[4].append('d')
print("a:{}, id(a):{}, id(a[4]):{}".format(a,id(a),id(a[4])))
print("b:{}, id(b):{}, id(b[4]):{}".format(b,id(b),id(b[4])))
print("c:{}, id(c):{}, id(c[4]):{}".format(c,id(c),id(c[4])))
print("d:{}, id(d):{}, id(d[4]):{}".format(d,id(d),id(d[4])))
# 输出为:
# a:[1, 2, 3, 4, ['a', 'b', 'c', 'd'], 5], id(a):1998224934024, id(a[4]):1998224933896
# b:[1, 2, 3, 4, ['a', 'b', 'c', 'd'], 5], id(b):1998224934024, id(b[4]):1998224933896
# c:[1, 2, 3, 4, ['a', 'b', 'c', 'd']], id(c):1998224936904, id(c[4]):1998224933896
# d:[1, 2, 3, 4, ['a', 'b', 'c']], id(d):1998224935752, id(d[4]):1998224957960
*args和**kwargs
*args 表示传一个元组给函数,可以同时传递多个参数
**kwargs 表示传一个字典给函数,可以传多个键值对
个人理解:只要是在调用函数时传递的键值对都会给kwargs这个参数,其余类型的参数都给args
def fun(*args, **kwargs):
print("args: ", args)
print("kwargs: ", kwargs)
fun("hello", 2222, [1, 2, 3], (1, 2, 3), 0.5, a=1, b=2, c=3)
# 输出为:
# args: ('hello', 2222, [1, 2, 3], (1, 2, 3), 0.5)
# kwargs: {'a': 1, 'b': 2, 'c': 3}
单例模式
1:装饰器实现:
def single_instance(cls):
_classes = {}
def wrap(*args, **kwargs):
if not _classes :
# 把类作为关键字,类对象作为值,传递给字典,下次直接看列表中有没有这个类的对象,如果有直接返回类对象,否则新建类对象
_classes [cls] = cls(*args, **kwargs)
return _classes [cls]
return _classes [cls]
return wrap
@single_instance
class Person():
def jump(self):
print('***')
2:__new__方法实现:
当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.new),实例化对象(在内存开辟地址);然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1)
print(obj2)
is和==的区别
is 是对地址(指针)的比较
== 是对值得比较
- 对于Python命令行,对于小整数【-5,256】Python会创建一个对象池,所以变量的值在池中时,不会开辟地址,而是直接引用。所以 is 和 “==”都是True。其他范围内的数值,Python不会提供对象池,创建变量的时候会开辟地址,此时即使变量的值一样,其地址也是不同的。
- 对于pycharm:所有的数值变量(整数、小数),只要值相同,地址就相同。对于字符串,只要内容相同,地址就相同。
a = [1,2,3]
b = [1,2,3]
a == b #True
a is b #False
在Python中,a += X和a = a + x的实现机制是不同的,这里分为四种情况。
可变类型(列表、字典)
a += x:将会在a原地址上进行修改,a的地址不变
a = a + x:将会新创建一个对象,名称为a,但是地址与原来的地址不同。
不可变类型
a += x:将会新创建一个对象,名称为a,但是地址与原来的地址不同。
a = a + x:将会新创建一个对象,名称为a,但是地址与原来的地址不同。
a = [1, 2, 3]
print(id(a)) # 1949586175872
a += [4]
print(id(a)) # 1949586175872 地址不变
# *****************************
b = [1, 2, 3]
print(id(b)) # 1949586176384
b = b + [4]
print(id(b)) # 1949588476352 地址变了
# *****************************
c = 2
print(id(c)) # 1949584419152
c += 2
print(id(c)) # 1949584419216 地址变了
# *****************************
d = 2
print(id(d)) # 1949584419152
d = d + 2
print(id(d)) # 1949584419216 地址变了
'''
突然发现,这里的c和d的地址是一样的,是因为 2(不可变类型)是只有一个地址?,相当于把 2 的地址给了c d ???8.10日解答:因为Python有个小整数的内存池,好像是-3到255的整数都是公用一个地址,此时Python会记录每个小整数被引用的次数。
但是,对于可变类型,都是从内存重新开辟地址。
a = [1,2,3]
b = [1,2,3]
id(a) >>>>>> 1413779944704
id(b) >>>>>> 1413779283136
'''
with方法内部原理
避免转义字符
在字符串前面加r
zip
zip()函数接收两个可迭代对象,将对应位置的对象组合成一个元组。最后返回一个zip对象res。通过list(res),将结果解码成list。也可以用[i for i in res]获取。当参数长度不一致时,以较短的对象做截取。
x ='abc'
y = 'defgh'
c = zip(x,y)
list(c) # [('a', 'd'), ('b', 'e'), ('c', 'f')]
map
map(func, iter)函数接收一个函数和一个可迭代对象,将可迭代对象的值挨个传入func进行计算.返回一个map对象
a = [1,2,3,4]
m = map(lambda x:x**2,a)
list(m)
# [1,4,9,16]
简述mysql和redis区别
redis: 内存型非关系数据库,数据保存在内存中,速度快
mysql:关系型数据库,数据保存在磁盘中,检索的话,会有一定的Io操作,访问速度相对慢
单例设计
def single_instance(cls):
_instance = {}
def wrap(*args, **kwargs):
# global instance
if not _instance:
# 字典,key是类,值是实例化对象
_instance[cls] = cls(*args, **kwargs)
return _instance[cls]
return wrap
@single_instance
class P(object):
def __init__(self, name, age):
self.name = name
self.age = age
p1 = P('tom', 16)
p2 = P('sam', 18)
print(id(p1), p1.name)
print(id(p2), p2.name)
#输出tom 2904473974864 ***** tom 2904473974864
'''这里再实例化对象时,直接返回了第一次创建的对象,第二次的属性不会覆盖第一次的属性
也就是说:__init__只走一次
'''
****************************************************************************************
class T(object):
_instance = False
def __init__(self, name, age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = True
cls.obj = object.__new__(cls)
# return cls.obj
return cls.obj
t1 = T('sam', 18)
t2 = T('aaa', 19)
print(t1.name, id(t1), t2.name, id(t2))
#输出aaa 2904473974864 ***** aaa 2904473974864
'''这里实例化对象时,第二次实例化对象,将会改变第一次实例化对象的属性,他们是同一个对象,内存地址相同
也就是说:每次都会走__init__初始化对象。
'''
****************************************************************************************
#方法三,通过@classmethod实现
class M(object):
def __init__(self,name):
self.name = name
@classmethod
def create_instance(cls,name):
if not hasattr(cls,'instance'):
cls.instance = cls(name=name)
return cls.instance
m1 = M.create_instance('tom')
m2 = M.create_instance('sam')
print(m1.name,id(m1),'*****',m2.name,id(m2))
#输出tom 2904473974864 ***** tom 2904473974864
'''这里也只会走一次__init__(self,name)'''
这是一个神奇的东西
好像是说dic是一个可变数据类型,所以会追加输出。但是为什么不是每次都初始化dic了呢????????????
def fn(k,v,dic={}):
dic[k] = v
print(dic)
fn("one",1)
fn('two',2)
fn('three',3,{})
'''
结果:
{'one': 1}
{'one': 1, 'two': 2}
{'three': 3}
'''
分别从前端、后端、数据库阐述web项目的性能优化
该题目网上有很多方法,我不想截图网上的长串文字,看的头疼,按我自己的理解说几点
前端优化:
1、减少http请求、例如制作精灵图
2、html和CSS放在页面上部,javascript放在页面下面,因为js加载比HTML和Css加载慢,所以要优先加载html和css,以防页面显示不全,性能差,也影响用户体验差
后端优化:
1、缓存存储读写次数高,变化少的数据,比如网站首页的信息、商品的信息等。应用程序读取数据时,一般是先从缓存中读取,如果读取不到或数据已失效,再访问磁盘数据库,并将数据再次写入缓存。
2、异步方式,如果有耗时操作,可以采用异步,比如celery(这是啥???)
3、代码优化,避免循环和判断次数太多,如果多个if else判断,优先判断最有可能先发生的情况
数据库优化:
1、如有条件,数据可以存放于redis,读取速度快
2、建立索引、外键等
简述多线程、多进程
进程:(一个进程一个GIL,线程需要争夺这个GIL才可以运行)
1、操作系统进行资源分配和调度的基本单位,多个进程之间相互独立
2、稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制
线程:
1、CPU进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的所有资源
2、如果IO操作密集,则可以多线程运行效率高,缺点是如果一个线程崩溃,都会造成进程的崩溃
应用:(IO密集型计算用多线程,计算密集型用多进程)
IO密集的用多线程,在用户输入,sleep 时候,可以切换到其他线程执行,减少等待的时间
CPU密集的用多进程,因为假如IO操作少,用多线程的话,因为线程共享一个全局解释器锁,当前运行的线程会霸占GIL,其他线程没有GIL,就不能充分利用多核CPU的优势