(三十三)补充Python经典面试题(吸收高级编程特性)

2023-12-18 11:36

本文主要是介绍(三十三)补充Python经典面试题(吸收高级编程特性),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第一题:

def func(a, b=[]): pass

一、上题讲解:

这个函数定义有一个默认参数b,它的默认值是一个空列表[]。这道面试题涉及到Python中函数参数默认值的一些重要概念和陷阱

首先,当你调用这个函数时,如果不传递参数b的值,它将使用默认的空列表[]。例如:

func(1)  # 这会将a设置为1,b设置为默认的空列表[]

但是,这里有一个陷阱。默认参数b(即空列表[])在函数定义时只会被创建一次,而不是每次函数调用时都会创建一个新的空列表。这就意味着,如果你在一个函数调用中修改了b的值,那么下一次调用该函数时,b将保留上一次的修改。

例如:

func(1)  # a=1, b=[]
b.append(2)
func(3)  # a=3, b=[2]

在上面的例子中,我们首先调用了func(1),然后在b上执行了append(2)操作,导致b变成了[2]。接下来,我们调用了func(3),此时a被设置为3,但b仍然是[2],而不是一个新的空列表[]。

这种行为可能会导致一些不直观的问题和bug,因为使用者可能期望每次调用函数时都会得到一个独立的空列表。

为了避免这种问题,我们可以使用None作为默认值,并在函数内部检查b是否为None,然后在需要时创建一个新的空列表。例如:

def func(a, b=None):if b is None:b = []# 现在每次函数调用都会得到一个新的空列表# 其他函数逻辑

这样做可以确保每次调用函数时都会得到一个新的空列表,避免了默认参数共享的问题。

二、是什么导致的上述问题:

这是因为在Python中,默认参数在函数定义时只会被创建一次,并且在函数的整个生命周期内都会保留它们的状态。这是为了提高函数的性能和效率。

当你定义一个函数时,Python会在函数的定义阶段创建默认参数的值,然后将这些值存储在函数的代码对象中。这意味着每次调用函数时,不会重新创建默认参数的新实例,而是会重用已经存在的默认参数。

这种行为有一些优点和一些潜在的陷阱:

优点:

  1. 提高了函数的性能,因为不需要每次函数调用都创建新的默认参数对象。
  2. 可以实现一些有用的功能,例如在多次函数调用之间共享状态。这可以在某些情况下很有用。

潜在的陷阱:

  1. 如果默认参数是可变对象(如列表或字典),并且在函数内部进行了修改,那么这些修改会在后续函数调用中保留下来,可能导致不直观的行为。
  2. 开发者需要谨慎处理默认参数,以避免意外共享状态的问题。

要避免默认参数共享状态的问题,可以使用None作为默认参数的值,并在函数内部检查并创建新的实例,如上解决的方法所示。

总之,Python的默认参数在函数定义时只会被创建一次,这是出于性能和实现的考虑,但在使用可变对象作为默认参数时需要特别小心,以避免不希望的副作用。

第二题:

val = [lambda: i + 1 for i in range(10)]data = val[0]()
print(data)

这道面试题涉及到Python中的lambda函数和列表推导式,并且可能会引发一个常见的陷阱,即闭包与变量作用域的问题。

将上述代码拆开来看:

def a():return i + 1s = []
for i in range(10):s.append(a)print(s[0]())

相信很多小伙伴看到上述拆开的代码都已经能理解本道面试题的精髓所在了。
但也请继续看下原理,是否和你想的一样~

讲解原理:

首先,列表推导式 [lambda: i + 1 for i in range(10)] 创建了一个包含 10 个 lambda 表达式的列表,每个 lambda 表达式在调用时都会返回 i + 1 的值。**需要注意的是这里每个 lambda 表达式都是一个闭包,它们“记住”了变量 i 的值。 然而,关键之处在于 lambda 表达式记住的是变量 i 而非 i 当时的值。**由于列表推导式内的 i 是在单个作用域内循环的,因此当列表推导式结束时,i 的值将停留在最后一次循环的值,即 9。 之后的代码 data = val[0]选择列表中的第一个 lambda 函数并调用它。因为所有的 lambda 闭包都是对同一个 i 的引用,这时 i 的值是循环结束时的值 9。因此,无论调用列表中的哪一个 lambda 表达式,它都能返回 9 + 1,即 10。

这是因为,**在 Python 的 for 循环中,循环变量 i 会被绑定到列表推导式的外部作用域,而不是每次迭代都创建一个新的作用域。**所以,所有的 lambda 表达式都引用着同一个 i 变量,而在循环结束时,i 的值为 9。 要让每个 lambda 表达式保留它被定义时的 i 值,可以使用默认参数来捕获i的值,以确保每个lambda函数都捕获到不同的值:

val = [lambda i=i: i + 1 for i in range(10)]data = val[0]()
print(data)

上面修改后的代码中,lambda i=i: i + 1 为每个 lambda 函数创建了一个默认参数 i,它的值在定义 lambda 函数时就被确定下来了。这时,val[0]会输出 1,因为它将使用列表推导式中第一次迭代时 i 的值,即 0,然后加 1。

第三题:

老生常谈,请讲一讲迭代器,生成器,可迭代对象,装饰器,并讲一下它们各自的应用场景。

首先,迭代器(Iterator)、生成器(Generator)、可迭代对象(Iterable)都与遍历数据集合相关,但各有特点,所以放一起讲:

1.1 迭代器(Iterators):

迭代器是遵循迭代器协议的对象,这意味着迭代器对象需要实现两个方法:__iter__()__next__()__iter__() 返回迭代器对象本身,而 __next__() 方法返回容器中的下一个元素。当迭代器中没有更多元素时,__next__()应该抛出一个 StopIteration 异常。迭代器允许一个对象对一组数据进行遍历,但不需要此数据在内存中完全展开。

  • Python的内置容器类型:

    如列表、元组、字典等,都提供了迭代器。例如,当你在列表上调用 __iter__()函数时,会返回一个迭代器,该迭代器可以遍历列表的所有元素。

  • 使用场景:

    当需要访问集合中的元素而不暴露底层表示时;

    当需要一个能够记住遍历位置的对象时,以便在需要时能够从同一位置继续。

1.2 生成器(Generators):

生成器是一种特殊的迭代器,更容易编写。**当需要一次一个地按顺序生成一个序列的值时,使用生成器是非常有用的。**生成器函数使用 yield 语句,每次产生(yield)一个值,函数的状态会被挂起,直到下一个值被请求时再恢复。 生成器表达式是另一种构建生成器的方式,它看起来像列表推导式,但使用圆括号而不是方括号。

  • 使用场景:

    当需要一个懒序列(lazy sequence),该序列按需计算元素而不是预先计算,并且不希望一次性加载所有元素到内存中;

    当处理的是流式数据或大数据集合,只需要一次处理一部分数据;

    当需要一个函数来生成无穷序列下的元素。

1.3 可迭代对象(Iterables):

可迭代对象是实现了 __iter__() 方法的任何 Python 对象,__iter__() 需要返回一个迭代器。另外,可迭代对象也可以实现 __getitem__() 方法,以便按照索引访问元素。字符串、列表等 Python 标准类型都是可迭代的。

class Demo(object):def __iter__(self):return iter([1, 2, 3])obj = Demo()
  • 使用场景:

    在使用 for 循环时,你通常会迭代一个可迭代对象;

    当需要一种方式可以一次访问一组元素,而无需将它们全部保存在内存中;

    在使用 map()、filter()、sum()、min()、max() 等内置函数时,这些函数接受一个可迭代对象作为参数

1.4 总结一下:

在 Python 中,迭代器、生成器和可迭代对象是集合数据访问的三个基本概念。迭代器提供了一种通用的遍历集合数据的方法,而生成器提供了一种生成迭代数据的简洁方式,可迭代对象则定义了可以生成迭代器的对象。它们的共同目的是为了在保持代码简洁的同时,有效地处理数据集合,尤其是在数据量非常大或者是无限的情况下。 理解并掌握这些概念对于编写高效和可读性高的 Python 代码非常重要。每个概念都在数据处理和控制流的抽象中扮演着关键角色,并广泛应用于数据分析领域、系统操作领域和网络编程等领域。

2.0 装饰器(Decorator):

装饰器是Python中的一种高级编程特性,**它允许你在不修改原始函数代码的情况下,动态地增强或修改函数的行为。**装饰器通常用于代码重用、添加功能、修改函数的输入/输出等方面,它是Python函数式编程的一部分,非常强大和灵活。

  1. 函数装饰器

    • 装饰器本质上是一个Python函数,它接受一个函数作为参数,并返回一个新的函数。
    • 装饰器函数通常在函数定义之前使用@符号来装饰目标函数。
    • 装饰器的主要作用是在不修改原函数代码的情况下,为函数添加额外的功能或修改其行为。
  2. 装饰器示例
    下面是一个简单的装饰器示例,它用于测量函数的执行时间:

    import timedef timing_decorator(func):def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"{func.__name__} took {end_time - start_time} seconds to execute.")return resultreturn wrapper@timing_decorator
    def my_function():# Some time-consuming tasktime.sleep(2)my_function()

    在这个示例中,timing_decorator装饰器测量了my_function函数的执行时间,而不需要修改my_function的源代码。

  3. 多个装饰器
    你可以为一个函数应用多个装饰器,它们按照从上到下的顺序执行。这允许你将不同的功能组合在一起,以增强函数的行为。

    @decorator1
    @decorator2
    def my_function():# ...# 等效于
    my_function = decorator1(decorator2(my_function))
    
  4. 内置装饰器
    Python提供了一些内置装饰器,如@staticmethod@classmethod,用于定义静态方法和类方法。这些装饰器可以用于类中的方法,以提供不同类型的方法调用。

    class MyClass:def __init__(self, value):self.value = value@staticmethoddef static_method():print("This is a static method")@classmethoddef class_method(cls):print("This is a class method")obj = MyClass(42)
    obj.static_method()
    obj.class_method()
  5. 自定义装饰器
    你可以自己编写装饰器函数,以满足特定需求。通常,自定义装饰器需要接受函数作为参数,并返回一个包装函数。装饰器函数可以在包装函数的前后执行自定义逻辑。

装饰器是Python中强大而灵活的工具,它们用于增强函数的功能、提供代码重用和简化代码结构。常见的装饰器包括日志记录、性能分析、权限验证、缓存等。理解和熟练使用装饰器是成为高级Python开发人员的关键一步。

这篇关于(三十三)补充Python经典面试题(吸收高级编程特性)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/508233

相关文章

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

Python pandas库自学超详细教程

《Pythonpandas库自学超详细教程》文章介绍了Pandas库的基本功能、安装方法及核心操作,涵盖数据导入(CSV/Excel等)、数据结构(Series、DataFrame)、数据清洗、转换... 目录一、什么是Pandas库(1)、Pandas 应用(2)、Pandas 功能(3)、数据结构二、安

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

Python安装Pandas库的两种方法

《Python安装Pandas库的两种方法》本文介绍了三种安装PythonPandas库的方法,通过cmd命令行安装并解决版本冲突,手动下载whl文件安装,更换国内镜像源加速下载,最后建议用pipli... 目录方法一:cmd命令行执行pip install pandas方法二:找到pandas下载库,然后

Python实现网格交易策略的过程

《Python实现网格交易策略的过程》本文讲解Python网格交易策略,利用ccxt获取加密货币数据及backtrader回测,通过设定网格节点,低买高卖获利,适合震荡行情,下面跟我一起看看我们的第一... 网格交易是一种经典的量化交易策略,其核心思想是在价格上下预设多个“网格”,当价格触发特定网格时执行买

Python标准库之数据压缩和存档的应用详解

《Python标准库之数据压缩和存档的应用详解》在数据处理与存储领域,压缩和存档是提升效率的关键技术,Python标准库提供了一套完整的工具链,下面小编就来和大家简单介绍一下吧... 目录一、核心模块架构与设计哲学二、关键模块深度解析1.tarfile:专业级归档工具2.zipfile:跨平台归档首选3.

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

Python进行JSON和Excel文件转换处理指南

《Python进行JSON和Excel文件转换处理指南》在数据交换与系统集成中,JSON与Excel是两种极为常见的数据格式,本文将介绍如何使用Python实现将JSON转换为格式化的Excel文件,... 目录将 jsON 导入为格式化 Excel将 Excel 导出为结构化 JSON处理嵌套 JSON:

Python操作PDF文档的主流库使用指南

《Python操作PDF文档的主流库使用指南》PDF因其跨平台、格式固定的特性成为文档交换的标准,然而,由于其复杂的内部结构,程序化操作PDF一直是个挑战,本文主要为大家整理了Python操作PD... 目录一、 基础操作1.PyPDF2 (及其继任者 pypdf)2.PyMuPDF / fitz3.Fre

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统