Skip to main content

1、曾经被问到的面试题

Y-aong...About 11 minpythonpython面试

1、曾经被问到的面试题

1、python中的鸭子对象

一句话概述,看起来像是鸭子,走路像鸭子,叫声像鸭子他就是鸭子

对于代码来说,比如可以iter(obj) == True 就是可迭代对象,str, list,没有__next__方法,但是它实现了__getitem__()方法,所以也是可迭代对象

2、python3python2中for * in xx,xx是什么,为什么要改变

可迭代对象

具有惰性计算特点的序列称为惰性序列,Python 中的迭代器就是一个惰性序列,调用 iter() 返回一个 iterator 并赋值给一个变量后不会立即进行求值,而是当你用到其中某些元素的时候才去求某元素的值。

惰性计算还可以在大规模数据处理中平滑处理时间,提高内存使用率。当处理大规模数据时,一次性进行处理往往是不方便的。

3、python中的深浅拷贝

浅拷贝通常只复制对象本身,而深拷贝不仅会复制对象,还会递归的复制对象所关联的对象。深拷贝可能会遇到两个问题:一是一个对象如果直接或间接的引用了自身,会导致无休止的递归拷贝;二是深拷贝可能对原本设计为多个对象共享的数据也进行拷贝。Python通过copy模块中的copydeepcopy函数来实现浅拷贝和深拷贝操作,其中deepcopy可以通过memo字典来保存已经拷贝过的对象,从而避免刚才所说的自引用递归问题;此外,可以通过copyreg模块的pickle函数来定制指定类型对象的拷贝行为。

deepcopy函数的本质其实就是对象的一次序列化和一次返回序列化,面试题中还考过用自定义函数实现对象的深拷贝操作,显然我们可以使用pickle模块的dumpsloads来做到,代码如下所示。

import pickle

my_deep_copy = lambda obj: pickle.loads(pickle.dumps(obj))

列表的切片操作[:]相当于实现了列表对象的浅拷贝,而字典的copy方法可以实现字典对象的浅拷贝。对象拷贝其实是更为快捷的创建对象的方式。在Python中,通过构造器创建对象属于两阶段构造,首先是分配内存空间,然后是初始化。在创建对象时,我们也可以基于“原型”对象来创建新对象,通过对原型对象的拷贝(复制内存)就完成了对象的创建和初始化,这种做法更加高效,这也就是设计模式中的原型模式。

4、正则表达式的match方法和search方法有什么区别?

match方法是从字符串的起始位置进行正则表达式匹配,返回Match对象或None。search方法会扫描整个字符串来找寻匹配的模式,同样也是返回Match对象或None。

5、Python中为什么没有函数重载

首先Python是解释型语言,函数重载现象通常出现在编译型语言中。其次Python是动态类型语言,函数的参数没有类型约束,也就无法根据参数类型来区分重载。再者Python中函数的参数可以有默认值,可以使用可变参数和关键字参数,因此即便没有函数重载,也要可以让一个函数根据调用者传入的参数产生不同的行为。

6、python中为什么要引入全局解释器锁

Python引入全局解释器锁(GIL,Global Interpreter Lock)的主要原因是为了简化Python解释器的设计和实现,并确保解释器内部数据结构在多线程环境下的安全性
GIL是Python解释器中的一种机制,它是一把全局锁,用于保护解释器免受多线程并发访问的影响。这意味着在Python中,同一时刻只允许一个线程执行Python字节码。
当一个线程执行Python字节码时,其他线程将被阻塞,即使系统具有多个CPU核心,Python的多线程程序也不能同时利用它们。

GIL的引入可以追溯到Python的早期设计。在Python的设计初期,为了简化解释器的实现,并确保多线程环境下的线程安全,设计者决定引入GIL。通过GIL,Python解释器不需要在共享数据上实现复杂的同步机制,从而降低了实现的复杂性。然而,GIL的存在也带来了一些限制和挑战。由于GIL的存在,Python的多线程在CPU密集型任务上并不能提供真正的并行性。这意味着在多核CPU上,Python的多线程程序可能无法充分利用硬件资源。

为了解决这个问题,Python社区已经提出了一些解决方案,如使用多进程(multiprocessing)代替多线程,或者使用支持并行计算的库(如NumPy、SciP等)。

总之,Python引入GIL是为了简化解释器的设计和实现,并确保多线程环境下的线程安全。然而,这也带来了一些限制和挑战,需要在使用Python多线程时特别注意。

7、如何理解异步IO

  1. 同步 vs. 异步
    • 同步(Synchronous):同步操作是指在发起一个操作后,必须等待该操作完成才能继续执行后续的操作。在同步操作中,程序会阻塞(Block)当前线程或进程,直到操作完成。
    • 异步(Asynchronous):异步操作是指在发起一个操作后,可以立即返回并继续执行后续的操作,而不必等待该操作完成。在异步操作中,程序不会阻塞当前线程或进程,而是使用回调函数、事件循环等机制来处理操作的结果。
  2. IO操作
    • 输入/输出(IO)操作:指的是与外部设备(例如磁盘、网络等)进行数据交换的操作。在计算机中,IO操作是相对于CPU执行的计算操作而言的。常见的IO操作包括读取文件、发送网络请求、接收网络响应等。
  3. 异步IO
    • 异步IO(Asynchronous IO):指的是在进行IO操作时,不需要等待IO操作完成才能继续执行后续的操作。相反,程序可以继续执行其他任务,而IO操作在后台进行。当IO操作完成时,程序可以通过回调函数、事件通知等方式获取IO操作的结果。

在编程中,异步IO通常与事件循环(Event Loop)结合使用,例如在Python中,使用 asyncio 模块来实现异步IO操作。异步IO的优点在于它可以提高程序的并发性和吞吐量,使程序能够更有效地利用系统资源,从而提高性能

总的来说,理解异步IO就是理解在IO操作时,程序如何以非阻塞的方式继续执行其他任务,并在IO操作完成后获取操作结果的过程。

8、异步IO和事件循环的关系

在 Python 中,异步IO和事件循环密切相关,它们通常一起使用来实现异步编程。

  1. 事件循环(Event Loop)
    • 事件循环是一个在程序中运行的循环,它负责处理和调度异步任务(例如IO操作、定时器等)。
    • 在事件循环中,任务被添加到事件队列中,并在适当的时候执行。事件循环负责选择要执行的任务,并确保任务按正确的顺序执行。
    • Python 中常用的事件循环实现是 asyncio 模块提供的 asyncio.EventLoop
  2. 异步IO(Asynchronous IO)
    • 异步IO是一种编程模型,它允许程序在执行IO操作时不阻塞当前线程或进程,而是可以继续执行其他任务。
    • 在Python中,异步IO通常使用 asyncio 模块来实现,它提供了异步编程的基础设施,包括异步IO操作、协程等。
  3. 关系
    • 异步IO依赖于事件循环来调度和执行异步任务。当需要执行一个异步IO操作时,任务会被添加到事件循环的事件队列中,并在事件循环的控制下执行。
    • 事件循环负责管理异步任务的执行顺序、任务的状态和执行过程中的异常处理等。
    • 在事件循环中,异步IO操作通常以协程(Coroutine)的形式表示,而协程是一种可以暂停和恢复执行的函数,适用于异步编程。

简而言之,事件循环是异步IO编程的基础,它负责调度和执行异步任务,而异步IO操作则是在事件循环的管理下执行的。通过事件循环,异步IO可以实现非阻塞的IO操作,并使程序能够更高效地利用系统资源

9、__new__ 方法是什么?

__new__ 方法是Python中的一个特殊的静态方法,用于创建类的新实例。它是在__init__方法之前被调用的,并且负责返回类的新实例。__new__方法通常不需要被直接调用,而是由Python解释器在实例化对象时自动调用。

__new__方法的主要作用是创建一个新对象,而__init__方法则用于初始化这个新创建的对象。__new__方法只接收类本身(通常用cls表示)作为第一个参数,后面可以跟任意数量的参数,这些参数将传递给__init__方法。

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

    def __init__(self):
        pass  # 这里可以初始化实例的属性

# 创建Singleton的两个实例
singleton1 = Singleton()
singleton2 = Singleton()

# 检查两个实例是否是同一个对象
print(singleton1 is singleton2)  # 输出: True

__new__方法通常用于以下情况:

  1. 控制对象的创建:比如实现单例模式。
  2. 继承不可变类型:比如intstrtuple等,因为这些类型的实例是不可变的,所以需要在__new__方法中创建新实例。
  3. 多态实例化:根据传入的参数动态决定创建哪种类型的对象。

在大多数情况下,你不需要自定义__new__方法,除非你有特殊的对象创建需求。通常情况下,只需要定义__init__方法来初始化对象即可。

10、__init__ 方法是什么?

__init__ 方法是Python中类的构造器,用于在创建类的新实例时初始化对象。当你创建一个新对象时,Python会自动调用__init__方法。这个方法通常用于设置对象的初始状态,比如给对象的属性赋初始值。

__init__方法的第一个参数始终是self,它代表类的实例本身,允许我们访问类的属性和方法。在__init__方法中,你可以定义其他参数来接收初始化数据,并根据这些数据来设置对象的状态。

如果你不显式定义__init__方法,Python会提供一个默认的__init__方法,这个默认的方法什么也不做。自定义__init__方法可以让你控制对象创建时的行为。

11、__new__ 的作用

依照Python官方文档的说法,__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。 首先我们来看一下第一个功能,具体我们可以用int来作为一个例子: 假如我们需要一个永远都是正数的整数类型,通过集成int,我们可能会写出这样的代码:

工厂模式的实现

class Fruit(object):
    def __init__(self):
        pass

    def print_color(self):
        pass

class Apple(Fruit):
    def __init__(self):
        pass

    def print_color(self):
        print("apple is in red")

class Orange(Fruit):
    def __init__(self):
        pass

    def print_color(self):
        print("orange is in orange")

class FruitFactory(object):
    fruits = {"apple": Apple, "orange": Orange}

    def __new__(cls, name):
        if name in cls.fruits.keys():
            return cls.fruits[name]()
        else:
            return Fruit()

fruit1 = FruitFactory("apple")
fruit2 = FruitFactory("orange")
fruit1.print_color()    
fruit2.print_color()    

13、__init____new__的区别

从上述过程中我们可以发现,这两个方法区别在于:

  • 作用区别,init实例级别,new类级别

    • 1.__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
    • 2.__new__ 通常用于控制生成一个类实例的过程。它是类级别的方法
  • 执行顺序,先new 后init

Comments
  • Latest
  • Oldest
  • Hottest
Powered by Waline v2.15.8