中级面试题
什么是鸭子类型
面试题目
- 级别: L2
- 知识模块: Python 编程语言
请说一下什么是 Python 中的鸭子类型?
公司
- 美团
招聘类型
社招
题目解析
此题目是考察对 Python 面象对象及 Python 特性的理解深度
答案
鸭子类型的概念
- 鸭子类型(Duck Typing)是动态类型语言中的一个概念,源自一句格言:“如果它走起路来像鸭子,叫起来像鸭子,那么它就是鸭子。”
- 这个概念在程序设计中指的是关注对象的行为(方法和属性)而不是对象的类型。
鸭子类型的特征
- 在鸭子类型中,关注的是对象是否具有特定的方法和属性,而不是关注对象的具体类型。
- 当代码中需要调用特定的方法或属性时,程序会查找对象是否具有这些方法或属性,而不关心对象的具体类型。
- 如果对象拥有对应的方法或属性,在运行时就可以被调用,即使对象的类型与要求的类型不同。
鸭子类型的应用
- 一个经典的鸭子类型的例子是在 Python 中使用迭代器。
- 在 Python 中,只要对象实现了
__iter__()
和__next__()
方法,它就可以被视为一个迭代器,即使它并非继承自任何特定的迭代器类。
鸭子类型的优势
- 鸭子类型的思想使代码更加灵活,可以适应不同的对象类型,同时也鼓励了面向对象的设计原则中的接口隔离和依赖倒置。
- 通过鸭子类型,程序可以更加简洁、可扩展,同时减少了对类型的依赖,提高了代码的易维护性和可读性。
什么是多态
面试题目
- 级别: L2
- 知识模块: Python 编程语言
请讲一下 Python 中的多态。
公司
- 美团
招聘类型
社招
题目解析
这个问题考察的是对Python面向对象编程及其特性(尤其是多态)的理解深度。多态是面向对象编程中的一个核心概念,它允许不同类的对象以相同的方式被操作,从而提供了灵活性和可扩展性。
在 Python 中,多态是面向对象程序设计中的一个重要概念。它指的是同一个方法调用可以在不同对象上有不同的行为。换句话说,多态允许使用相同的接口来调用不同对象的方法,而根据对象的类型,会产生不同的行为。
-
在多态性下,对象对同一消息可以作出多种反应。这样就允许将具体的操作延迟到运行时,而不必在编译时确定。
-
多态性通常与继承和接口(或抽象类)一起使用,通过继承和接口的实现,不同的子类可以对同一方法进行不同的实现,实现了多态性。
-
Python 由于类型动态推导机制,并没有在编译阶段确定一个对象的类型,所以天生具备多态性,这种多态称为鸭子类型
-
鸭子类型(Duck Typing)是动态类型语言中的一个概念,源自一句格言:“如果它走起路来像鸭子,叫起来像鸭子,那么它就是鸭子。”
-
这个概念在程序设计中指的是关注对象的行为(方法和属性)而不是对象的类型。
-
可以使用
isinstance()
、issubclass()
或类型注解
对对象执行的方法进行判断或约束。
答案
在Python中,多态是面向对象编程的核心概念,允许同一个方法在不同对象上有不同的行为。简单来说,多态使得不同类的对象可以通过相同的接口调用各自不同的实现,从而提高了灵活性和扩展性。
Python的多态主要体现在“鸭子类型”上,即关注对象的行为而不是对象的具体类型。例如,如果一个对象实现了特定的方法,它就可以被视为符合某个接口,不论它的具体类是什么。Python的动态类型特性自然支持这种多态。
多态通常与继承和接口(或抽象类)一起使用,通过这些机制,不同的子类可以对相同的方法进行不同的实现,从而实现多态。通过使用isinstance()、issubclass()等工具,可以检查对象的类型或约束对象的方法。
用过哪些装饰器
面试题目
- 级别: L2
- 知识模块: Python 编程语言
用过那些装饰器
公司
- 小米外包
招聘类型
社招
题目解析
分享曾用过的装饰器。
答案
@classmethod
定义类方法装饰器@staticmethod
定义静态方法装饰器@property
计算属性装饰器,用来将一个方法转变为属性,可以在调用时像访问属性一样调用,而不需要加括号。常用于访问和设置对象的私有属性。@pytest.mark.skip
Pytest 跳过测试用例装饰器@pytest.mark.parametrize
Pytest 测试框架参数化装饰器@pytest.fixture
Pytest 夹具装饰器app.route
flask 框架定义路由装饰器
什么是MRO
面试题目
- 级别: L2
- 知识模块: Python 编程语言
Python 中的 MRO 是什么?
公司
- 美团
招聘类型
社招
题目解析
此题目是考察对 Python 面象对象的理解深度
答案
-
MRO(Method Resolution Order)即方法解析顺序。
-
它是用于确定在多继承情况下,调用方法时搜索方法的顺序。
-
在 Python 中,类的 MRO 顺序是通过 C3 算法计算出来的。
-
解决了早期多继承时可能出现的“菱形继承”和“时间倾斜”等问题。
-
通过调用类的
mro(类名)
方法或类名.__mro__
魔法属性,可以查看该类的方法解析顺序。 -
C3 算法简单理解就是在搜索父类时,先去查找当前类的父类的所有子类,当所有子类都查找完毕后,再去查找父类,然后再去查找其它继承关系
-
总结一下就是,先广度查找,再深度查找
示例:
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B,C):
pass
class X:
pass
class Y(X):
pass
class Z(X):
pass
class T(D,Z,Y):
pass
if __name__ == '__main__':
print("T1")
print(T1.mro())
print(T1.__mro__)
print("T2")
print(T2.mro())
print(T2.__mro__)
# 输出
# T1
# (<class '__main__.T1'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.Z'>, <class '__main__.Y'>, <class '__main__.X'>, <class 'object'>)
# (<class '__main__.T1'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.Z'>, <class '__main__.Y'>, <class '__main__.X'>, <class 'object'>)
# T2
# (<class '__main__.T2'>, <class '__main__.Z'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.Y'>, <class '__main__.X'>, <class 'object'>)
# (<class '__main__.T2'>, <class '__main__.Z'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.Y'>, <class '__main__.X'>, <class 'object'>)
Python对文件的操作
面试题目
- 级别: L2
- 知识模块: Python 编程语言
Python 对文件的操作?
公司
- 字节外包
招聘类型
- 社招
题目解析
Python 提供了丰富的文件操作功能,包括文件的读、写、追加等操作。掌握文件操作是编写数据处理、日志记录、配置管理等程序的基础技能。
文件操作的基本步骤
- 打开文件: 使用 open() 函数打开文件,返回一个文件对象。可以指定文件模式,如读、写、追加等。
file = open('example.txt', 'r') # 以只读模式打开文件
- 读写文件: 使用文件对象的方法进行读写操作,如 read(), write(), readline(), readlines(), writelines() 等。
- 读取文件内容:
content = file.read() # 读取整个文件内容
或
lines = file.readlines() # 按行读取文件内容,返回一个列表
- 写入文件内容:
file = open('example.txt', 'w') # 以写模式打开文件
file.write('Hello, World!') # 写入字符串到文件
或
file.writelines(['Line 1\n', 'Line 2\n']) # 写入多行字符串到文件
- 关闭文件: 完成读写操作后,应关闭文件以释放资源。
file.close() # 关闭文件
文件操作的上下文管理 使用 with 语句可以更简洁、安全地进行文件操作,自动管理文件的打开和关闭。 在 with 语句块中,文件会在块结束后自动关闭。
with open('example.txt', 'r') as file:
content = file.read()
print(content)
文件模式
常用的文件模式有:
- 'r': 只读模式(默认)
- 'w': 写模式(会覆盖文件内容)
- 'a': 追加模式(在文件末尾添加内容)
- 'b': 二进制模式(如 'rb' 读二进制文件,'wb' 写二进制文件)
- '+': 读写模式(如 'r+' 读写文件)
示例:
# 以只读模式打开文本文件
with open('example.txt', 'r') as file:
content = file.read()
print(content)
# 以写模式打开文本文件
with open('example.txt', 'w') as file:
file.write('Hello, World!')
# 以追加模式打开文本文件
with open('example.txt', 'a') as file:
file.write('\nAppend this line.')
# 以二进制读模式打开文件
with open('example.bin', 'rb') as file:
binary_content = file.read()
print(binary_content)
文件指针的操作 文件对象提供了 seek() 和 tell() 方法来操作文件指针。 - seek(offset, whence): 移动文件指针到指定位置。 - tell(): 返回当前文件指针位置。
示例:
with open('example.txt', 'r') as file:
file.seek(5) # 移动文件指针到位置 5
print(file.read()) # 从位置 5 开始读取文件
position = file.tell() # 获取当前文件指针位置
print(f'Current position: {position}')
答案
Python 提供了简洁且功能强大的文件操作接口,能够满足日常开发中的各种文件读写需求。使用 open() 函数打开文件,结合上下文管理 (with 语句) 进行文件操作,是编写安全可靠文件处理代码的最佳实践。掌握文件模式和文件指针操作,能够更灵活地处理文件内容。
python中类方法,类实例方法,静态方法的区别
面试题目
- 级别: L2
- 知识模块: Python 编程语言
python 中类方法,类实例方法,静态方法的区别?
公司
- 字节外包
招聘类型
- 社招
题目解析
在 Python 中,类方法、实例方法和静态方法是定义在类中的不同类型的方法,它们的使用场景和行为有所不同。理解它们的区别对于设计和使用类时非常重要。以下是这三种方法的详细说明:
-
实例方法(Instance Method)
- 定义:实例方法是最常见的方法类型。它需要一个实例对象作为第一个参数(通常是
self
),并且可以访问类的实例属性和调用其他实例方法。 - 特点:
- 通过类的实例调用。
- 可以访问和修改实例的状态。
- 不能在没有创建类的实例的情况下调用。
- 示例:
- 定义:实例方法是最常见的方法类型。它需要一个实例对象作为第一个参数(通常是
class MyClass:
def __init__(self, value):
self.value = value
def instance_method(self):
return self.value
obj = MyClass(10)
print(obj.instance_method()) # 输出: 10
-
类方法(Class Method)
- 定义:类方法是绑定到类而不是实例的方法。它需要一个类对象作为第一个参数(通常是
cls
),可以通过 @classmethod 装饰器定义。 - 特点:
- 通过类名或实例调用。
- 只能访问类属性,不能访问实例属性。
- 适用于需要操作类级别的数据或方法的场景。
- 示例:
- 定义:类方法是绑定到类而不是实例的方法。它需要一个类对象作为第一个参数(通常是
class MyClass:
class_variable = 'class value'
@classmethod
def class_method(cls):
return cls.class_variable
print(MyClass.class_method()) # 输出: class value
obj = MyClass()
print(obj.class_method()) # 输出: class value
-
静态方法(Static Method)
- 定义:静态方法既不依赖于类实例,也不依赖于类本身。它不需要接受 self 或 cls 参数,可以通过 @staticmethod 装饰器定义。
- 特点:
- 通过类名或实例调用。
- 无法访问实例属性或类属性。
- 适用于与类的内部状态无关的功能。
- 示例:
class MyClass:
@staticmethod
def static_method(x, y):
return x + y
print(MyClass.static_method(5, 10)) # 输出: 15
obj = MyClass()
print(obj.static_method(5, 10)) # 输出: 15
答案
-
实例方法是最常见的一种方法,它需要一个实例对象作为第一个参数(通常是
self
),并且可以访问类的实例属性和调用其他实例方法。实例方法可以访问和修改实例的状态,不能在没有创建类的实例的情况下调用。实例方法通过类的实例调用。 -
类方法是绑定到类而不是实例的方法,它需要一个类对象作为第一个参数(通常是
cls
),可以通过@classmethod
装饰器定义。类方法只能访问类属性,不能访问实例属性。类方法通过类名或实例调用,适用于需要操作类级别的数据或方法的场景。 -
静态方法既不依赖于类实例,也不依赖于类本身。它不需要接受
self
或cls
参数,可以通过@staticmethod
装饰器定义。静态方法通过类名或实例调用,无法访问实例属性或类属性,适用于与类的内部状态无关的功能。
python引用参数和值参数的区别
面试题目
- 级别: L2
- 知识模块: Python 编程语言
python 引用参数和值参数的区别
公司
- 字节外包
招聘类型
- 社招
题目解析
引用参数(Reference Parameter)
定义:引用参数传递方式是指函数接收到的是实际参数的引用(即内存地址)。函数内部对参数的修改会直接影响原始数据,因为函数操作的是原始数据的引用。
特点:
- 传递的是数据的地址或引用。
- 修改引用会直接影响原始数据。
值参数(Value Parameter)
定义:值参数传递方式是指函数接收到的是实际参数的副本。函数内部对参数的修改不会影响原始数据,因为函数操作的是副本而不是原始数据。
特点:
- 只传递数据的副本。
- 修改副本不会影响原始数据。
在 Python 中,参数的传递机制可以分为“值传递”和“引用传递”。尽管 Python 的参数传递被称为“对象引用传递”,它与传统的“值传递”或“引用传递”有些不同。以下是这两种传递机制的核心概念及其区别:
-
值传递:
- 在值传递中,函数接收的是参数值的副本。对副本的任何修改不会影响原始数据。这意味着函数内部的改变不会影响外部变量。
-
引用传递:
- 在引用传递中,函数接收的是对象的引用(即地址),而不是对象本身。函数内部的修改将直接影响原始对象。
-
Python 的传递机制:
-
Python 实际上采用的是“对象引用传递”的机制。传递给函数的是对象的引用(即对象的地址),但不是对象的引用本身。函数可以修改传递的对象(如果对象是可变的),但不能改变对象的引用(即不能使其指向一个新的对象)。
-
如果传递的是可变对象(如列表、字典),函数内部的修改会影响原始对象。
-
如果传递的是不可变对象(如整数、字符串、元组),函数内部的修改不会改变原始对象。
-
示例:
def modify_list(lst):
lst.append(4)
def modify_integer(n):
n += 1
my_list = [1, 2, 3]
my_integer = 10
modify_list(my_list)
modify_integer(my_integer)
print(my_list) # 输出: [1, 2, 3, 4]
print(my_integer) # 输出: 10
在这个例子中,modify_list
函数修改了传入的列表(可变对象),因此 my_list
被改变了。而 modify_integer
函数试图修改整数(不可变对象),但 my_integer
没有被改变。
答案
值参数:
- 传递的是数据的副本。
- 修改副本不会影响原始数据。
引用参数:
- 传递的是数据的地址或引用。
- 修改引用会直接影响原始数据。
Python 中,参数的传递机制可以分为“值传递”和“引用传递”。
-
值传递:在值传递中,函数接收的是参数值的副本。对副本的任何修改不会影响原始数据。这意味着函数内部的改变不会影响外部变量。
-
引用传递:在引用传递中,函数接收的是对象的引用(即地址),而不是对象本身。函数内部的修改将直接影响原始对象。
python中序列化有几种实现方式
面试题目
- 级别: L2
- 知识模块: Python 编程语言
python 中序列化有几种实现方式
公司
- 传音控股
招聘类型
社招
题目解析
序列化是将 Python 对象转换为可存储或传输格式的过程,以便在不同的环境或会话中恢复原始对象。在 Python 中,序列化通常指将对象转换为字节流或字符串,以便进行存储或传输。反序列化则是将这些数据重新转换为对象。Python 提供了多种实现序列化的方式,主要包括以下几种:
-
json
模块: -
功能:
json
模块用于将 Python 对象转换为JSON
格式(序列化)以及从JSON
格式恢复对象(反序列化)。JSON
是一种轻量级的数据交换格式,广泛用于 Web 开发和数据交换。 - 优点:格式简单,广泛支持,易于与其他编程语言进行交互。
- 缺点:只支持部分 Python 数据类型,如字典、列表、字符串、数字等;不支持自定义对象。
- 示例:
import json
# 序列化
data = {'key': 'value', 'number': 42}
with open('data.json', 'w') as f:
json.dump(data, f)
# 反序列化
with open('data.json', 'r') as f:
loaded_data = json.load(f)
print(loaded_data) # 输出: {'key': 'value', 'number': 42}
-
yaml 模块:
-
功能:
yaml
模块用于将 Python 对象转换为YAML
格式(序列化)以及从YAML
格式恢复对象(反序列化)。YAML
是一种可读性强的数据序列化格式,常用于配置文件。 - 优点:格式可读性强,支持复杂数据结构。
- 缺点:需要额外的库(如
pyyaml
),可能存在安全性问题(避免加载不信任的YAML
数据)。 - 示例:
import yaml
# 序列化
data = {'key': 'value', 'number': 42}
with open('data.yaml', 'w') as f:
yaml.dump(data, f)
# 反序列化
with open('data.yaml', 'r') as f:
loaded_data = yaml.safe_load(f)
print(loaded_data) # 输出: {'key': 'value', 'number': 42}
答案
-
json
模块:将 Python 对象序列化为JSON
格式,广泛用于数据交换和配置文件。只支持基本数据类型,不支持自定义对象。 -
yaml
模块:将 Python 对象序列化为YAML
格式,适用于配置文件和数据交换。需要安装额外的库(如pyyaml
),格式可读性强。