秋招算法岗python面经

目录

1.python的深拷贝和浅拷贝

2.python的多线程可以用多个cpu吗?

3.python垃圾回收机制

4.迭代器和生成器的区别

5.装饰器

6.python的数据类型

7.python列表删除元素的三种方法

8.python yield 和 return 的区别

9.python里面的可哈希对象和不可哈希对象

10.如何对字典根据值进行排序

11.python的lambda函数

12.python中的魔法方法

13.python在内存上有哪些优化

14.python中的类方法和静态方法的区别

15.python的多线程和多进程的实现

16. python的传参与传值

17.猴子补丁

18.python中is与 ==的区别

19,python常用简单写法

20.python2和python3里面的range区别

21.项目里面经常看到__init__.py文件


1.python的深拷贝和浅拷贝

浅拷贝得到的对象与原始对象共享一块内存地址,当一个对象发生改变时,另一个也会发生改变。可以使用切片,copy.copy(),list()等完成浅拷贝。

深拷贝是指创建一个全新的对象,与原始对象互不干扰。可以使用copy.deepcopy().

2.python的多线程可以用多个cpu吗?

因为python每个进程会有一个全局解释器锁的存在(GIL),全局(包括多个cpu)只有一个线程可以占用这个锁。这使得每个python进程只能在一个cpu上运行。那么就有人问了,既然多线程起不到作用,为什么还要用那,那是因为还存在线程阻塞的问题,对于密集i/o操作,网络请求等阻塞型任务时,当一个线程被阻塞了,他可以释放GIL,让其他线程继续工作,可以适当加速,所以还是有用的。

那么我们如何解决GIL的问题?可以使用多进程,每个进程都有各自独立的GIL锁,互不影响。使用其他python解释器,如PYPY。

对于密集IO操作,一般用多线程,对于密集计算型任务,一般使用多进程。多线程共享一个内存空间,所以信息传输更加简单。

3.python垃圾回收机制

python的垃圾回收机制会自动处理对象的内存分配和释放问题。一般有两种回收机制:一种是引用计数,python里的每个对象都有一个计数器,用于记录指向该对象的引用次数,当变为0时,会自动释放。还有一种循环垃圾回收机制,循环引用是指一组对象相互引用形成闭环,且没有任何外部引用指向这个闭环,这时使用循环垃圾回收机制,周期性的检测清理,在进行垃圾回收时,首先会对当前所有对象进行标记,然后回收那些未被标记的对象。这样就会清除掉循环引用等无法通过引用计数判断的对象。

4.迭代器和生成器的区别

迭代器和生成器是python中用于处理可迭代对象的两种不同机制。迭代器学要手动实现__iter__和__next__方法,并在next中定义迭代逻辑。生成器是一种特殊的函数,每次调用生成器时,他会保存当前的执行状态,并在yield语句处暂停,下次再次调用时,会从上次结束的位置继续执行,直到函数结束或遇到下一个yield。生成器简化了迭代器的创建过程,适用于惰性计算。而迭代器适用于复杂的迭代逻辑。生成器可以节省内存,因为它在运行时只保留当前的状态,而不是一次性生成所有值。

当生成器函数被调用时,它并不立即执行,而是返回一个生成器对象。每当生成器的 __next__() 方法被调用时,生成器函数才会从上次暂停的地方继续执行,直到遇到下一个 yield 语句

def fibonacci(): //生成器函数
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 使用生成器逐个打印斐波那契数列的前十个数
fib = fibonacci()
for _ in range(10):
    print(next(fib))
class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        value = self.data[self.index]
        self.index += 1
        return value

# 创建一个列表
my_list = [1, 2, 3, 4, 5]

# 创建一个迭代器对象
my_iter = MyIterator(my_list)

# 使用迭代器遍历列表并打印每个元素
for item in my_iter:
    print(item)

 my_iter是一个迭代器对象,使用for循环对其进行遍历,每次得到的值,都为next中处理后返回的。

5.装饰器

装饰器是一种特殊的函数,用来扩展,包装和修改函数,而无需改变其原始定义。通常采用函数嵌套的形式,接受一个函数作为参数。通过闭包来实现装饰器的定义。

def counter(func):
    count = 0

    def wrapper(*args, **kwargs):
        nonlocal count
        count += 1
        print(f"函数 {func.__name__} 已被调用 {count} 次")
        return func(*args, **kwargs)

    return wrapper

# 使用装饰器修饰目标函数
@counter
def say_hello(name):
    print(f"Hello, {name}!")

# 调用被修饰的函数
say_hello("Alice")
say_hello("Bob")

6.python的数据类型

字符串(string)

列表类型(list)用[]中括号表示。

元组类型(tuple):用小括号表示。

字典类型(dict):{},键值之间用冒号,不同键值对之间用逗号。

集合(set):{}。

7.python列表删除元素的三种方法

del通过索引删除指定位置的元素,或者全部删除。

remove通过值来删除列表中第一个匹配项。

pop通过索引来删除元素,并返回值。

my_list = [1, 2, 3, 4, 5]
del my_list[2]  # 删除索引为2的元素
print(my_list)  # 输出:[1, 2, 4, 5]

del my_list  # 删除整个列表

# 注意:使用 del 关键字删除的元素是永久性删除,不可恢复。



my_list = [1, 2, 3, 4, 5]
my_list.remove(3)  # 删除值为3的元素
print(my_list)  # 输出:[1, 2, 4, 5]

# 如果要删除多个匹配项,需要循环调用 remove 方法。


my_list = [1, 2, 3, 4, 5]
value = my_list.pop(2)  # 删除索引为2的元素,并返回该元素的值
print(my_list)  # 输出:[1, 2, 4, 5]
print(value)  # 输出:3

# 如果不指定索引,pop 方法默认删除最后一个元素,并返回其值。

8.python yield 和 return 的区别

return会结束函数的执行,而yield会返回一个值,并暂停函数的执行,然后记住函数的执行状态,以便从暂停处继续执行。

9.python里面的可哈希对象和不可哈希对象

可哈希对象可以作为字典的键或集合的元素,其包含:数值类型,字符串,元组。不可哈希对象包含:列表,字典,集合

10.如何对字典根据值进行排序

字典的排序需要用到items()方法,根据值来操作,则需要lambda函数

my_dict = {'apple': 5, 'banana': 2, 'orange': 8, 'kiwi': 3}
my_dict = dict(sorted(my_dict.items(), key = lambda x : x[1]))

11.python的lambda函数

lambda函数是一种匿名函数,可以在需要一个简单的函数时使用。后面跟着参数列表和冒号,冒号后面是函数表达式,这个表达式的结果作为lambda的返回值。

# 使用 lambda 函数计算两个数的和
sum_func = lambda a, b: a + b

result = sum_func(3, 4)
print(result)

12.python中的魔法方法

__new__方法:是一个对象实例化之前被调用的方法,他负责创建并返回一个实例对象。参数列表中至少有一个必须是类本身

__init__方法:是一个对象实例化之后调用的方法,它用于对实例对象进行属性的初始化设置。参数列表中至少有一个必须是实例对象,通常被命名为self。

__call__方法:使得一个对象可以像函数一样被调用。当将对象作为函数调用时,会触发__call__ 方法。

13.python在内存上有哪些优化

引用计数和循环垃圾回收机制。迭代器和生成器按需生成数据,而不是一次性生成所有结果。

14.python中的类方法和静态方法的区别

在 Python 中,类方法(class method)和静态方法(static method)都是类中定义的特殊方法,但它们在功能和使用方式上有一些区别。

  1. 类方法(class method):
    • 使用 @classmethod 装饰器来修饰方法,使之成为类方法。
    • 第一个参数被约定为 cls,指向类本身,可以通过该参数访问类的属性和调用类的其他方法。
    • 类方法可以被类本身或类的实例调用。
    • 类方法通常用于执行与类相关的操作,如创建对象、修改类属性等。
  2. 静态方法(static method):
    • 使用 @staticmethod 装饰器来修饰方法,使之成为静态方法。
    • 静态方法没有对类或实例进行特殊处理,因此没有默认的第一个参数指向类或实例。
    • 静态方法可以通过类或实例访问。
    • 静态方法通常用于执行与类相关的功能,但不需要访问类的属性或调用类的其他方法
  • 类方法和静态方法均为类的特殊方法,通过装饰器修饰成特定类型的方法。
  • 类方法通过第一个参数 cls 可以访问类的属性和调用类的其他方法,主要用于执行与类相关的操作。
  • 静态方法没有默认的类或实例参数,可以通过类或实例访问,主要用于执行与类相关的功能,但不需要访问类的属性或调用类的其他方法。
    class MyClass:
        @classmethod
        def class_method(cls):
            print("This is a class method")
    
    # 调用类方法
    MyClass.class_method()
    
    
    
    
    class MyClass:
        @staticmethod
        def static_method():
            print("This is a static method")
    
    # 调用静态方法
    MyClass.static_method()

15.python的多线程和多进程的实现

import threading

# 定义一个函数作为线程的执行体
def my_thread():
    print("Thread started.")

# 创建 10 个线程
threads = []
for i in range(10):
    thread = threading.Thread(target=my_thread)
    thread.start()
    threads.append(thread)

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

print("All threads are done.")

在上面的代码中,我们创建了一个线程列表 threads,然后通过循环创建了 10 个线程,并将它们添加到列表中。每个线程都通过调用 start() 方法启动并开始执行。接着,我们使用另一个循环遍历线程列表,并调用每个线程的 join() 方法,程序会在该方法处阻塞,直到该线程执行完毕才继续执行下一行代码。这样可以确保主线程在所有子线程完成后再继续执行。

import multiprocessing

# 定义一个函数作为进程的执行体
def my_process():
    print("Process started.")

# 创建 10 个进程
processes = []
for i in range(10):
    process = multiprocessing.Process(target=my_process)
    process.start()
    processes.append(process)

# 等待所有进程执行完毕
for process in processes:
    process.join()

print("All processes are done.")

16. python的传参与传值

主要根据参数是可变对象还是不可变对象来说。对于不可变对象,如数值类型,字符串,元组,是值传递,不会影响原始参数。

对于可变对象,如列表,字典,集合等,是引用传递,会直接在原始对象上操作。

17.猴子补丁

是指在运行时,动态修改已有的代码或者类成员。

class MyClass:
    def method(self):
        print("Original method")

# 定义新方法
def new_method(self):
    print("Patched method")

# 将新方法绑定到原始类
MyClass.method = new_method

# 创建对象并调用方法
obj = MyClass()
obj.method()  # 输出: "Patched method"

18.python中is与 ==的区别

is是判断两个对象是否引用了内存中的同一块地址。 == 是判断两者的值是否相等。

19,python常用简单写法

列表生成式  [i for i in range(n)]

字典生成式(元组变为字典)  {x[0] : x[1] for x in turple}

列表切片反转   [::-1]

20.python2和python3里面的range区别

在python2中range()函数会返回一个列表类型,python3会返回一个迭代器。仅在需要的时候才会计算下一个元素,占用内存更少,可以用list(range(n))直接使用。

21.项目里面经常看到__init__.py文件

它可以使当前目录被视为一个包,并提供了进行初始化和控制导入行为的能力,他是构建和组织复杂项目的重要组成部分。__init__.py 文件还可以通过定义 __all__ 变量来控制包的导入行为。__all__ 是一个列表,指定了在使用 from package import * 语句导入包时,应该导入哪些子模块或对象

猜你喜欢

转载自blog.csdn.net/slamer111/article/details/131541402