个人简介:机电专业在读研究生,CSDN内容合伙人,博主个人首页
Python面试专栏:《Python面试》此专栏面向准备面试的2024届毕业生。欢迎阅读,一起进步!
码字不易,如果觉得文章不错或能帮助到你学习,可以点赞收藏评论+关注哦!
本文是Python面试专栏的第一篇。在本专栏中,我将总结华为、阿里巴巴、字节跳动等互联网公司 Python 面试中最常见的 100+ 问题。每道题都提供参考答案,希望能够帮助你在求职面试中脱颖而出,找到一份高薪工作。这些面试题涉及 Python 基础知识、Python 编程、数据分析以及 Python 函数库等多个方面。
Python 是一种高级的、解释型的、通用的编程语言。作为一种通用语言,它可以使用合适的工具/库来构建几乎任何类型的应用程序。此外,Python 还支持对象、模块、线程、异常处理和自动内存管理,这有助于对现实世界的问题进行建模,并构建应用程序来解决这些问题。
使用 Python 的好处:
在理解动态类型语言之前,我们应该先了解什么是类型检查。
类型检查是一个验证和施加类型约束的过程,编译器或解释器通常在编译或运行阶段做类型检查。用更简单的术语,类型检查仅仅就是查看变量和它们的类型,然后说这个表达式是合理的。
因此,现在我们知道类型检查是什么,明白这些术语真的很简单。
静态类型:编译阶段做更多处理,但是运行时性能更好
动态类型:编译阶段更高效,但是运行时的类型检查会影响到性能
静态类型:运行时出错机会更小,但是提供给程序员的灵活性不好
动态类型:提供更多的灵活性但是运行时出错机会相对更大
首先看下什么是强类型,在强类型中,不管在编译时还是运行时,一旦某个类型赋值给某个变量,它会持有这个类型,并且不能同其他类型在计算某个表达式时混合计算
然而,在弱类型中,它是很容易与其他类型混合计算的。
例如,在 Python 中,“1”+ 2 将导致类型错误,因为这些语言不允许 “类型强制转换”(数据类型的隐式转换)。然而,在另一门同样伟大的语言 Javascript 中,将简单地输出 “12” 作为结果。
各种语言按照动态/静态,强/弱类型的划分:
解释型语言逐行执行它的语句。Python、Javascript、R、PHP 和 Ruby 等语言是解释型语言的主要例子。用解释型语言编写的程序直接从源代码运行,没有中间的编译步骤。
PEP 8 是 Python 官方推荐的编码规范。其中 PEP 是 Python Enhancement Proposal(Python 增强建议书)的缩写,8 代表的是 Python 代码的样式指南。PEP 8 是向 Python 社区提供信息的官方设计文档,或者描述 Python 或其进程的新特性。PEP 8 特别重要,因为它记录了Python 代码的样式指南。显然,为使代码易读、易懂、易维护需要真诚而严格地遵循这些样式指南。
Python 中的每个对象都在一个作用域内运行。作用域是 Python 中对象保持相关的代码块。命名空间唯一地标识程序中的所有对象。但是,这些名称空间也有一个为它们定义的作用域,可以在没有任何前缀的情况下使用它们的对象。Python 代码执行过程中创建作用域的几个例子如下:
注意:局部作用域对象可以使用
global
关键字与全局作用域对象同步。
列表和元组都是序列数据类型,可以在 Python 中存储数据。存储在这两个序列中的数据可以具有不同的数据类型。列表用方括号表示,而元组用圆括号表示。
但两者的真正区别是什么呢?两者之间的关键区别在于,列表是可变的,而元组是不可变的对象。这意味着列表可以随时修改、追加或切片,但元组保持不变。可以在 Python IDLE 上运行以下示例来确认差异:
my_tuple = ('sara', 6, 5, 0.97)
my_list = ['sara', 6, 5, 0.97]
print(my_tuple[0]) # output => 'sara'
print(my_list[0]) # output => 'sara'
my_tuple[0] = 'ansh' # modifying tuple => throws an error
my_list[0] = 'ansh' # modifying list => list modified
print(my_tuple[0]) # output => 'sara'
print(my_list[0]) # output => 'ansh'
Python 中有几种内置数据类型。虽然 Python 不要求在变量声明期间显式定义数据类型,但如果忽略数据类型的知识及其相互兼容性,则可能会发生类型错误。Python 提供 type()
和 isinstance()
函数来检查这些变量的类型。这些数据类型可分为以下几类:
None Type:
关键字 None
表示 Python 中的空值。可以使用这些 NoneType 对象执行布尔相等运算。
类名 | 描述 |
---|---|
NoneType | 表示 Python 中的 NULL 值。 |
数值类型:
有三种不同的数值类型——整数、浮点数和复数。此外,布尔类型是整数类型的子类型。
类名 | 描述 |
---|---|
整型 | 将整数文本(包括十六进制、八进制和二进制数)存储为整数。 |
浮点型 | 将包含十进制值和/或指数符号的文本存储为浮点数。 |
复数型 | 以(A+Bj)的形式存储复数,并具有属性:real 和 imag 。 |
布尔型 | 存储布尔值(True 或 False)。 |
序列类型:
根据 Python 文档,有三种基本的序列类型—— 列表、元组和 range 对象。序列类型定义了用于遍历其元素的 in
和 not in
运算符。这些运算符与比较操作具有相同的优先级。
类名 | 描述 |
---|---|
列表 | 通常用于存放同类项目集合的可变序列。 |
元组 | 通常用于储存异构数据多项集的不可变序列。 |
range 对象 | 在执行期间生成的不可变的数字序列。 |
字符串 | 由 Unicode 码位构成的不可变序列。 |
注意: 标准库还包括用于处理的其他类型:二进制数据,如 bytes、bytearray 和 memoryview。
映射类型:
映射对象可以将可哈希值映射到 Python 中的随机对象。映射对象是可变的,目前只有一种标准映射类型,即字典。
类名 | 描述 |
---|---|
字典 | 存储以逗号分隔的键:值对列表。 |
集合类型:
目前,Python 有两种内置的集合类型—— set 和 frozenset。 set 类型是可变的,其内容可以使用 add()
和 remove()
这样的方法来改变。 由于是可变类型,它没有哈希值,且不能被用作字典的键或其他集合的元素。 frozenset 类型是不可变并且为 hashable,其内容在被创建后不能再改变,因此它可以被用作字典的键或其他集合的元素。
类名 | 描述 |
---|---|
set | 不同可哈希对象的可变无序集合。 |
frozenset | 不同可哈希对象的不可变集合。 |
模块:
模块是 Python 解释器支持的附加内置类型。模块唯一的特殊操作是属性访问:m.name,这里 m 为一个模块而 name为定义在 m 的符号表中的一个名称。 模块属性可以被赋值。
可调用类型:
可调用类型是可以应用函数调用的类型。它们可以是用户定义的函数、实例方法、生成器函数以及其他一些内置函数、方法和类。
有关 Python 中数据类型的详细,可以参阅 docs.python.org 中的文档。
关键字 pass
表示 Python 中的空操作。它通常用于填充可能在运行时执行但尚未编写的空代码块。如果没有以下代码中的 pass
语句,我们可能会在代码执行过程中遇到一些错误。
def myEmptyFunc():
# do nothing
pass
myEmptyFunc() # nothing happens
## Without the pass keyword
# File "", line 3
# IndentationError: expected an indented block
Python 包和模块是允许在 Python 中进行模块化编程的两种机制。模块化有几个优点:
一般来说,模块只是具有 .py 扩展名的 Python 文件,可以定义和实现一组函数、类或变量。它们可以使用语句导入并初始化一次。如果需要部分功能,使用 import ... from ...
导入必需的类或函数
包允许使用点符号对模块命名空间进行分层结构。模块有助于避免全局变量名之间的冲突,同样,包有助于避免模块名之间的冲突。创建包很容易,因为它利用了系统固有的文件结构。所以只要把模块塞进一个文件夹,就可以了,文件夹名就是包名。从这个包中导入模块或其内容需要将包名作为模块名的前缀,并加上一个点。
全局变量是在全局范围内定义的公共变量。要在函数内的全局范围内使用该变量,我们使用关键字 global
。
受保护属性是用前缀在其标识符上的下划线定义的属性,例如。_萨拉。仍然可以从定义它们的类之外访问和修改它们,但负责任的开发人员应避免这样做。
私有属性是在其标识符上带有双下划线前缀的属性,例如,__ansh
。它们不能直接从外部访问或修改,如果进行此类尝试,将导致 AttributeError。
self 在 Python 中是一个特殊的参数,用于表示对象自身。它通常作为方法的第一个参数传递,并用于访问对象的属性和方法。
在类定义中,我们可以使用 self 来引用当前实例对象的属性和方法。通过 self,我们可以在类的方法中操作对象的状态,并将其封装起来以供其他方法调用。
举个例子,在一个 Person 类中,我们可以定义一个名为 set_name 的方法,使用 self 参数来设置对象的姓名属性:
class Person:
def set_name(self, name):
self.name = name
在这个例子中,self 参数表示当前实例对象,我们可以通过 self.name 来访问该对象的 name 属性。
总而言之,self 在 Python 中用于表示对象自身,在类的方法中使用 self 可以方便地访问和操作对象的属性和方法。
__init__是Python中的一个特殊方法,用于在创建类的实例时进行初始化操作。它被称为构造函数或初始化方法。
当我们使用类创建对象时,Python 会自动调用该类的__init__方法来执行一些初始化任务。在__init__方法中,我们可以定义对象的属性并给它们赋初始值。
举个例子,假设我们有一个 Person 类,希望在创建 Person 对象时可以设置姓名和年龄。可以在类中定义一个__init__方法,并接受 name 和 age 两个参数来初始化对象的属性:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
当我们创建一个 Person 对象时,可以传递相应的参数来初始化对象的属性:
person = Person("Alice", 25)
在这个例子中,当我们执行 Person("Alice", 25)
时,Python 会自动调用 Person 类的__init__方法,并将 “Aliice” 和 25 作为参数传递给该方法。在__init__方法中,通过 self.name 和 self.age 来分别初始化对象的 name 和 age 属性。
break | break 语句立即终止循环,控制程序运行到循环体后面的语句。 |
continue | continue 语句是 break 语句的小版本,跳过当前迭代中的其余代码,控制程序运行到循环的下一个迭代。 |
pass | 如上所述,Python 中的 pass 关键字通常用于填充空块,类似于 Java、C++、Javascript 等语言中由分号表示的空语句。 |
pat = [1, 3, 2, 1, 2, 3, 1, 0, 1, 3]
for p in pat:
pass
if (p == 0):
current = p
break
elif (p % 2 == 0):
continue
print(p) # output => 1 3 1 3 1
print(current) # output => 0
Python 中的单元测试是一种侧重于验证软件系统中单个单元或组件的正确性的测试。单元通常指的是一小段独立的代码,如函数、方法或类。
单元测试的目的是确保每个代码单元按预期执行,并在给定特定输入的情况下产生所需的输出。通过编写和运行单元测试,开发人员可以在开发过程的早期检测和修复错误,在进行更改时最小化引入新错误的风险,并提高代码库的整体质量和可靠性。
在 Python 中,单元测试通常使用测试框架(如 unittest、pytest 或 nose)创建。这些框架为定义测试用例、执行测试用例和报告结果提供了实用工具和断言。
典型的单元测试包括:
通过定期运行单元测试,开发人员可以获得对代码行为的信心,促进团队成员之间的协作,并确保对代码库的修改或添加不会引入回档。
下面是一个带有 docstring 的函数示例:
def add(a, b):
"""
Adds two numbers and returns the result.
Args:
a (int): The first number.
b (int): The second number.
Returns:
int: The sum of the two numbers.
"""
return a + b
[start : end : step]
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers[1 : : 2]) # output : [2, 4, 6, 8, 10]
要在 Unix 上使 Python 脚本可执行,可以按照以下步骤进行操作:
#!/usr/bin/env python
这将告诉系统使用 env 来查找 Python 解释器,并使用找到的解释器来运行脚本。如果你的 Python 解释器的路径不是 /usr/bin/env python
,请相应地更改该行。
chmod
命令为脚本添加可执行权限。chmod +x script.py
这将允许直接运行脚本而无需在命令前添加 python 关键字。
./script.py
注意,脚本文件必须位于当前工作目录中,或者必须提供脚本的完整路径。
这样,就可以在 Unix 上使 Python 脚本可执行了。
在 Python 中,数组和列表是两种不同的数据结构。它们有以下区别:
内存分配方式:数组通常在内存中以连续的块存储数据,这使得它们在访问元素时具有更快的速度。而列表是由指针链表实现的,每个节点都包含指向下一个节点的指针。
数据类型:数组只能包含相同数据类型的元素,即数组的数据类型应该是同类的,消耗的内存远少于列表。而列表可以包含不同数据类型的元素,即列表的数据类型可以是异构的,它具有消耗大量内存的缺点。这意味着数组支持更广泛的数值计算操作,如矩阵运算等。
大小调整:数组的大小是固定的,一旦创建就无法更改。如果需要更改数组的大小,通常需要创建一个新的数组并复制原始数据。而列表的大小是可变的,可以根据需要添加或删除元素。
考虑到上述区别,当需要进行数值计算和处理时,使用数组可能更有效。而当需要灵活性和动态调整大小时,列表可能更适合。