Python面试题
Python面试题回答
1. Python面向对象的三个特征?
Python面向对象编程的三个主要特征是:
- 封装(Encapsulation):将数据和操作数据的方法绑定在一起,隐藏内部实现细节,只暴露必要的接口。在Python中通过类(class)实现。
- 继承(Inheritance):子类可以继承父类的属性和方法,实现代码重用。Python支持多重继承。
- 多态(Polymorphism):同一操作作用于不同对象可以有不同的解释和执行结果。Python通过鸭子类型(duck typing)实现多态。
多态如何实现和使用
在Python中,多态的实现和使用可以通过以下几种方式:
1. 通过鸭子类型实现多态
Python不检查对象类型,而是关注对象是否具有所需方法或属性。只要对象实现了对应方法,就可以被调用。
class Cat:
def speak(self):
return "喵喵~"
class Dog:
def speak(self):
return "汪汪!"
def animal_speak(animal):
print(animal.speak())
# 不同对象都可以调用
animal_speak(Cat()) # 输出:喵喵~
animal_speak(Dog()) # 输出:汪汪!
2. 通过继承和方法重写实现多态
子类可以重写父类的方法,实现不同的行为表现。
class Animal:
def speak(self):
raise NotImplementedError("子类必须实现此方法")
class Cat(Animal):
def speak(self):
return "喵喵~"
class Dog(Animal):
def speak(self):
return "汪汪!"
# 统一调用接口
animals = [Cat(), Dog()]
for animal in animals:
print(animal.speak())
3. 使用抽象基类规范多态
通过abc
模块强制要求子类实现特定方法。
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
# 统一调用方式
shapes = [Circle(5), Square(4)]
for shape in shapes:
print(f"面积: {shape.area()}")
4. 运算符重载实现多态
通过特殊方法实现运算符的多态行为。
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # 输出:(4, 6)
实际应用场景:
- 支付系统:统一处理支付宝、微信等不同支付方式
- 数据库操作:不同数据库驱动实现相同接口
- 游戏开发:不同角色实现相同的攻击接口
- 插件系统:统一调用插件接口
多态的优势:
- 提高代码扩展性,新增功能只需添加新类
- 增强代码可维护性,统一调用接口
- 降低耦合度,各模块独立发展
- 提高代码复用性,通用逻辑只需编写一次
注意事项:
- Python的多态更加灵活,不强制要求继承关系
- 建议使用抽象基类来规范接口
- 注意文档说明,明确各子类需要实现的方法
- 适当使用类型提示提高代码可读性
2. is 和 == 的区别?
==
是值比较,检查两个对象的值是否相等is
是身份比较,检查两个对象是否是内存中的同一个对象(即id是否相同)
示例:
a = [1, 2, 3]
b = a
c = [1, 2, 3]
a == b # True
a is b # True
a == c # True
a is c # False (不同对象)
3. GIL了解吗?说说
GIL(Global Interpreter Lock)是全局解释器锁,是CPython解释器中的一个机制:
- 同一时刻只允许一个线程执行Python字节码
- 目的是简化CPython实现和内存管理
- 主要影响CPU密集型多线程程序(会使多线程无法真正并行)
- 对I/O密集型任务影响不大(因为I/O操作会释放GIL)
- 可以通过多进程(而非多线程)来绕过GIL限制
4. 可变类型和不可变类型?
-
不可变类型(immutable):创建后不能修改内容
- 数字(int, float, complex)
- 字符串(str)
- 元组(tuple)
- 布尔(bool)
- frozenset
-
可变类型(mutable):创建后可以修改内容
- 列表(list)
- 字典(dict)
- 集合(set)
- 字节数组(bytearray)
5. yield用法?
yield
用于定义生成器函数:
- 函数执行到yield时会暂停并返回yield后的值
- 下次调用时会从暂停处继续执行
- 相比return,yield可以多次返回值
- 节省内存,适合处理大数据集(惰性求值)
示例:
def count_up_to(max):
count = 1
while count <= max:
yield count
count += 1
# 使用
for num in count_up_to(5):
print(num)
6. 深拷贝和浅拷贝区别?
-
浅拷贝(copy.copy):
- 只复制对象本身,不复制内部的子对象
- 新对象和原对象共享内部子对象
- 适用于不可变对象或简单结构
-
深拷贝(copy.deepcopy):
- 递归复制对象及其所有子对象
- 新对象和原对象完全独立
- 适用于嵌套结构或需要完全独立的场景
示例:
import copy
lst1 = [1, [2, 3], 4]
lst2 = copy.copy(lst1) # 浅拷贝
lst3 = copy.deepcopy(lst1) # 深拷贝
lst1[1][0] = 'changed'
# lst2会看到变化,lst3不会
7. Python中的线程
Python中的线程:
- 通过
threading
模块实现 - 由于GIL限制,多线程不适合CPU密集型任务
- 适合I/O密集型任务(如网络请求、文件操作)
- 线程共享同一进程的内存空间
- 需要注意线程安全问题(使用Lock、RLock等同步机制)
基本使用:
import threading
def worker():
print("Worker thread")
t = threading.Thread(target=worker)
t.start()
8. 生成器和迭代器区别
-
迭代器(Iterator):
- 实现了
__iter__
和__next__
方法的对象 - 一次性,遍历后无法重新开始
- 所有生成器都是迭代器
- 实现了
-
生成器(Generator):
- 使用
yield
关键字定义的函数返回的对象 - 一种特殊的迭代器,更简洁的实现方式
- 惰性求值,节省内存
- 可以通过生成器表达式创建(类似列表推导式,但用圆括号)
- 使用
示例:
# 迭代器
class Count:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self):
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
# 生成器
def count_gen(low, high):
current = low
while current <= high:
yield current
current += 1
9. *args和**kwargs是什么?区别,用法
-
*args
:- 用于接收任意数量的位置参数
- 参数被打包为元组(tuple)
- 函数定义时使用
-
**kwargs
:- 用于接收任意数量的关键字参数
- 参数被打包为字典(dict)
- 函数定义时使用
区别:
*args
处理无名参数,**kwargs
处理有名参数- 可以同时使用,但
*args
必须在**kwargs
之前
用法示例:
def example_func(arg1, *args, **kwargs):
print(f"固定参数: {arg1}")
print(f"额外位置参数: {args}")
print(f"额外关键字参数: {kwargs}")
example_func(1, 2, 3, 4, name="Alice", age=25)
输出:
固定参数: 1
额外位置参数: (2, 3, 4)
额外关键字参数: {'name': 'Alice', 'age': 25}