锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

校招总结--建议全文背诵

时间:2023-02-12 07:00:00 折合式卡连接器5ka5t压力变送器sc无耳连接器

Python基础

1、Python判断对象是否相等(== 和 is)

Python对象包含三个基本要素:id:用来识别一个对象,可以理解为内存地址;type:标识对象的类型;value:对象的值;

== :比较两个对象的内容是否相等,即两个对象是否相等 value 不管等不等 id 默认会调用对象是否相等? eq()方法。
is: 比较的是两个对象是否完全相同,即他们的 id 也就是说,如果是这样的话。 a is b 为 True,那么 a == b 也为True。

2、如何在Python管理内存?

Python内存由Python私有堆空间管理Python对象数据结构它们都位于私人堆空间中。程序员无法访问私人堆空间,解释器负责处理此问题Python私有堆空间。Python内存管理器Python堆空间的分配,核心API允许程序员使用一些工具来编写代码。Python还拥有一个内置的grabage收集器,它回收所有未使用的内存,释放内存,使其可用于堆叠空间。

Python从三个方面可以看出内存管理机制:

(1)垃圾回收

(2)引用计数

(3)内存池机制

一、垃圾回收:

python不像C ,Java和语言一样,他们可以直接赋值变量,而不需要事先声明变量类型。Python在语言方面,运行时确定对象的类型和内存。这就是为什么我们称之为Python语言为动态类型原因(在这里,我们可以简单地将动态类型归因于变量内存地址的分配是在运行过程中自动判断变量类型并赋值变量)。

垃圾回收

1.当内存中有不再使用的部分时,垃圾收集器将清理干净。它会检查引用计数为0的对象,然后清除内存中的空间。当然,除了0引用计外,垃圾收集器还会清除另一种情况:当两个对象相互引用时,其他引用为0。

2.垃圾回收机制还有一个循环垃圾回收器, 确保释放循环引用对象(a引用b, b引用a, 引用计数永远不会为0)。

二、引用计数:

Python采用了类似Windows内核对象以同样的方式管理内存。每个对象都保持指向对象的引用计数。如图所示(图片来自)Python核心编程)

img

x = 3.14

y = x

我们首先创建了一个对象3.14, 然后将浮点对象的引用赋值x,因为x是第一个引用,所以这个浮点对象的引用计数是1. 语句y = x指向同一对象的引用别名y,我们发现Y并没有为Y创建一个新的对象,而是建新的对象,而是引用了2的计数.

我们可以很容易地证明上述观点:

变量a 和 变量b的id一致(我们可以id想象C中变量的指针).

我们援引另一个网站的图片来解释这个问题:对于C语言,当我们创建变量A时,我们将为变量申请内存空间,并将变量值 在这个空间中,当变量被赋予另一个变量B时,它将为B申请一个新的内存空间,并将变量值放入B的内存空间就是为什么A和B指针不一致的原因如图所示:

int A = 1 int A = 2

而Python事实上,情况不同,Python处理方法和Javascript如图所示,变量更像是附着在对象上的标签(类似于引用的定义)。当变量绑定在一个对象上时,变量的引用计数为1(其他情况也会导致变量引用计数的增加),系统会自动维护这些标签并定期扫描。当标签的引用计数变为0时,它将被回收。

a = 1 a = 2 b = a

三、内存池机制

Python金字塔型内存机制-1-2层操作系统进行操作,

C中有第0层malloc,free操作等内存分配和释放函数;

第1层和第2层是内存池,有Python的接口函数PyMem_Malloc当对象小于256K该层直接分配内存;

第三层是最上层,也就是我们对Python对象的直接操作;

在 C 如果调用频繁 malloc 与 free 当时,会出现性能问题.此外,小块内存的频繁分配和释放会产生内存碎片. Python 这里的主要工作有:

  • 请求分配的内存为1~256字节之间使用自己的内存管理系统,否则直接使用 malloc。这里还是会调用的 malloc 分配内存,但每次会分配一块大小为256k的大块内存.

  • 通过内存池注册的内存最终收到内存池,不会调用 C 的 free 释放掉.以便下次使用。对于简单的Python对象,例如数值、字符串,元组(tuple不允许更改)复制(深拷贝)),也就是说,当另一个变量B赋值变量A时,虽然A和B内存空间还是一样的,但是当A值发生变化时,A会重新分配空间,A和B的地址变得不再相同

3、python闭包及装饰

也就是了一个函数A,函数A返回函数B给你。返回函数B称为闭包。自由变量是您在调用函数A时传递的参数。多层函数嵌套,(函数中有定义函数,通常是两个),内层函数通常使用外层函数的变量,内层函数和外部函数的变量作为特殊对象,即封闭包。闭包比面向对象更纯净、更轻,有数据和执行数据代码;比普通函数更强大,不仅有代码,还有数据;
一、封闭应用场景

  1. 统计函数被调用次数

  2. 用作装饰品

def make_averager():     series = [] # series 相对averager函数是全局变量      def averager(new_value):         series.append(new_value) ##此处访问全局变量series         total = sum(series)         return total/len(series)      
       
        return averager avg 
        = make_averager
        (
        ) 使用make_average 函数返回的是averager函数 当这个过程发生后,理论上讲make_averager的任务就完成了,serier变量是应该消失的,此时,如果使用avg会出现报错 但是 
        >>
        > avg
        (
        10
        ) 
        10.0 
        >>
        > avg
        (
        11
        ) 
        10.5 
        >>
        > avg
        (
        12
        ) 
        11.0 说明series的变量依然存在 
       

匿名函数、普通函数、闭包、面向对象的区别?
1). 匿名函数能够完成基本的简单功能,传递是这个函数的引用 只有功能。
2). 普通函数能够完成较为复杂的功能,传递是这个函数的引用 只有功能。
3). 闭包能够将较为复杂的功能,传递是这个闭包中的函数以及数据,占用资源比较小
4). 对象能够完成最为复杂的功能,传递是数据+功能,但占用大量空间,浪费资源。

4、Lambda表达式

calc=lambda x,y:x**y
print(calc(2,3))


sum_lambda = lambda a, b, c: a + b + c


#一般解决方案
li = [1,2,3,4,5,6,7,8,9]
for ind,val in enumerate(li):
    li[ind] = val * val
print(li)
# [1, 4, 9, 16, 25, 36, 49, 64, 81]

# 高级解决方案
li = [1,2,3,4,5,6,7,8,9]
print(list(map(lambda x:x*x,li)))
# [1, 4, 9, 16, 25, 36, 49, 64, 81]



#接受一个list并利用reduce()求积
from functools import reduce
li = [1,2,3,4,5,6,7,8,9]
print(reduce(lambda x,y:x * y,li))
# 结果=1*2*3*4*5*6*7*8*9 = 362880



# 把下面单词以首字母排序
li = ['bad', 'about', 'Zoo', 'Credit']
print(sorted(li, key = lambda x : x[0]))
# 输出['Credit', 'Zoo', 'about', 'bad']
""" 对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。 """

# 假设我们用一组tuple表示学生名字和成绩:

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
# 请用sorted()对上述列表分别按名字排序
print(sorted(L, key = lambda x : x[0]))
# 输出[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]

# 再按成绩从高到低排序
print(sorted(L, key = lambda x : x[1], reverse=True))
# 输出[('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]


result = [li+3 for li in range(4)]
print(result)


a_list=[i*i for i in range(10)]

5、字典和数组

有序字典

  • 遍历字典,返回数据,和定义字典时数据顺序,一致。
  • 按顺序插入,遍历时,按顺序返回。
import collections
x =collections.OrderedDict() #有序字典
x['a'] = 3
x['b'] = 5
x['c'] = 8

>>> x    #输出按输入的顺序输出
OrderedDict([('a', 3), ('b', 5),('c', 8)])

6、生成器和迭代器

迭代器

迭代是Python最强大的功能之一,是访问集合元素的一种方式。

迭代器是一个可以记住遍历的位置的对象。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

迭代器有两个基本的方法:iter()next()

字符串,列表或元组对象都可用于创建迭代器:

>>> list=[1,2,3,4]
>>> it = iter(list)    # 创建迭代器对象
>>> print (next(it))   # 输出迭代器的下一个元素
1
>>> print (next(it))
2
>>>

 
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
for x in it:
    print (x, end=" ")
    
import sys         # 引入 sys 模块
list=[1,2,3,4]
it = iter(list)    # 创建迭代器对象
while True:
    try:
        print (next(it))
    except StopIteration:
        sys.exit()

生成器

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

调用一个生成器函数,返回的是一个迭代器对象。

以下实例使用 yield 实现斐波那契数列:

import sys
 
def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
 
while True:
    try:
        print (next(f), end=" ")
    except StopIteration:
        sys.exit()

类方法和静态方法的区

列表生成式

li2=[i*i for i in range(1,11)]
li=[i*i for i in range(1,11) if i%2==0]
li2=[i+j for i in "ABC" for j in "123"]
li1=[
  [1,2,3],
  [4,5,6],
  [7,8,9]
]
print([item2 for item1 in li1 for item2 in item1 if item2%2==0])

迭代生成式

>>> def square(x) :         # 计算平方数
...     return x ** 2
...
>>> map(square, [1,2,3,4,5])    # 计算列表各个元素的平方
<map object at 0x100d3d550>     # 返回迭代器
>>> list(map(square, [1,2,3,4,5]))   # 使用 list() 转换为列表
[1, 4, 9, 16, 25]
>>> list(map(lambda x: x ** 2, [1, 2, 3, 4, 5]))   # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
>>>

7、元组和列表的区别、底层实现

元组和列表有哪些区别呢?

一个可以修改(列表),一个不可以修改(元组)

列表和元组的底层实现
  • 元组和列表同属序列类型,且都可以按照特定顺序存放一组数据,数据类型不受限制,只要是 Python 支持的数据类型就可以。
元组和列表有哪些区别呢?
  • 元组和列表最大的区别就是,列表中的元素可以进行任意修改,就好比是用铅笔在纸上写的字,写错了还可以擦除重写;而元组中的元素无法修改,除非将元组整体替换掉,就好比是用圆珠笔写的字,写了就擦不掉了,除非换一张纸。可以理解为,tuple 元组是一个只读版本的 list 列表。

需要注意的是,这样的差异势必会影响两者的存储方式,我们来直接看下面的例子:

listdemo = []
listdemo.__sizeof__()
40
tupleDemo = ()
tupleDemo.__sizeof__()
24

可以看到,对于列表和元组来说,虽然它们都是空的,但元组却比列表少占用 16 个字节,这是为什么呢?

  • 事实上,就是由于列表是动态的,它需要存储指针来指向对应的元素(占用 8 个字节)。
  • 另外,由于列表中元素可变,所以需要额外存储已经分配的长度大小(占用 8 个字节)。

但是对于元组,情况就不同了,元组长度大小固定,且存储元素不可变,所以存储空间也是固定的。

既然列表这么强大,还要元组这种序列类型干什么?
  • 通过对比列表和元组存储方式的差异,我们可以引申出这样的结论,即元组要比列表更加轻量级,所以从总体上来说,元组的性能速度要优于列表。

  • 另外,Python 会在后台,对静态数据做一些资源缓存。通常来说,因为垃圾回收机制的存在,如果一些变量不被使用了,Python 就会回收它们所占用的内存,返还给操作系统,以便其他变量或其他应用使用。

  • 但是对于一些静态变量(比如元组),如果它不被使用并且占用空间不大时,Python 会暂时缓存这部分内存

  • 这样的话,当下次再创建同样大小的元组时,Python 就可以不用再向操作系统发出请求去寻找内存,而是可以直接分配之前缓存的内存空间,这样就能大大加快程序的运行速度。

下面的例子,是计算初始化一个相同元素的列表和元组分别所需的时间。可以看到,元组的初始化速度要比列表快 5 倍。

C:\Users\qinjl>python -m timeit 'x=(1,2,3,4,5,6)'
20000000 loops, best of 5: 9.97 nsec per loop
C:\Users\qinjl>python -m timeit 'x=[1,2,3,4,5,6]'
5000000 loops, best of 5: 50.1 nsec per loop
  • 当然,如果你想要增加、删减或者改变元素,那么列表显然更优。因为对于元组来说,必须得通过新建一个元组来完成。总的来说,元组确实没有列表那么多功能,但是元组依旧是很重要的序列类型之一,元组的不可替代性体现在以下这些场景中:

  • 元组作为很多内置函数和序列类型方法的返回值存在,也就是说,在使用某些函数或者方法时,它的返回值会是元组类型,因此你必须对元组进行处理。

  • 元组比列表的访问和处理速度更快,因此,当需要对指定元素进行访问,且不涉及修改元素的操作时,建议使用元组。

元组可以在映射(和集合的成员)中当做“键”使用,而列表不行。

列表和元组的底层实现
有关列表(list)和元组(tuple)的底层实现,分别从它们的源码来进行分析。

首先来分析 list 列表,它的具体结构如下所示:

typedef struct { 
        
    PyObject_VAR_HEAD
    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
    PyObject **ob_item;
    /* ob_item contains space for 'allocated' elements.  The number
     * currently in use is ob_size.
     * Invariants:
     *     0 <= ob_size <= allocated
     *     len(list) == ob_size
     *     ob_item == NULL implies ob_size == allocated == 0
     * list.sort() temporarily sets allocated to -1 to detect mutations.
     *
     * Items must normally not be NULL, except during construction when
     * the list is not yet visible outside the function that builds it.
     */
    Py_ssize_t allocated;
} PyListObject;

list 列表实现的源码文件 listobject.h 和 listobject.c。

list 本质上是一个长度可变的连续数组:

  • 其中 ob_item 是一个指针列表,里边的每一个指针都指向列表中的元素,而 allocated 则用于存储该列表目前已被分配的空间大小。需要注意的是,allocated 和列表的实际空间大小不同,列表实际空间大小,指的是 len(list) 返回的结果,也就是上边代码中注释中的 ob_size,表示该列表总共存储了多少个元素。

  • 而在实际情况中,为了优化存储结构,避免每次增加元素都要重新分配内存,列表预分配的空间 allocated 往往会大于 ob_size。因此 allocated 和 ob_size 的关系是:allocated >= len(list) = ob_size >= 0。如果当前列表分配的空间已满(即 allocated == len(list)),则会向系统请求更大的内存空间,并把原来的元素全部拷贝过去。

接下来再分析元组,如下所示为 Python 3.7 tuple 元组的具体结构:

typedef struct { 
        
    PyObject_VAR_HEAD
    PyObject *ob_item[1];
    /* ob_item contains space for 'ob_size' elements.
     * Items must normally not be NULL, except during construction when
     * the tuple is not yet visible outside the function that builds it.
     */
} PyTupleObject;

tuple 元组实现的源码文件 tupleobject.h 和 tupleobject.c。

  • tuple 和 list 相似,本质也是一个数组,但是空间大小固定。不同于一般数组,Python 的 tuple 做了许多优化,来提升在程序中的效率。举个例子,为了提高效率,避免频繁的调用系统函数 free 和 malloc 向操作系统申请和释放空间,tuple 源文件中定义了一个 free_list:static PyTupleObject *free_list[PyTuple_MAXSAVESIZE];

所有申请过的,小于一定大小的元组,在释放的时候会被放进这个 free_list 中以供下次使用。也就是说,如果以后需要再去创建同样的 tuple,Python 就可以直接从缓存中载入。

原文链接:https://blog.csdn.net/qq_32727095/article/details/118383426

8、python set()去重的底层原理

一、set去重简单实例
ls = [1,2,3,1,2]
print(set(ls))

我们知道对于一个列表最简单的去重方法就是直接调用set函数,利用集合元素的唯一性,就可以做到去重。但是,这个底层原理究竟是什么样的却一直半解。

且看下面剖析

二、重新set实现机制
class Foo:
    def __init__(self,name,count):
        self.name = name
        self.count = count
    def __hash__(self):
        print("%s调用了哈希方法"%self.name)
        return hash(id(self))
    def __eq__(self, other):
        print("%s调用了eq方法")
        if self.__dict__ == other.__dict__:
            return True
        else:return False
f1 = Foo('f1',1)
f2 = Foo('f2',2)
f3 = Foo('f3',3)
ls = [f1,f2,f3]
print(set(ls))

从上面可以看出,set方法就是去调用hash方法(内置的),然后根据哈希值一不一样就行去重判断,但是事实就是样吗?且看下面程序。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3xfzW7Pz-1647795627089)(/Users/hello/Desktop/面试/image/截屏2022-03-19 15.10.12.png)]我看可以看出,实际上f1,f2的哈希值是相等的,但是set并没有这么简单就判断f1,f2是重复的,而是进一步通过eq方法判断这两个值是否相等,只有相等时才会认为这两个之间实际上是同一个。为了验证上面的说法,我们来看看下面的代码。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HVnteRKD-1647795627090)(/Users/hello/Desktop/面试/image/截屏2022-03-19 15.10.27.png)]可以看出去重后,只有两个元素,所以上面说法得证。

三、结论
set的去重是通过两个函数__hash__和__eq__结合实现的。
1、当两个变量的哈希值不相同时,就认为这两个变量是不同的
2、当两个变量哈希值一样时,调用__eq__方法,当返回值为True时认为这两个变量是同一个,应该去除一个。返回FALSE时,不去重

9、Python字典底层实现原理

​ 在Python中,字典是通过散列表或说哈希表实现的。也就是说,字典也是一个数组,但数组的索引是键经过哈希函数处理后得到的散列值。哈希函数的目的是使键均匀地分布在数组中,并且可以在内存中以O(1)的时间复杂度进行寻址,从而实现快速查找和修改。

​ 哈希表中哈希函数的设计困难在于将数据均匀分布在哈希表中,从而尽量减少哈希碰撞和冲突。由于不同的键可能具有相同的哈希值,即可能出现冲突,高级的哈希函数能够使冲突数目最小化。Python中并不包含这样高级的哈希函数,几个重要(用于处理字符串和整数)的哈希函数是常见的几个类型。通常情况下建立哈希表的具体过程如下:

  • 1、数据添加:把key通过哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。

  • 2、数据查询:再次使用哈希函数将key转换为对应的数组下标,并定位到数组的位置获取value。
    哈希函数就是一个映射,因此哈希函数的设定很灵活,只要使得任何关键字由此所得的哈希函数值都落在表长允许的范围之内即可。本质上看哈希函数不可能做成一个一对一的映射关系,其本质是一个多对一的映射,这也就引出了下面一个概念–哈希冲突或者说哈希碰撞。哈希碰撞是不可避免的,但是一个好的哈希函数的设计需要尽量避免哈希碰撞。

Python2 中使用使用开放地址法解决冲突。

CPython使用伪随机探测(pseudo-random probing)的散列表(hash table)作为字典的底层数据结构。由于这个实现细节,只有可哈希的对象才能作为字典的键。字典的三个基本操作(添加元素,获取元素和删除元素)的平均事件复杂度为O(1)。

Python中所有不可变的内置类型都是可哈希的。
可变类型(如列表,字典和集合)就是不可哈希的,因此不能作为字典的键。

常见的哈希碰撞解决方法:

  • 1 开放寻址法(open addressing)

    开放寻址法中,所有的元素都存放在散列表里,当产生哈希冲突时,通过一个探测函数计算出下一个候选位置,如果下一个获选位置还是有冲突,那么不断通过探测函数往下找,直到找个一个空槽来存放待插入元素。

    开放地址的意思是除了哈希函数得出的地址可用,当出现冲突的时候其他的地址也一样可用,常见的开放地址思想的方法有线性探测再散列,二次探测再散列等,这些方法都是在第一选择被占用的情况下的解决方法。

  • 2 再哈希法

    这个方法是按顺序规定多个哈希函数,每次查询的时候按顺序调用哈希函数,调用到第一个为空的时候返回不存在,调用到此键的时候返回其值。

  • 3 链地址法

    将所有关键字哈希值相同的记录都存在同一线性链表中,这样不需要占用其他的哈希地址,相同的哈希值在一条链表上,按顺序遍历就可以找到。

  • 4 公共溢出区

    ​ 其基本思想是:所有关键字和基本表中关键字为相同哈希值的记录,不管他们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。

  • 5 装填因子α

    一般情况下,处理冲突方法相同的哈希表,其平均查找长度依赖于哈希表的装填因子。哈希表的装填因子定义为表中填入的记录数和哈希表长度的比值,也就是标志着哈希表的装满程度。直观看来,α越小,发生冲突的可能性就越小,反之越大。一般0.75比较合适,涉及数学推导。

在python中一个key-value是一个entry,

entry有三种状态

Unused: me_key == me_value == NULL

Unused是entry的初始状态,key和value都为NULL。插入元素时,Unused状态转换成Active状态。这是me_key为NULL的唯一情况。

Active: me_key != NULL and me_key != dummy 且 me_value != NULL

插入元素后,entry就成了Active状态,这是me_value唯一不为NULL的情况,删除元素时Active状态刻转换成Dummy状态。

Dummy: me_key == dummy 且 me_value == NULL

此处的dummy对象实际上一个PyStringObject对象,仅作为指示标志。Dummy状态的元素可以在插入元素的时候将它变成Active状态,但它不可能再变成Unused状态。

为什么entry有Dummy状态呢?

  • 这是因为采用开放寻址法中,遇到哈希冲突时会找到下一个合适的位置,例如某元素经过哈希计算应该插入到A处,但是此时A处有元素的,通过探测函数计算得到下一个位置B,仍然有元素,直到找到位置C为止,此时ABC构成了探测链,查找元素时如果hash值相同,那么也是顺着这条探测链不断往后找,当删除探测链中的某个元素时,比如B,如果直接把B从哈希表中移除,即变成Unused状态,那么C就不可能再找到了,因为AC之间出现了断裂的现象,正是如此才出现了第三种状态—Dummy,Dummy是一种类似的伪删除方式,保证探测链的连续性。

提示

  • 一般情况下普通的顺序表数组存储结构也可以认为是简单的哈希表,虽然没有采用哈希函数(取余),但同样可以在O(1)时间内进行查找和修改。但是这种方法存在两个问题:扩展性不强;浪费空间。

  • set集合和dict一样也是基于散列表的,只是他的表元只包含键的引用,而没有对值的引用,其他的和dict基本上是一致的,所以在此就不再多说了。并且dict要求键必须是能被哈希的不可变对象,因此普通的set无法作为dict的键,必须选择被“冻结”的不可变集合类:frozenset。顾名思义,一旦初始化,集合内数据不可修改。
    ————————————————

原文链接:https://blog.csdn.net/answer3lin/article/details/84523332

10、深拷贝浅拷贝

如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。
在时间上来看浅拷贝花费时间更少;空间角度:浅拷贝花费内存更少;
效率角度:浅拷贝只拷贝顶层数据,一般情况下比深拷贝效率高。
浅拷贝:只是去拷贝第一层

L改变a和b都会改变。

深拷贝:拷贝所有层(完全拷贝)

a=9999
b=a
print(a)
print(b)
输出:9999 9999      这里的赋值知识复制了引用,a、b都指向了同一个数据对象
print(id(a))
print(id(b))
打印的也相同,表明a和b指向同一个数据对象

a=9999
b=9999
print(id(a))
print(id(b))
打印的不相同,表明a和b指向不同一个数据对象

11、Python中的GIL(全局解释器)以及python的多线程性能。

对于单核CPU,同一时刻只能有一个线程来执行,但是现在我们的电脑设备都是多核CPU,所以为了充分利用CPU的性能优势,我们会选择多线程或者多进程来开发。但是前面已经说了,多进程之间切换对于CPU的开销是巨大的,所以一般的应用程序开发会选择使用多线程来实现。

上面的一切看似很完美,但是很遗憾的是,python为了解决多线程之间数据的完整性和状态同步问题,python的解释器CPython中加了一把大锁GIL即全局解释器锁,它的作用在于,同一时刻只允许一个线程执行,不论计算机是几核。

所以对于CPU密集型场景,python的多线程是没有意义的。

但是对于IO密集型场景,python的多线程确实有意义的。

原因在于,IO密集型大部分情况下是处于等待状态,比如一个请求发出去了,线程就空闲了,要等待返回结果之后,才会继续执行,所以这一段时间,CPU是空闲的,可以分配给别的线程来执行任务。

12、解释装饰器概念

12.1 装饰器

简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
一般而言,我们要想拓展原来函数代码,最直接的办法就是侵入代码里面修改,例如:

import time
def func():
    print("hello")
    time.sleep(1)
    print("world")

这是我们最原始的的一个函数,然后我们试图记录下这个函数执行的总时间,那最简单的做法就是:

#原始侵入,篡改原函数
import time
def func():
    startTime = time.time()
    
    print("hello")
    time.sleep(<

相关文章