python knowledge

1.list跟tuple区别(列表跟元组)
tuple 不可修改元素,放弃了对元素的增删(内存结构设计上变的更精简),换取的是性能上的提升:创建 tuple 比 list 要快,存储空间比 list 占用更小。能用 tuple 的地方就不用 list。

2.集合set跟字典dict的区别
集合set是指无序的不重复元素序列。
set和dict的区别在于,dict是存储key-value,每一个key都是唯一的,set相对于dict存储的是key,元素重复会自动过滤掉。
3.深拷贝&浅拷贝(值的复制 地址的复制)
深拷贝就是完全跟以前就没有任何关系了,原来的对象怎么改都不会影响当前对象

浅拷贝,引用拷贝,复制后id不同,原可变对象(比如list,dict)改变的话会改变当前对象。

浅拷贝就是藕断丝连

深拷贝就是离婚了

3.构造函数& 析构函数
def init(self):
pass
结论:创建对象的过程中调用了构造函数。一般用来初始化对象属性并分配内存。
def del(self):
pass
析构函数:实例被销毁的时候执行的。另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间。

4.请解释使用*args和**kwargs的含义

当我们不知道向函数传递多少参数时,比如我们向传递一个列表或元组,我们就使用*args。

def func(*args):
for i in args:
print(i)

func(3,2,1,4,7)
运行结果为:

3

2

1

4

7
在我们不知道该传递多少关键字参数时,使用**kwargs来收集关键字参数。

def func(**kwargs):
for i in kwargs:
print(i,kwargs[i])

func(a=1,b=2,c=7)
运行结果为:

a.1

b.2

c.7

5.Python垃圾回收机制?
Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾。在引用计数的基础上,还可以通
过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用的问题。通过“分代回收”(generation collection)以空间换取时间来进一步提高垃圾回收的效率。

6.简述 三次握手、四次挥手的流程。
所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发

  1. 第一次握手:
    Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
  2. 第二次握手:
    Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
  3. 第三次握手:
    Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发

第一次挥手:
Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
第二次挥手:
Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
第三次挥手:
Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
第四次挥手:
Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
7. python内存管理机制
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。
垃圾回收机制:当一个对象给给某个变量引用,该对象的引用计数变+1,一旦对象的引用计数为0,该对象立即被回收。缺点是需要额外的空间维护引用计数。它不能解决对象的“循环引用”的问题。
解决循环引用问题:
标记清除算法:标记清除算法使用双向链表维护了一个数据结构,对象间的互相引用,这个引用关系构成这个有向图,如果从一个节点出发进行遍历,并标记其经过的所有节点;那么,在遍历结束后,所有没有被标记的节点,我们就称之为不可达节点。显而易见,这些节点的存在是没有任何意义的,自然的,我们就需要对它们进行垃圾回收。
分代回收机制:建立在标记清除算法上,Python 中的所有对象分为三代。刚刚创立的对象是第 0 代;经过一次垃圾回收后,依然存在的对象,便会依次从上一代挪到下一代。存活时间越久的对象,代数越高,回收频率越低。越不可能在后面的程序中变成垃圾。
内存池(memory pool)的概念:

当 创建大量消耗小内存的对象时,频繁调用new/malloc会导致大量的内存碎片,致使效率降低。内存池的概念就是预先在内存中申请一定数量的,大小相等 的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够了之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。

8.谈下python的GIL

GIL 是python的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。

为什么有GIL?
出现多线程编程之间数据和状态一致性问题,为了解决这个数据不能同步的问题

多进程中因为每个进程都能被系统分配资源,相当于每个进程有了一个python解释器,所以多进程可以实现多个进程的同时运行,缺点是进程系统资源开销大
9.悲观锁&乐观锁
悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式。总是假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据,因此需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等
悲观锁适用于并发竞争很厉害,写比较多的操作。

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。
乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。
10、同步异步 阻塞非阻塞
同步:当进程执行IO(等待外部数据)的时候,-----等。同步(例如打电话的时候必须等)
异步:当进程执行IO(等待外部数据)的时候,-----不等,去执行其他任务,一直等到数据接收成功,再回来处理。异步(例如发短信)
体现在网页请求上面就是我请求一个网页时候等待他回复,否则不接收其它请求,这就是同步。另一种就是我发送请求之后不去等待他是否回复,而去处理其它请求,当处理完其他请求之后,某个请求说,我的回复了,然后程序转而去处理他的回复数据。这就是异步请求。所以,异步可以充分cpu的效率。

阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务。等待当前函数返回

非阻塞:非阻塞和阻塞的概念相对应,非阻塞调用指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程

同步和异步的区别是遇到IO请求是否等待。阻塞和非阻塞的区别是数据没准备好的情况下是否立即返回。
  同步与异步是对应的,它们是线程之间的关系,两个线程之间要么是同步的,要么是异步的。

阻塞与非阻塞是对同一个线程来说的,在某个时刻,线程要么处于阻塞,要么处于非阻塞。

11 进程 线程 协程
进程是CPU资源分配的基本单位,进程控制块用来保存程序运行的状态。
线程是独立运行和独立调度的基本单位(CPU上真正运行的是线程)。因为一个程序(进程)中,线程共享一套数据。
进程拥有自己的资源空间,程序执行过程中的最小单元。一个进程包含若干个线程,线程与CPU资源分配无关,多个线程共享同一进程内的资源。
协程:协程是一种用户态的轻量级线程。有用户程序来控制。是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行。
执行效率极高,因为子程序切换(函数)不是线程切换,由程序自身控制,没有切换线程的开销。所以与多线程相比,线程的数量越多,协程性能的优势越明显。
不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在控制共享资源时也不需要加锁,因此执行效率高很多。
协程:https://blog.51cto.com/13786054/2132997
GIL:python因为他有GIL(全局解释器锁 )在同一时间只有一个线程在工作。

12 docker,k8s了解吗
docker
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
Docker 主要有如下几个概念:

引擎:创建和管理容器的工具,通过读取镜像来生成容器,并负责从仓库拉取镜像或提交镜像到仓库中; 镜像:类似于虚拟机镜像,一般由一个基本操作系统环境和多个应用程序打包而成,是创建容器的模板; 容器:可看作一个简易版的 ** Linux ** 系统环境(包括 root 用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子; 仓库:集中存放镜像文件的场所,分为公共仓库和私有仓库,目前最大的公共仓库是官方提供的 **Docker Hub ** ,此外国内的阿里云、腾讯云等也提供了公共仓库; 宿主机:运行引擎的操作系统所在服务器。

k8s
它是一个工业级的容器编排平台。它负责应用的部署、应用的弹性以及应用的管理,这些都是基于容器的。
Kubernetes 有如下几个核心的功能:

服务的发现与负载的均衡; 容器的自动装箱,就是“调度”,把一个容器放到一个集群的某一个机器上,**Kubernetes **会帮助我们去做存储的编排,让存储的声明周期与容器的生命周期能有一个连接; **Kubernetes **会帮助我们去做自动化的容器的恢复。在一个集群中,经常会出现宿主机的问题或者说是 OS 的问题,导致容器本身的不可用,Kubernetes 会自动地对这些不可用的容器进行恢复; Kubernetes 会帮助我们去做应用的自动发布与应用的回滚,以及与应用相关的配置密文的管理; 对于 job 类型任务,**Kubernetes **可以去做批量的执行; 为了让这个集群、这个应用更富有弹性,Kubernetes 也支持水平的伸缩。

Kubernetes 架构是一个比较典型的 二层架构和 server-client 架构。

Master 作为中央的管控节点,会去与Node 进行一个连接。 所有 U I 、clients这些user侧的组件,只会和 Master 进行连接,把希望的状态或者想执行的命令下发给Master,Master 会把这些命令或者状态下发给相应的节点,进行最终的执行。

核心概念

第一个概念:Pod Pod是 Kubernetes的一个最小调度以及资源单元。用户可以通过Kubernetes 的Pod API 生产一个Pod,让Kubernetes 对这个 Pod 进行调度,也就是把它放在某一个 Kubernetes 管理的节点上运行起来。一个** Pod** 简单来说是对一组容器的抽象,它里面会包含一个或多个容器。 第二个概念:Volume Volume就是卷的概念,它是用来管理 Kubernetes 存储的,是用来声明在 Pod 中的容器可以访问文件目录的,一个卷可以被挂载在 Pod 中一个或者多个容器的指定路径下面。 第三个概念:Deployment Deployment 是在 Pod 这个抽象上更为上层的一个抽象,它可以定义一组Pod 的副本数目、以及这个Pod 的版本。一般大家用Deployment 这个抽象来做应用的真正的管理,而 Pod 是组成Deployment 最小的单元。 第四个概念:Service Service 提供了一个或者多个 Pod 实例的稳定访问地址。 第五个概念:Namespace Namespace 是用来做一个集群内部的逻辑隔离的,它包括鉴权、资源管理等。
13.
break 用于终止整个循环;continue 用于跳出本次循环,还会继续下一次循环。
14. 迭代器和可迭代对象
一个类如果实现了「迭代器协议」,就可以称之为「迭代器」
实现迭代器协议就是实现以下 2 个方法:
__ iter __ :这个方法返回对象本身,即 self
__ next __:这个方法每次返回迭代的值,在没有可迭代元素时,抛出 StopIteration 异常
可迭代对象: __ iter __ 方法返回一个迭代器,那么这个对象就是「可迭代对象」
迭代器一定是可迭代对象。
迭代器的优势:
在构建迭代器时,不是将所有的元素一次性的加载,而是等调用next方法时返回元素,所以不需要考虑内存的问题。

class A:
    """A 实现了迭代器协议 它的实例就是一个迭代器"""
    def __init__(self, n):
        self.idx = 0
        self.n = n

    def __iter__(self):
        print('__iter__')
        return self

    def __next__(self):
        if self.idx < self.n:
            val = self.idx
            self.idx += 1
            return val
        else:
            raise StopIteration()

list、tuple、set、dict 类型,都只是「可迭代对象」,但不是「迭代器」,因为它们都是把迭代细节交给了另外一个类,这个类才是真正的迭代器。
比如列表>>> l = [1, 2]
#list 的迭代器是 list_iterator
__ iter __(l)
<list_iterator object at 0x1009c1c18>
#执行的是 list_iterator 的 next
__ iter __(l).next()
1
生成器
yield 关键字。其实,包含 yield 关键字的函数,不再是一个普通的函数,而返回的是一个生成器
包含 yield 的方法一般用于迭代,每次执行时遇到 yield 就返回 yield 后的结果,但内部会保留上次执行的状态,下次继续迭代时,会继续执行 yield 之后的代码,直到再次遇到 yield 后返回。
优点:简洁
生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表 squares = (x**2 for x in range(5)) <generator object at 0x00B2EC88> next(squares)
使用生成器创建这个大集合大列表,只有在迭代执行到 yield 时,才会返回一个元素,在这个过程中,不会一次性申请非常大的内存空间。当我们面对这种场景时,使用生成器就非常合适了。

总结:
如果一个类实现了 __iter__和 __next__方法,那么它就是一个迭代器。如果只是实现了 __ iter __,并且这个方法返回的是一个迭代器类,那么这个类的实例就只是一个可迭代对象,因为它的迭代细节是交给了另一个类来处理。
像我们经常使用的 list、tuple、set、dict 类型,它们并不是迭代器,只能叫做可迭代对象,它们的迭代细节都是交给了另一个类来处理的。由此我们也得知,一个迭代器一定是一个可迭代对象,但可迭代对象不一定是迭代器。
迭代器生成器区别:
迭代器作用:
而生成器可以看做是一个特殊的迭代器,更加简洁。唯一的区别在于实现方式上不一样,后者更加简洁。使用生成器配合 yield 使用,生成器只能遍历一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值