Python八股文:基础知识Part2

1. Python中变量的保存和访问

Python中的变量实际上是一个指向对象的引用,每个对象都有一个唯一的标识符(即内存地址)。对于一些不可变对象,如字符串和整数,因为它们的值不可更改,所以当多个变量引用相同的值时,实际上都是在指向同一个对象。这样可以节省内存空间,避免重复创建相同的对象。对于可变对象,如列表和字典,将会为每个变量分配独立的内存空间

同时要记住python的用‘=’符号对变量进行赋值的时候总是在创建新的对象并将变量名与新对象进行绑定,而不是修改原有对象,所以在下面的代码中,尽管list是可变数据类型,但是还是开辟了新的内存空间


2. 对于字符串的添加,如何更有效率的完成代码

对于下图中的代码,在Python中,字符串是不可变的数据类型。每次执行s += 's'时,实际上会创建一个新的字符串对象,其中包含原始字符串 s 的内容以及要追加的字符 's'。这意味着每次迭代都会创建一个新的字符串对象,并将其赋值给变量 s。由于每次迭代都会创建一个新的字符串对象,并且这些对象的大小会逐渐增长,因此会产生大量的临时对象,这会导致效率很低 

s = 's'
for i in range(1000):
	s += 's'

print(s)

解决的方法1,就是用列表来构建字符串

chars = ['s']
for i in range(1000):
    chars.append('s')

s = ''.join(chars)
print(s)

 另一个解决方案是使用字符串的 str.join() 方法以及列表推导式。这种方法利用了列表推导式生成一个包含要添加的字符的列表,然后使用 str.join() 方法将这些字符连接成一个字符串。这种方法避免了在循环中不断地进行字符串连接,因此效率更高

s = 's'
s += ''.join('s' for _ in range(1000))
print(s)

3. 函数的默认参数机制

Python解释器对于函数的默认参数,在第一次调用时,会计算一次,即在内存中创建一个对象保存默认参数,而后如果继续调用,且对于默认参数没有显性传参,那么后续调用的同一函数的同一默认参数共享内存

如上图中所示,第一次调用会在内存中新建一个对象,而后的调用会共享该对象,所以foo(2)打印出来的就会保存着ls之前的值

我们应该避免使用可变对象作为默认参数,这是因为假如使用可变对象作为默认参数,且没有进行显性传参,那么该对象会被多次默认调用的函数参数共享,如果函数内部又存在原地修改对象的操作,便会影响到其他调用的函数,导致不同调用的内部默认参数不再独立,会互相影响

如果是不可变数据类型,那么就不存在这个问题


4. Python中变量的作用域

Python中有四种作用域,分别是局部作用域(Local)、嵌套作用域(Embedded)、全局作用域(Global)、内置作用域(Built-in),搜索一个标识符时,会按照LEGB的顺序进行搜索,如果所有的作用域中都没有找到这个标识符,就会引发NameError异常。

你会发现在上面的代码中,尽管y和z分别在全局作用域和嵌套作用域中赋值了,但是打印出来的结果是按照搜索顺序的,然后呢对于print变量,其实就是一个内置函数的名字,所以是可以找到的,不会报错,唯一报错的就是从来没有命名过的w变量


5. MRO(Method Resolution Order)方法解析顺序在继承中的应用

MRO 的计算遵循以下规则:

  1. 首先考虑当前类(即子类)。
  2. 然后按照从左到右的顺序,递归地考虑每个父类,直到所有父类都被考虑。
  3. 在这个过程中,如果有多个父类,且它们有相同的祖先,则按照其在类定义中的顺序确定 MRO

 也就是说,每一个类创建了之后都会有一个MRO的顺序,就像图中的M类一样,然后我们在调用函数或者获取变量的时候,就会遵守这个MRO的顺序一个一个的在每一个类中去找,优先使用或返回在MRO中位置靠前的,所以上图中m.say()的调用会打印B

这里对于mro顺序的底层实现逻辑用到了C3算法,简单的来说大致分两种,第一种就是菱形查找,也就是上图中的情况,

在菱形查找中,我们如果有类会最终汇聚到同一个父类,那么这个父类一定是最后一个被搜索的,在那之前,我们进行广度优先搜索,一层一层的找,

在非菱形搜索中,我们搜索的时候,当然同一层也就是当前类如果有多个父类,那肯定还是从左往右进行搜索,但遇到一个分支之后,会继续往上寻找他的父类,直到直到头之后,再对之前同一层的下一个类的分支进行搜索


6. 迭代器和生成器

迭代器

迭代器类需要满足有两个函数,

__iter__() 方法:返回迭代器自身,这使得迭代器对象本身也是可迭代的。通常该方法直接返回对象本身。

__next__() 方法:返回容器中的下一个元素。当没有元素可以返回时,抛出 StopIteration 异常,这标志着迭代结束。如果不想抛出异常,也可以在 __next__() 方法中返回一个特殊值,如 None

同时我们还要记住,通过迭代器类实例化的一个迭代器对象,和可迭代对象之间的关系

1. 可迭代对象包含迭代器,也就是说我们python中自带的数据类型如列表,元组,字典其实都是可迭代对象,类中只要包含__iter__ 函数,就是一个可迭代对象,代表可以被for循环,因为我们可以通过__iter__会返回一个迭代器,意味着我们可以像for i in list 一样通过for循环语句对生成的迭代器对象进行遍历,也就是下图中我们第一个对象even_iter的变量方法,

2. 定义可迭代对象,必须实现__iter__方法;定义迭代器,必须实现__iter__和next方法

通过for循环对迭代器进行遍历的过程其实就是迭代器先通过 __iter__() 函数获取一个迭代器对象然后再不断通过 next( ) 取值,也就是说,在进行for循环的时候,迭代器对象的__iter__ 返回他自身,也就是一个迭代器然后被for循环,而可迭代对象也通过调用__iter__函数来返回一个迭代器,被for循环,总而言之,只有迭代器对象才能被for循环,Python 中,确实是先将可迭代对象转换为迭代器对象,然后才能进行遍历。这是因为 for 循环实际上是在处理迭代器对象,而不是可迭代对象  我们的 range方法其实返回的就是一个可迭代对象,obj = range(10000), 这里如果用dir(obj)来查看obj的信息,就会发现其中包含__iter__函数,但是没有__next__ 函数,只有我们在for i in range(10000) 的时候,在for循环中,才会调用range( ) 返回的可迭代对象的__iter__函数来得到一个迭代器供for循环来遍历

然后除了使用for循环,还可以直接调用 __next__( ) 函数来获取迭代器中的值,写成next( ) 也可以,注意这种方法如果我们手动调用的时候,next会记录上一次遍历到的位置因为同一个对象中的类属性被更改之后是保留着一直更新的,那么当遇到stop iteration的条件时,就会报错

生成器

然后呢我们来了解一下生成器,记住:生成器是特殊的迭代器,它可以动态生成值而不需要预先分配内存空间来存储所有的值。生成器通过使用 yield 关键字来定义,在每次调用时生成(yield)一个值,而不是将所有的值一次性生成并存储在内存中

他有两种方式,第一种方式就是通过生成器函数,任何函数中带有yield语句的,就是一个生成器函数,然后当我们调用生成器函数的时候,就会返回一个生成器对象,而不会执行函数体内的代码。每次调用生成器对象的 __next__() 方法时,生成器函数会从上一次 yield 语句的位置开始执行,直到遇到下一个 yield 语句为止,并将 yield 后面的表达式的值返回给调用者

当我们调用生成器函数的时候,其实python底层会根据生成器类 generator来创建这个生成器对象,这个类中也有__iter__ 和 __next__ 函数 

生成器还有一种方法就是生成器表达式,类似于列表表达式,只是把[ ] 换成 ()

并且这里你可以看到,正因为我们说了,生成器是特殊的迭代器,所以他也能够被for循环遍历, 但是这里要注意,生成器只能被遍历一次,就如上图我们的打印gen值发生了一次

接下来是迭代器和生成器两者的区别,重点记忆


7. 以下代码会返回什么值

def multiply():
    return [lambda x: i * x for i in range(4)]

print([m(100) for m in multiply()])

正确答案是[300, 300, 300, 300], 很多同学可能会误认为是[0, 100, 200, 300] 因为认为我们的multiply函数返回了四个匿名函数分别是lambda x: 0 * x, lambda x: 1 * x, lambda x: 2 * x 还有 lambda x: 3 * x,但其实并不是这样,这里需要注意的就是一个闭包的现象

当你调用 `multiply()` 函数时,它返回了一个包含四个 lambda 函数的列表,每个 lambda 函数都是一个闭包,其中 `i` 是一个自由变量。在这个列表推导式中,`i` 是在循环结束时保存的最后一个值,因为 lambda 函数在定义时会捕获当前的环境变量,而不是在调用时。因此,无论何时调用 lambda 函数,它都会使用最后一个值 `i`。

当你调用 `[m(100) for m in multiply()]` 时,会遍历这个列表并对其中的每个 lambda 函数调用,`x` 的值为 100。但是由于每个 lambda 函数都捕获了同一个自由变量 `i`,而 `i` 在循环结束时的值是 3,所以每个 lambda 函数都会返回 `3 * 100`,即 300。因此,结果列表中的所有元素都是 300。

这里不用是不用列表推导式的写法,会更好理解一点

def multiply():
    result = []
    for i in range(4):
        def inner_func(x):
            return i * x 
        result.append(inner_func)
    return result

funcs = multiply()
results = []
for func in funcs:
    results.append(func(100))
print(results)

你这里就会明白因为变量 i 的生成是在外部函数 multiply 中的,当我们在内部函数 inner_func 使用 i 的时候,就形成了一个闭包,局部变量i的生命周期被延展了,在下面我们调用func(100) 的时候,还是会需要获取 i 的值,这时其实 i 已经结束了for 循环了,并且值被留在了最后一层循环的3,所以会全都返回300

这里如果我们的确想要得到[0, 100, 200, 300]的话,可以用下面的方法,把列表推导式换成生成器,生成器表达式会在每次迭代时动态地生成新的作用域,而列表推导式则会在定义时捕获当前作用域的变量

def multiply():
    return (lambda x: i * x for i in range(4))

print([m(100) for m in multiply()])

这里为了能更好的理解这里出现的问题的原因,我们就要聊到闭包


8. 闭包

闭包是指在一个函数内部定义的函数,并且内部函数可以访问外部函数的变量。当外部函数调用结束后,内部函数仍然可以访问并操作外部函数中的变量,这种行为称为闭包 

闭包是函数,是一种可以访问其他函数作用域里面东西的函数

def outer_func():
    x = 10  # 外部函数的局部变量

    def inner_func():
        print("x 的值为:", x)  # 内部函数引用了外部函数的变量
    return inner_func  # 外部函数返回内部函数的引用

# 调用外部函数,得到内部函数的引用
my_func = outer_func()

# 调用内部函数,内部函数仍然可以访问外部函数的变量
my_func()

我们可以这样简单的理解一下闭包的使用,这里我有一个函数,我想要在每次调用的时候,都往列表中添加一个值,然后打印出来当前列表中的所有值

你会发现上面的写法很明显不是我们想要的,我们不能每次都在调用这个函数的时候重新生成一个num列表,然后其实解决方法也很简单,我们可以把num[ ] 列表在函数外面生成为全局变量,但是这种方法在实际开发中是不好用的,我们没办法在其他的文件中对函数进行重复利用,因为我们还得把全局变量都一起进行迁移,所以更好的方法就是进行闭包

使用闭包的时候需要注意,闭包会使得函数中创建的对象不会被垃圾回收,可能会导致很大的内存开销,所以闭包一定不能滥用 

闭包的使用场景除了上面这种对函数进行了封装以外,还有就是用来实现装饰器


 9. 装饰器

装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数。这个新函数通常会在原始函数执行之前或之后,执行额外的逻辑,他的使用方法也就是 @decorator 这个写法本身就是一个语法糖,没有什么特别的,每一个函数被装饰器装饰的时候都可以理解成一下的写法

上图的f1和f2中,f1被装饰器装饰了,然后f2是被赋值成了把自己传入装饰器后的结果,这两者其实是一样的,装饰器做的就是对f2的操作

装饰器是一个参数是函数,返回值也是函数的函数 

同时我们的decorator为了满足能够修饰不同的任意函数,会把wrapper函数的参数设定为可变长度的参数,下面是一个装饰器的例子

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def say_hello(name):
    return f"Hello, {name}!"

# 调用被装饰后的函数
message = say_hello("Alice")
print(message)

10. Python标准库中的模块


 11. Python的解释器

我们都知道当我们写完一个python代码然后保存成.py文件之后,如果没有解释器来运行这个文件,那这个文件和普通的txt file没任何区别,python这个语言有不只一个的解释器,而python官方提供使用的解释器叫CPython,他的底层是由C语言编写的,其他解释器还有例如PyPy,是一个用 Python 编写的、自举的、高度优化的 Python 解释器。PyPy 使用即时编译(JIT)技术,还有Jython,将 Python 代码编译成 Java 字节码,然后在 Java 虚拟机上执行


12. Python的内存管理和垃圾回收

1. 引用计数器

我们需要先明白在python中,有一个双向环形列表,用于管理内存中的对象。CPython解释器来说,Python中的每一个对象其实就是PyObject结构体,这个结构体就被储存在这个双向环形链表中,每个结构体会保存的数据有:两个指针一个指向上一个对象结构体一个指向下一个对象结构体,然后和当前对象的类型,还有一个很重要的就是名为ob_refcnt 的引用计数器成员变量

这里其实就是当我们创建了一个float对象之后,在底层发生的对其数据的创建 ,这里的计数器会被默认为1,然后在那之后,每次有其他变量引用这个对象的时候,refcnt就会+1

同样能加1就能减一,如下图,然后当一个对象的引用计数变为0了之后,就会被回收,也就是从我们的双向环形链表内存池的refchain中移除 ,并且销毁对象,内存归还

2. 标记清除

但是这种引用计数器的处理机制其实也有一个bug,那就是循环引用的问题,在下面的代码中,当我们执行完del v1 和 del v2后,与上面的int对象99999不同的是,我们的v1和v2的引用计数器此时并不会变成0而是都为1,但是按理来说我们del之后已经没办法再对其进行修改了,所以如果我们只依靠引用计数器来决定一个内存空间是否为垃圾需要回收的话,此时的v1和v2就不会被清理,如果这种循环引用次数很多,那就会造成内存泄漏,也就是程序中的内存分配操作并未被正确释放,导致程序在运行过程中不断占用内存而不释放,最终导致可用内存耗尽

于是python就又多了一个内存管理机制就是标记清除,首先我们要知道可能会出现循环引用的对象一般都是可以储存多个元素的对象例如list/set/tuple/dict,那标记清除的机制其实就是当我们创建了这类对象的时候,除了会将其加入到我们的refchain中,还会再使用和维护一个新的双向环形链表来储存这个对象

就如上图所示,普通的int对象19只存在于refchain中,但是列表对象在两个链表中都有储存,然后呢python内部中某种情况时,他会去扫描我们的这个记录潜在循环引用对象的链表,去一个一个遍历其中的元素以及每个元素引用的元素,去找是否存在两个对象之间含有你引用我,我又引用你的循环引用现象,如果又,就把这两个对象的引用计数都一起 -1,然后去看是否变成了0,如果是,就认为是垃圾,然后进行回收

3. 分代回收

但是呢,光有上面的其实还是不够,我并没有说明到底如何来决定什么时候去扫描我们的循环引用链表,并且要知道,为了查找是否存在循环引用,对于每一个对象中存放的各个元素我们还要去找对应的子元素,其实是一个比较花费时间代价比较大的操作,那么为了解决这两个问题,就继续引出分代回收的概念

思路就是把我们上面说的存放可能存在循环引用对象的链表拆分成三个链表,也就是三代,分别为0代,1代,和2代

上图就是对于每一代的链表进行扫描的机制,用一个具体例子来说明就是一开始三个链表都是空的,然后呢我们开始创建很多有可能发生循环引用的数据例如list,每一个list对象一开始都会被存入到0代中,当我们存放了很多很多对象达到700的时候,我们对0代进行一次扫描,来找任何发生了循环引用的对象,并将其的引用计数-1,接着就对那些引用计数经过-1变成0的对象进行垃圾回收,接下来这700个对象中那些没有变成垃圾的对象,会被移动到1代链表中,0代链表被清空,当我们有经过一段时间的添加后,0代再次达到700个时,重复上面的操作,进行扫描清除然后移动到1代,1代会记录0代进行扫描的次数,达到10次时,1代就也进行一次扫描也清理,并将剩余的对象移动到2代链表中,就根据这个机制一直循环下去,也就是说2代链表中存放的对象是存活时间最长的对象

到此为止我们可以先对python的内存管理和垃圾回收的面试做一个回答的答案就是

4. 缓存机制

除了上面的回答以外,还可以给面试官聊一下缓存机制,cpython在进行上述的进程的时候还提出了一个优化机制

也就是说Python为了避免多次重复的去新建和销毁一些很常用的值,他选择维护一个池,里面已经为[-5, 256] 的整数创建好了空间当我们去创建这些池内的值的时候, 就直接去池中获取就行了,就如下图所示,因为v2和v3需要的数值都是9,所以他们会指向我们池中的同一个内存,而不是分别各自去开辟新内存新建9

这里有个小地方需要注意,当我们在使用一些文本编辑器然后运行python或者ide进行集成式开发的时候,程序的运行还会发生一些别的优化或调整,你会遇到这种情况,即使v2和v3的值已经超出了我们整数缓存池的范围,但是他们依然指向了同一个地址,不用太在意这种情况,你如果在终端命令行中执行Python的话又会看到不一样的结果

然后我们需要明白的是,我们存放在池中的这些对象,他们的引用计数永远不会变为0,意味着永远不会被垃圾回收,因为在程序开始的时候他们就已经被赋予了默认值为1的引用计数,后续我们在添加或删除只能在1的基础上进行

在这个基础上,还有一个缓存机制,在 Python 内部,一种特定类型的对象池被称为 "freelist"(空闲列表),他的机制是当我们的一个对象的引用计数变为0时,不会直接对其进行销毁而是先放入我们的free_list列表中,当我们下一次又创建了新的对象的时候并且对象类型和free_list有的对象一样,我们不会开辟新的内存空间而且取到free_list中的这个对象然后将其的值改成我们新对象的值

上图中你可以清楚的看到,v2所使用的内存地址和v1在被删除之前是一样的

free_list的空间不是无限的,例如在源码中,说明了free_list中最多可以储存80个list对象,如果超出了限制之后,我们再发现一个list对象的引用计数已经变为了0,那就只能直接销毁了 

python中使用free_list机制的数据类型有float, list, tuple, dict

然后对于字符串来说,首先我们也知道字符串都是由字符组成的,python会想整数一样,在内存中维护一个链表叫做ascii_latin然后把所有的ascii code中的字符都先储存起来,需要的之后直接去拿,然后对于字符串来说,还有一个驻留机制,如果你是只包含字母数字下划线长度小于20,那么你在创建之后,如果又需要创建一个和你一模一样的字符串,那么我们不会再开辟新的内存空间而是让其直接指向你


13. 并发编程,Python的线程与进程

在编程中引入并发最直接的原因就是提高程序的运行速度

1. cpu密集型计算和io密集型计算

2. 进程,线程,协程的对比

 也就是说当我们需要选择使用哪种技术的时候,首先就是看的是不是CPU密集型计算,如果是,直接选择进程,其次再看IO密集型计算中我们的任务多不多,现成库支不支持,并且能不能接受协程的复杂度,如果可以那就可以选择协程

3. GIL

在 Python 中,GIL(全局解释器锁)是一种机制,用于确保在同一时间只有一个线程可以执行 Python 字节码,也就是说由于 GIL 的存在,Python 中的多线程并不能真正意义上实现并行执行,即使在多核系统中。当一个线程获得 GIL 后,其他线程会被阻塞,直到该线程释放 GIL,这点不同于Java和C++,那为什么Python要设计这个看起来很笨的东西呢?其实这又要牵扯到Python的内存管理机制,也就是引用计数器的使用,导致了Python需要解决多线程之间的数据完整性和状态同步问题

我们为了避免GIL的问题,就只能确保我们在使用threading多线程计算的时候,是IO密集型运算,因为GIL锁在遇到IO操作的时候,是会释放锁然后允许其他线程运行的,这样以来,依然会帮助我们提升运行速度,然后呢就是Python也提供了multiprocessing 的多进程机制来让我们能真正利用多核CPU的好处

4. 多线程的实现和注意事项

下面的代码是一个非常简单的线程展示,每个线程就是一个Thread对象,然后对象的生成需要传入一个线程对应的函数以及用元组的形式传入那个函数的参数

import threading
import time

# 定义一个函数作为线程的执行体
def thread_function(name):
    print("Thread", name, "is starting...")
    # 模拟线程执行一些任务
    time.sleep(2)
    print("Thread", name, "is ending.")

# 创建并启动多个线程
threads = []
for i in range(5):
    thread = threading.Thread(target=thread_function, args=(i,))
    thread.start()
    threads.append(thread)

# 等待所有线程执行完成
for thread in threads:
    thread.join()

print("All threads have finished.")

 在 Python 中,queue.Queue 是一个线程安全的队列实现,它可以用于多个线程之间的安全通信。Queue 提供了多个方法来实现线程之间的数据传递和同步

除了线程之间的数据传递以外,我们还可能遇到当在多线程并发执行的情况下,由于多个线程同时访问共享资源或共享数据而可能导致的数据不一致或不正确的问题,这就是线程安全问题,最简单的就是两个取钱的线程会判断余额是否充足,但是有可能线程2的判断发生在了线程1取钱之前,那么就会错误判断,我们有两种方法利用锁Lock来解决线程安全问题

 5. 多进程的实现

我们要明白,因为Python有GIL锁的机制,代表每个线程在遇到IO操作之前不会释放GIL,那当然如果我们要运行的程序本身就是IO密集,那代表每个线程之间可以频繁的进行切换从而提高代码的运行速度,但是在CPU密集的操作中,同样也是同一时间只有一个线程在执行,缺少了IO操作就会让其他线程大部分时间都处在堵塞的状态,并且切换的时候也没有带来更好的效益,反而变成了负担,减慢了运行速度

多进程的multiprocessing模块的用法几乎和threading语法一模一样 

由于GIL的问题,我们python中的线程只是并发但是不是并行 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/557907.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OSPF大型实验

OSPF大型实验 实验拓扑图 实验思路 1、R4为ISP,其上只配置IP地址;R4与其他所直连设备间均使用公有IP; 2、R3-R5、R6、R7为MGRE环境,R3为中心站点; 3、整个OSPF环境IP基于172.16.0.0/16划分;除了R12有两…

Java精品项目--第8期基于SpringBoot的宠物用品商城的设计分析与实现

项目使用技术栈 SpringBootThymeleafMyBatisMySQLAopJavajdk1.8 项目介绍 项目截图

JAVA 集合框架(一) Collection集合详解和常用方法

Java集合框架(Java Collections Framework)是一个强大的、高度灵活的数据结构库,它为Java应用程序提供了组织、存储和操作对象集合的标准方法。 集合类体系结构 接口类: Collection: 是单例集合类的顶层接口,包括Lis…

移动端适配之viewport

目录 盒模型:widthcontent(padding border) class"content-box"内容盒模型(W3C盒) class"border-box"边框盒模型(IE 盒) scroll滚动 window浏览器视窗:包括…

MySQL高级(索引-性能分析-explain执行计划)

explain 或者 desc 命令获取 MySQL 如何执行 select 语句的信息,包括在 select 语句执行过程中表如何连接和连接的顺序。 -- 直接在 select 语句之前加上关键字 explain / desc explain select 字段列表 from 表名 where 条件 ; explain select * …

自养号测评:如何提高国际站,敦煌网的店铺销量?

随着互联网技术的迅猛进步,电子商务已经成为现代商业领域中不可或缺的一环。敦煌网,作为专注于中国文化艺术产品的电商平台,成功吸引了大量消费者的目光。然而,对于商家而言,如何进一步提升敦煌网的销售业绩&#xff0…

用这些工具搭建企业内部知识库,原来这么轻松

在快速发展和信息爆炸的时代,为企业构建一个内部知识库变得十分重要。它不仅有助于保存关键信息,促进知识传承,还能提高企业的整体效率和响应能力。今天,我们将探讨三款非常适合搭建企业内部知识库的工具,它们各具特色…

Python教学入门:流程控制

条件语句(if 语句): 条件语句用于根据条件的真假执行不同的代码块。 x 10if x > 0: # 如果 x 大于 0print("x 是正数") # 输出:x 是正数 elif x 0: # 如果 x 等于 0print("x 是零") else: # 如果以…

第07-1章 计算机网络相关概念

7.1 本章目标 了解网络协议的概念了解网络体系结构熟悉ISO/OSI参考模型以及每一层的功能掌握TCP/IP模型各层的主要协议及其功能熟练掌握IP地址、子网规划等相关内容 7.2 网络协议的概念 7.2.1 概念介绍 (1)网络协议:计算机网络和分布系统中…

Java 笔试强训篇- Day1

🔥博客主页: 【小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录 1.0 点击消除 1.1 解题思路一 1.2 解题思路二 2.0 在两个数组中找出相同的数 2.1 解题思路 笔试强训说明:有一些题目提供不了原题。 1.0 点击消除 该题链…

初学python记录:力扣1883. 准时抵达会议现场的最小跳过休息次数

题目: 给你一个整数 hoursBefore ,表示你要前往会议所剩下的可用小时数。要想成功抵达会议现场,你必须途经 n 条道路。道路的长度用一个长度为 n 的整数数组 dist 表示,其中 dist[i] 表示第 i 条道路的长度(单位&…

Redis之路系列(2)纸上得来终觉浅(上)

02 纸上得来终觉浅(上) 文章内容基于redis6,本章节介绍了redis的实际应用,主要包含:大量键值对保存的案例场景,海量key时的聚合计算、排序计算、状态统计、基础统计的应用 大量键值对保存 场景案例 有这么一个需求场景&#xff…

OpenSearch的几种认证

在Amazon OpenSearch Service中,主用户的配置可以通过三种方式进行:用户名和密码组合、IAM角色,以及通过第三方联合登录。这样的配置授权主用户在OpenSearch仪表板上进行内部用户、角色和角色映射的创建。需要注意的是,OpenSearch…

【nginx代理和tengine的启动-重启等命令】

在nginx成功启动后[任务管理器有nginx.exe进程],运行vue项目,在浏览器访问http://localhost:10001/,提示:访问拒绝(调试中network某些地址403); 解决方案: localhost改为ip&#xff…

【论文笔记 | 异步联邦】Asynchronous Federated Optimization

论文信息 Asynchronous Federated Optimization,OPT2020: 12th Annual Workshop on Optimization for Machine Learning,不属于ccfa introduction 背景:联邦学习有三个关键性质 任务激活不频繁(比较难以达成条件)&…

怎么配置python

右键点击“计算机”,选择“属性”。 在左侧栏找到“高级系统设置”。 点击“环境变量”。 在系统变量中,双击“Path”。 在字符串的末尾,加一个分号; 然后再输入你安装python的路径,如图所示: 点击“确定”&#xff0…

Python中的迭代器:深入理解与实用指南

文章目录 1. 迭代器的基本概念2. Python中的迭代器实例3. 自定义迭代器3.1 例子3.2 详细过程 4. 迭代器的高级应用5. 常见问题与解答 迭代器是Python中非常核心的概念之一,在面试中也会被问到。下面我会详细介绍什么是迭代器,使用方法,以及使…

JVM之初识垃圾收集器

JDK 8:Parallel Scavenge(新生代) Parallel Old(老年代)JDK8以后:G1收集器 什么是串行回收和并行回收? Serial收集器:串行收集器 新生代使用标记复制算法,老年代使用标记…

JSON驱动的动态SQL查询:实现灵活条件筛选的查询

当我们构建动态 SQL 查询功能时,需要考虑到安全性和灵活性的平衡。本文将讨论如何通过 JSON 数据和 FreeMarker 模板构造动态 SQL 查询,以及如何减少 SQL 注入的风险。 JSON 数据与动态 SQL JSON 是一种常用的数据交换格式,它的灵活性和易读…

【读点论文】YOLOX: Exceeding YOLO Series in 2021,无锚框单阶段目标检测方案,解耦检测头的分类和回归分支,优化标签分配策略

YOLOX: Exceeding YOLO Series in 2021 Abstract 在本报告中,我们介绍了YOLO系列的一些经验改进,形成了一种新的高性能探测器—YOLOX。我们将YOLO检测器切换到无锚方式,并进行其他先进的检测技术,即去耦头和领先的标签分配策略S…