python django及运维开发面试题总结

面试题总结

一、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 集合

不可以作为字典的键值,因为不可以哈希

浅谈 Python 中为何无法使用列表作为字典键值

(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状态码

状态码类别原因短语
1xxInformational(信息性状态码)接受的请求正在处理
2xxSuccess(成功状态码)请求正常处理完毕
3xxRedirection(重定向)需要进行附加操作以完成请求
4xxClient error(客户端错误)客户端请求出错,服务器无法处理请求
5xxServer 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填充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值