面试题总结
- 一、python
- 1 python常用的魔术方法有哪些,其中__init__和__new__有什么区别?
- 2 python装饰器是什么?装饰器的应用场景
- 3 python的垃圾回收机制
- 4 python常用的数据类型,哪些是可变的,哪些是不可变的
- 5 python常见的算法你会吗?
- 6 python单例是什么?
- 7 常见的http状态码
- 8 cookie和session的概念及区别?
- 9 lambda函数是什么?为什么会用到及其用在哪里
- 10 django问题汇总
- 11 谈下 python 的 GIL(全局解释器锁)
- 12 python两个数值互换(浅析a,b=b,a原理)
- 13 with上下文机制原理
- 14 Python 装饰器的用法
- 15 python 协程
- 16 python线程死锁原理
- 17 python 字典 元组 列表底层原理
- 18 python 中的self和cls的区别
- 19 进程线程协程概念
- 20 多进程多线程的应用
- 21 Python类的继承顺序
- 22 python生成器和迭代器
- 23 python *args和**kwargs的区别
- 24 flask上下文
- 25 深拷贝与浅拷贝
- 二、mysql
- 三、linux相关
一、python
1 python常用的魔术方法有哪些,其中__init__和__new__有什么区别?
__del__方法
当一个对象在内存中被销毁的时候自动执行,比如当程序运行结束时,没有再引用的变量时,__del__会生效,需要至少有一个self参数,用于接收对象
# -*- coding:utf8 -*-
class Interview(object):
def __init__(self):
print('初始化实例')
def __del__(self):
print('程序运行结束时,且对象已经没有被引用时调用此方法')
interview = Interview()
view = interview
print('程序结束')
执行结果
初始化实例
程序结束
对象已经没有被调用,并进行销毁
__call__方法
可以让类的实例具有类似于函数的行为,进一步模糊了函数和对象之间的概念。
# -*- coding:utf8 -*-
class CallMethod(object):
def __call__(self, *args, **kwargs):
print('call方法可以让类的实例以函数的形式进行调用')
c = CallMethod() # 将Person()的内存地址赋值给person
c() # 内存地址()
CallMethod()() #
# 使用方式:对象后面加括号,触发执行。即:对象() 或者 类()()
执行结果
call方法可以让类的实例以函数的形式进行调用
call方法可以让类的实例以函数的形式进行调用
__init__方法
1 __init__的作用是初始化实例后的对象
2 __new__负责创建类的实例对象,__init__负责对__new__创建的对象进行初始化,二者都由Python解释器自动调用。
# -*- coding:utf8 -*-
class Aaa(object):
def __init__(self,name):
self.name = name
def __str__(self):
return self.name
a = Aaa('tom')
print(a)
__new__方法
1 __new__方法是类在实例化过程中调用的一个方法,该方法还要在__init__方法之前调用,用于创建类的实例化对象
2 在基础类object中,new被定义成了一个静态方法,并且需要传递一个参数cls。Cls表示需要实例化的类,此参数在实例化时由Python解析器自动提供。
3 在init()调用之前,new()决定是否要使用该init()方法,因为new()可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。
class Person(object):
# 初始化
def __init__(self):
print('init...')
# 实例化方法(构造方法)---》创建对象
def __new__(cls, *args, **kwargs):
print('new...')
ret = super().__new__(cls) # 调用父类的__new__()方法创建对象,并用接收返回值
return ret # 将对象返回给person
person = Person()
print(person)
结果演示
new...
init...
<__main__.Person object at 0x00000000021CC860>
__str__方法
如果类定义了__str__方法,那么会打印从这个方法中 return 的数据,
__str__方法通常返回一个字符串,作为这个对象的描述信息。
# -*- coding:utf8 -*-
class Aaa(object):
def __init__(self,name):
self.name = name
def __str__(self):
return self.name
a = Aaa('tom')
print(a)
2 python装饰器是什么?装饰器的应用场景
什么是装饰器,参数作用
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
(1)无参数
# -*- coding:utf8 -*-
def debug(func):
def wrapper():
print "[DEBUG]: enter {}()".format(func.__name__)
return func()
return wrapper
@debug
def say_hello():
print "hello!"
say_hello()
运行结果
[DEBUG]: enter say_hello()
hello!
有参数
# -*- coding:utf8 -*-
#带有参数的装饰器
import time
def deco(func):
def wrapper(*args,**kwargs):
startTime = time.time()
func(*args,**kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper
@deco
def func(a,b):
print("hello,here is a func for add :")
time.sleep(1)
print("result is %d" %(a+b))
if __name__ == '__main__':
f = func
f(3,4)
#func()
执行结果
hello,here is a func for add :
result is 7
time is 1001 ms
外层带参数的装饰器
# -*- coding:utf-8 -*-
# 1、带参数的装饰器
def wrapper_out(parameter):
print(parameter)
def wrapper(func):
def inner(*args, **kwargs):
ret = func(*args, **kwargs)
return ret
return inner
return wrapper
@wrapper_out('企鹅')
def qq():
print('成功登录企鹅!')
qq()
装饰器的应用场景
(1) 引入日志
(2) 函数执行时间统计
(3) 执行函数前预备处理
(4) 执行函数后的清理功能
(5) 权限校验等场景
3 python的垃圾回收机制
参考链接: https://blog.csdn.net/wxfghy/article/details/80901488
Python主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
(1) 引用计数
当对象没有被引用当时候,引用值为0,对象被销毁
(2) 标记清除
将被引用的有效的对象进行标记,没有被引用的资源被回收
(3) 分代回收
对象存在时间越长,越可能不是垃圾,资源不应该被回收
4 python常用的数据类型,哪些是可变的,哪些是不可变的
(1)数据类型
整数int
字符串str
浮点数float
布尔型bool
列表list
字典dict
集合set
元组tuple
(2)可变数据类型
可变类型:就是这个数据类型的值在不改变这一块内存空间,而去改变这个数据类型的值。
1 列表
2 字典
3 集合
不可以作为字典的键值,因为不可以哈希
(3)不可变数据类型
不可变类型:当改变值得时候,会申请一块新的内存空间,不再是原来的那一块内存空间了。
1 整数
2 字符串
3 浮点数
4 布尔型
5 元组
5 python常见的算法你会吗?
1 冒泡排序
(1) 冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
(2) 冒泡排序算法的运作如下:
比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
# -*- coding:utf8 -*-
def bubble_sort(li):
for i in range(len(li),0,-1):
for j in range(i-1):
if li[j] > li[j+1]:
li[j],li[j + 1]=li[j+1],li[j]
# print('-'*10)
li = [54,26,93,17,77,31,44,55,20]
bubble_sort(li)
print(li)
结果
[17, 20, 26, 31, 44, 54, 55, 77, 93]
2 选择排序
(1) 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
(2) 选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
# -*- coding:utf8 -*-
def selection_sort(alist):
n = len(alist)
# 需要进行n-1次选择操作
for i in range(n - 1):
# 记录最小位置
min_index = i
# 从i+1位置到末尾选择出最小数据
for j in range(i + 1, n):
if alist[j] < alist[min_index]:
min_index = j
# 如果选择出的数据不在正确位置,进行交换
if min_index != i:
alist[i], alist[min_index] = alist[min_index], alist[i]
alist = [54, 226, 93, 17, 77, 31, 44, 55, 20]
selection_sort(alist)
print(alist)
结果
[17, 20, 31, 44, 54, 55, 77, 93, 226]
选择排序总结:
选择排序的思路是固定位置,选择排序,即:先从序列中,找到最小的元素,放到第一个位置,然后找到第二小的元素,放到第二个位置,以此类推,直到排好所有的值。
3 插入排序
(1) 插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
# -*- coding:utf8 -*-
def insert_sort(alist):
# 从第二个位置,即下标为1的元素开始向前插入
for i in range(1, len(alist)):
# 从第i个元素开始向前比较,如果小于前一个元素,交换位置
for j in range(i, 0, -1):
if alist[j] < alist[j - 1]:
alist[j], alist[j - 1] = alist[j - 1], alist[j]
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
insert_sort(alist)
print(alist)
结果
[17, 20, 26, 31, 44, 54, 55, 77, 93]
4 快速排序
(1) 快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
步骤为:
1 从数列中挑出一个元素,称为"基准"(pivot),
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
def quick_sort(alist, start, end):
# 递归的退出条件
if start >= end:
return
# 设定起始元素为要寻找位置的基准元素
mid = alist[start]
# low为序列左边的由左向右移动的游标
low = start
# high为序列右边的由右向左移动的游标
high = end
while low < high:
# 如果low与high未重合,high指向的元素不比基准元素小,则high向左移动
while low < high and alist[high] >= mid:
high -= 1
# 将high指向的元素放到low的位置上
alist[low] = alist[high]
# 如果low与high未重合,low指向的元素比基准元素小,则low向右移动
while low < high and alist[low] < mid:
low += 1
# 将low指向的元素放到high的位置上
alist[high] = alist[low]
# 退出循环后,low与high重合,此时所指位置为基准元素的正确位置
# 将基准元素放到该位置
alist[low] = mid
# 对基准元素左边的子序列进行快速排序
quick_sort(alist, start, low - 1)
# 对基准元素右边的子序列进行快速排序
quick_sort(alist, low + 1, end)
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
quick_sort(alist, 0, len(alist) - 1)
print(alist)
结果
[17, 20, 26, 31, 44, 54, 55, 77, 93]
6 python单例是什么?
(1)我们知道__new__方法是类在实例化过程中调用的一个方法,该方法还要在__init__方法之前调用,用于创建类的实例化对象。那么我们是不是可以重写__new__ ,让他判断是否已经调用过该方法了,如果已经调用过了,直接返回上次实例化的对象,那么该类不就一直只存在一个实例化对象了吗,就实现了单例。
# 实例化一个单例
class Singleton(object):
__instance = None
def __new__(cls, age, name):
#如果类数字__instance没有或者没有赋值
#那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时
#能够知道之前已经创建过对象了,这样就保证了只有1个对象
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
a = Singleton(18, "xxx")
b = Singleton(8, "xxx")
print(id(a))
print(id(b))
a.age = 19 #给a指向的对象添加一个属性
print(b.age)#获取b指向的对象的age属性
运行结果:
4391023224
4391023224
19
扩展知识点 : hasattr
hasattr(object, "name")
判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。
需要注意的是name要用括号括起来
>>> class test():
... name="xiaohua"
... def run(self):
... return "HelloWord"
...
>>> t=test()
>>> hasattr(t, "name") #判断对象有name属性
True
>>> hasattr(t, "run") #判断对象有run方法
True
(2)单例模式就是确保一个类只有一个实例.当你希望整个系统中,某个类只有一个实例时,单例模式就派上了用场.
比如,某个服务器的配置信息存在在一个文件中,客户端通过AppConfig类来读取配置文件的信息.如果程序的运行的过程中,很多地方都会用到配置文件信息,则就需要创建很多的AppConfig实例,这样就导致内存中有很多AppConfig对象的实例,造成资源的浪费.其实这个时候AppConfig我们希望它只有一份,就可以使用单例模式.
基于__new__方法实现的单例模式(推荐使用,方便)
知识点:
1> 一个对象的实例化过程是先执行类的__new__方法,如果我们没有写,默认会调用object的__new__方法,返回一个实例化对象,然后再调用__init__方法,对这个对象进行初始化,我们可以根据这个实现单例.
2> 在一个类的__new__方法中先判断是不是存在实例,如果存在实例,就直接返回,如果不存在实例就创建.
# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/3/6 13:36'
import threading
class Singleton(object):
_instance_lock = threading.Lock()
def __init__(self, *args, **kwargs):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
with Singleton._instance_lock:
if not hasattr(cls, '_instance'):
Singleton._instance = super().__new__(cls)
return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1, obj2)
def task(arg):
obj = Singleton()
print(obj)
for i in range(10):
t = threading.Thread(target=task, args=[i, ])
t.start()
7 常见的http状态码
状态码 | 类别 | 原因短语 |
---|---|---|
1xx | Informational(信息性状态码) | 接受的请求正在处理 |
2xx | Success(成功状态码) | 请求正常处理完毕 |
3xx | Redirection(重定向) | 需要进行附加操作以完成请求 |
4xx | Client error(客户端错误) | 客户端请求出错,服务器无法处理请求 |
5xx | Server Error(服务器错误) | 服务器处理请求出错 |
200 OK:表示从客户端发送给服务器的请求被正常处理并返回
301 Moved Permanently:永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL
302 Found:临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL
403 Forbidden:服务器拒绝该次访问(访问权限出现问题)
404 Not Found:表示服务器上无法找到请求的资源,除此之外,也可以在服务器拒绝请求但不想给拒绝原因时使用
500 Inter Server Error:表示服务器在执行请求时发生了错误,也有可能是web应用存在的bug或某些临时的错误时
503 Server Unavailable:表示服务器暂时处于超负载或正在进行停机维护,无法处理请求;
8 cookie和session的概念及区别?
cookie的概念:
(1)HTTP 协议是无状态的,为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP 引入 Cookie 来保存状态信息。
(2)Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。
session的概念:
(1)Session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而Session 保存在服务器上。
(2)客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是 Session 。客户端浏览器再次访问时只需要从该 Session中查找该客户的状态就可以了。
cookie与session之间的区别
(1)cookie数据存放在客户的浏览器上,session数据放在服务器上
(2)cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session
(3)session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
(4)单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。
(5)所以:将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中
9 lambda函数是什么?为什么会用到及其用在哪里
什么是lambda?
lambda是Python中预留的一个关键词,当我们需要一个函数,而又不想重新去为函数命名时,我们就可以使用lambda关键词来简单的去定义一个函数,也称为匿名函数。
lambda优点:
(1)代码简洁
(2)不增加额外变量
lambda缺点
lambda 表达式只能包含一条语句, 因此某些提高可读性的语言功能, 如元组拆包, 不能与它们一起使用
lambda使用场景
(1)命名使用
如果我们只需要一个简单的函数,lambda 是一个很好的选择,因为它可以被看作是定义函数的一种更简单的方法。因此,我们可以给它一个名称,并像普通函数一样使用它。
(2)配合高阶函数使用
如果我们可以将 lambda 函数与 map()、 filter()和 reduce()和sorted()等高阶函数一起使用。
10 django问题汇总
1 django请求的生命周期?
(1) 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端,请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中.
(2) url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配,
一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了.
(3)视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端.
(4)客户端浏览器接收到返回的数据,经过渲染后显示给用户.
2django中session是怎么使用的?
3 django 中 ORM一对一、多对多,一对多查询操作
https://blog.csdn.net/wang_ze6456/article/details/119450784
11 谈下 python 的 GIL(全局解释器锁)
GIL:又称全局解释器锁。作用就是限制多线程同时执行,保证同一时间内只有一个线程在执行。线程非独立的,所以同一进程里线程是数据共享,当各个线程访问数据资源时会出现“竞争”状态,即数据可能会同时被多个线程占用,造成数据混乱,这就是线程的不安全。所以引进了互斥锁,确保某段关键代码、共享数据只能由一个线程从头到尾完整地执行。
GIL 是 python 的全局解释器锁,同一进程中假如有多个线程运行,一 个线程在运行 python 程序的时候会霸占 python 解释器(加了一把锁 即 GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线 程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使 其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不 是同时进行。
多进程中因为每个进程都能被系统分配资源,相当于每个进程有了一个 python 解释器,所以多进程可以实现多个进程的同时运行,缺点是进 程系统资源开销大
12 python两个数值互换(浅析a,b=b,a原理)
参考链接:https://blog.csdn.net/daydream9217/article/details/84859918
# a,b交换变量值是同时发生的,不是a先引用了b的变量,然后b引用了a的变量
a = 1
b = 2
a,b = b,a
print(a)
2
print(b)
1
13 with上下文机制原理
参考1
简单来说,就是在一个类里,实现了__enter__和__exit__的方法,这个类的实例就是一个上下文管理器.
(1)__ enter __ : 进入对象的上下文管理器调用的方法,会返回一个值,并赋值给as关键词之后的变量
(2)__ exit __:退出对象的上下文管理器调用的方法,定义了处理结束后要做的事情,比如文件的关闭,socket的断开等
参考2
(1)上下文管理器中有 __ enter __ 和 __ exit __ 两个方法,以with为例子,__ enter __ 方法会在执行 with 后面的语句时执行,一般用来处理操作前的内容。比如一些创建对象,初始化等;__ exit __ 方法会在 with 内的代码执行完毕后执行,一般用来处理一些善后收尾工作,比如文件的关闭,数据库的关闭等。
(2)使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须收到干预
14 Python 装饰器的用法
场景:有一个函数特别慢,已知函数没有副作用,现在模拟这个场景并优化。
模拟如下
def add(a, b):
time.sleep(10)
return a + b
现在要求添加装饰器 cache
实现:
add(1, 2) # -> 3, 耗时 10s
add(3, 4) # -> 7, 耗时 10s
add(1, 2) # -> 3, 重复请求,基本不消耗时间
代码实现
import time
def cache(func):
result = {}
print(result)
def wrapper(*args):
print(args)
if args in result:
return result[args]
else:
res = func(*args)
result[args] = res
return res
return wrapper
@cache
def add(a, b):
time.sleep(10)
return a + b
print(add(1, 2)) # -> 3, 耗时 10s
print(add(3, 4)) # -> 7, 耗时 10s
print(add(1, 2)) # -> 3, 重复请求,基本不消耗时间
15 python 协程
为什么使用协程?
当多线程或者多进程足够多时,实际上并不能解决性能的瓶颈问题,也就是多线程和多进程对小规模的请求可以提高效率,过多的请求实际上会降低服务资源响应效率,因此协程是更好的解决文案。
什么是协程?
当一个程序遇到阻塞时,如果将这个程序挂起,然后将它的cpu权限拿出来去执行我们的其他程序,执行完后再回过头来执行这些挂起的程序,此时所有非阻塞操作已经执行完毕,最后在一起执行阻塞程序,是不是相当于做了异步。
因此,协程的作用就是检测阻塞的程序,在单进程和单线程的情况下实现异步,相比多线程和多进程效率更高。
参考链接:协程
16 python线程死锁原理
死锁的原理非常简单,用一句话就可以描述完。就是当多线程访问多个锁的时候,不同的锁被不同的线程持有,它们都在等待其他线程释放出锁来,于是便陷入了永久等待。比如A线程持有1号锁,等待2号锁,B线程持有2号锁等待1号锁,那么它们永远也等不到执行的那天,这种情况就叫做死锁。
17 python 字典 元组 列表底层原理
https://www.jb51.net/article/207224.htm
18 python 中的self和cls的区别
self是类(Class)实例化对象
cls就是类(或子类)本身,取决于调用的是那个类。
一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁。
首先定义一个类A,类A中有三个函数,foo1为静态函数,用@staticmethod装饰器装饰,这种方法与类有某种关系但不需要使用到实例或者类来参与。
cls和self的区别(Python)
先看以下代码:
class A(object):
a = 'a'
@staticmethod
def fool1(name):
print(name)
def fool2(self, name):
print(name)
@classmethod
def fool3(cls, name):
print(name)
首先定义了一个类A,类A中有3个函数,fool1是静态函数,用@staticmethod装饰器装饰,既可以有类名调用,也可以用类的实例调用:
a = A()
a.foo1('mamq') # 输出: hello mamq
A.foo1('mamq') # 输出: hello mamq
fool2是正常函数,是类的实例的函数,只能通过实例a调用:
a.foo2('mamq') # 输出: hello mamq
A.foo2('mamq') # 报错: unbound method foo2() must be called with A instance as first argument (got str instance instead)
fool3是类函数,cls作为第一个参数用来表示类本身,类方法只与类本身有关而与实例无关,如下两种方式都可以正常输出:
a.foo3('mamq') # 输出: hello mamq
A.foo3('mamq') # 输出: hello mamq
大家可以发现@staticmethod和@classmeithod的使用方法和输出完全相同,但两者仍存在区别。
classmethod中可以调用类中定义的其他方法、类的属性,但staticmethod只能通过A.a调用类的属性,但无法通过在该函数内部调用A.foo2()
class A(object):
a = 'a'
@staticmethod
def foo1(name):
print 'hello', name
print A.a # 正常
print A.foo2('mamq') # 报错: unbound method foo2() must be called with A instance as first argument (got str instance instead)
def foo2(self, name):
print 'hello', name
@classmethod
def foo3(cls, name):
print 'hello', name
print A.a
print cls().foo2(name)
19 进程线程协程概念
CPU密集型
CPU密集型也叫计算密集型,计算密集型任务的特点是要进行大量的计算,消耗CPU资源,CPU占用率接近100%,比如计算圆周率。
IO密集型
IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。
对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,
进程
(1)进程是系统进行资源分配和调度的基本单位
(2)进程是指计算机中已运行的、并被分配了CPU、内存资源的程序
线程
(1)线程是操作系统中能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
(2)线程是一个基本的 CPU 执行单元。它必须依托于进程存活。所有的线程运行在同一个进程中,共享相同的运行环境
(3)一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯。
(4)所有线程共享进程的内存
协程
当一个程序遇到阻塞时,如果将这个程序挂起,然后将它的cpu权限拿出来去执行我们的其他程序,执行完后再回过头来执行这些挂起的程序,此时所有非阻塞操作已经执行完毕,最后在一起执行阻塞程序,是不是相当于做了异步。
因此,协程的作用就是检测阻塞的程序,在单进程和单线程的情况下实现异步,相比多线程和多进程效率更高。
即:多进程比较适用于CPU密集型的任务,线程和协程比较适用于IO密集型的任务
20 多进程多线程的应用
pass
21 Python类的继承顺序
从下到上搜索
# -*- coding:utf8 -*-
class Animal:
def eat(self):
print("吃")
def drink(self):
print("喝")
class Dog(Animal):
def eat(self):
print("Dog 吃")
def dark(self):
print("汪汪叫")
class Hello(Dog):
def run(self):
print('123')
goudan = Hello()
goudan.eat()
goudan.drink()
输出结果
Dog 吃
喝
22 python生成器和迭代器
什么是迭代器?
(1)迭代器可以看作是一个特殊的对象,每次调用该对象时会返回自身的下一个元素
(2)迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址)
(3)可循环遍历的是迭代器
什么是生成器?
(1)在 Python 中,使用了 yield 的函数被称为生成器(generator),生成器就是一个特殊的迭代器
(2)每次执行到有yield的时候,会返回yield后面的值,并且函数会暂停,直到下次调用才继续执行函数。最后迭代终止。
使用生成器的好处:
(1)节省内存空间
(2)提升代码运行速度
生成器和迭代器的区别
对于调用一个普通的Python函数,一般是从函数的第一行代码开始执行,结束于return语句、异常或者函数所有语句执行完毕。一旦函数将控制权交还给调用者,就意味着全部结束。函数中做的所有工作以及保存在局部变量中的数据都将丢失。再次调用这个函数时,一切都将从头创建。Python是通过生成器来实现类似于协同程序的概念:生成器可以暂时挂起函数,并保留函数的局部变量等数据,然后在再次调用它的时候,从上次暂停的位置继续执行下去。
23 python *args和**kwargs的区别
*args 表示任意个参数,将参数打包成tuple给函数体调用
**kwargs 表示任意个参数,将参数打包成dict给函数体调用
传入参数的区别:
*args 实际是一个tuple元素
**kwargs 实际上是k=v 形式的键值对
def test(*args,**kwargs):
print (args)
print (kwargs)
test(1, 2, 3, **{'a': 1})
test(1, 2, 3, a=1)
结果展示
(1, 2, 3)
{'a': 1}
(1, 2, 3)
{'a': 1}
24 flask上下文
flask上下文
https://blog.csdn.net/sunt2018/article/details/107853784
上下文详细
https://www.cnblogs.com/shijieli/p/10355856.html
25 深拷贝与浅拷贝
https://www.jianshu.com/p/895e06c0711f
二、mysql
1 mysql多表查询是怎么进行查询的
参考链接:
https://blog.csdn.net/wang_ze6456/article/details/119523192
2 mysql的索引有了解过吗?什么是索引,什么字段适合做索引,索引的优缺点?
(1)脚本之家:索引解释
(2) CSDN:索引解释
根据索引的具体用途,MySQL 中的索引在逻辑上分为以下 5 类:
1) 普通索引
普通索引是最基本的索引类型,唯一任务是加快对数据的访问速度,没有任何限制。创建普通索引时,通常使用的关键字是 INDEX 或 KEY。
2) 唯一性索引
唯一性索引是不允许索引列具有相同索引值的索引。如果能确定某个数据列只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字 UNIQUE 把它定义为一个唯一性索引。
创建唯一性索引的目的往往不是为了提高访问速度,而是为了避免数据出现重复。
3) 主键索引
主键索引是一种唯一性索引,即不允许值重复或者值为空,并且每个表只能有一个主键。主键可以在创建表的时候指定,也可以通过修改表的方式添加,必须指定关键字 PRIMARY KEY。
注意:主键是数据库考察的重点。注意每个表只能有一个主键。
4) 空间索引
空间索引主要用于地理空间数据类型 GEOMETRY。
5) 全文索引
全文索引只能在 VARCHAR 或 TEXT 类型的列上创建,并且只能在 MyISAM 表中创建。
3 mysql优化
如何优化MySQL的查询性能?
优化MySQL查询性能的方法包括:
设计合适的表结构,减少冗余字段和表;
选择合适的数据类型;
创建索引,包括优化索引类型、覆盖索引和联合索引等;
避免使用SELECT *,只选取需要的字段;
缓存数据,使用缓存机制,如Memcached等;
优化SQL语句,如避免子查询和使用合适的JOIN;
使用分区表,将大表分解为小表。
一般就是对常用的几个字段建立索引 。 然后就是避免select * 最好只select需要查询的字段 , 。然后就是避免子查询 。 如果使用inner join和left或者right join结果一致 就是用inner join 也就是正确使用join的意思
还有就是表关联的时候
一般情况下,应该将小表作为驱动表(即在前面进行查询),大表作为被驱动表(即在后面进行查询)。这是因为,驱动表的数据量相对较小,可以快速查找到符合条件的记录,然后再通过索引快速定位到大表中需要的数据,这样可以提高查询效率。
但是,也要根据具体的业务场景来决定。如果驱动表中的记录数量非常少,而且被驱动表的查询效率很高,则可以将大表作为驱动表,小表作为被驱动表。在实际的应用中,可以通过对SQL语句的执行计划进行分析,来确定哪个表应该作为驱动表。
意思就是小表join大表,因为小表在前 小表的条数少 查询比较快,如果大表在前 数量要匹配很多次才能查询完
三、linux相关
linux中空的目录及空文件占用大小是多少?
空目录大小为4kb,空文件大小为0
为什么空目录大小为4kb?
因为目录是包含关系所以不可能为0,4k是文件系统的块大小
为什么空文件大小为0
因为在创建的时候无法确定文件大小 ,所以全用0填充