阿里国际 AI应用开发 一面

1. 自我介绍

2. Python 里的 __new____init__ 的本质区别是什么,哪些场景必须重写 __new__

__new__ 负责创建对象,返回实例本身;__init__ 负责在实例创建完成后做初始化。真正必须重写 __new__ 的场景并不多,常见的是不可变类型子类化、单例控制、缓存复用、元编程对象创建路径劫持。因为像 strtupleint 这类不可变对象,很多状态在对象创建时就固定了,等到 __init__ 再改已经来不及了。

class MyInt(int):
    def __new__(cls, value):
        return super().__new__(cls, value * 10)

    def __init__(self, value):
        self.raw = value

x = MyInt(3)
print(x)       # 30
print(x.raw)   # 3

3. Python 里参数传递到底是传值、传引用,还是别的东西

更准确的说法是“对象引用的传递”。函数拿到的是对象引用的副本,而不是对象本身的复制品。于是可变对象在函数内部原地修改,外部可见;不可变对象如果在内部重新绑定,只是让局部变量指向了新对象,不会影响外部名字绑定。面试里如果只说“Python 全是传引用”或者“全是传值”,都不够严谨。

def f1(x):
    x.append(4)

def f2(x):
    x += [4]

def f3(x):
    x = x + [4]

a = [1, 2, 3]
f1(a)
print(a)  # [1, 2, 3, 4]

b = [1, 2, 3]
f2(b)
print(b)  # [1, 2, 3, 4]

c = [1, 2, 3]
f3(c)
print(c)  # [1, 2, 3]

4. 如果函数参数传入的是字符串,怎样在函数内部“修改它”并让外部拿到变化

字符串是不可变对象,所谓“修改”本质上只能是创建新字符串并返回。真正面试想考的是你是否理解不可变对象的语义,以及为什么不能指望在函数内部原地改掉调用方持有的字符串对象。能改的是变量绑定,不是字符串对象内部内容。

def change_name(s: str) -> str:
    return s.replace("old", "new")

name = "old_model"
name = change_name(name)
print(name)  # new_model

5. any() 和逻辑运算符 or 的区别到底在哪,为什么很多人会答浅

any() 接收一个可迭代对象,逐个取元素做真值判断,只返回布尔值;or 是短路表达式,返回的是第一个真值对象,若都为假则返回最后一个对象本身。也就是说,any() 关注的是“集合里有没有真值”,而 or 既参与逻辑控制,又保留原对象语义。面试里如果只说“都表示有一个为真就行”,其实没答到点上。

print(any([0, "", None, 3]))   # True
print(0 or "" or None or 3)    # 3
print(any([]))                 # False
print([] or {})                # {}

6. Python 中哪些对象会被判定为 False,自定义对象怎么控制真值行为

常见假值包括 NoneFalse、数值零、空字符串、空列表、空字典、空集合。自定义对象默认都是真值,除非实现了 __bool____len__。如果一个对象语义上代表“结果为空”“资源无效”“缓存未命中”,就可以通过这两个魔术方法控制其真值,这在框架设计里很常见。

class Result:
    def __init__(self, data):
        self.data = data

    def __bool__(self):
        return bool(self.data)

print(bool(Result([1, 2])))  # True
print(bool(Result([])))      # False

7. Python 的垃圾回收机制到底是怎样协同工作的,为什么只有“引用计数”这个回答不够

CPython 主要依赖引用计数做即时回收,配合分代垃圾回收处理循环引用。引用计数让大部分对象在引用归零时马上释放,但碰到互相引用的对象,即使外部已经不可达,引用计数也不会归零,这时就需要分代 GC 介入扫描。再往深一点,还要知道对象内存不一定立刻还给操作系统,而可能回收到 Python 自己的分配器池中。

import gc

class A:
    pass

a = A()
b = A()
a.other = b
b.other = a

del a
del b
print(gc.collect())  # 回收循环引用对象

8. Python 会发生内存泄漏吗,既然有 GC 为什么还会泄漏

会,所谓内存泄漏不一定是“永远不可达却没被回收”,更常见的是“逻辑上不再需要,但仍然有引用链活着”。例如全局缓存无限增长、闭包捕获大对象、长生命周期容器保存历史请求、日志回调挂住对象、C 扩展没正确释放资源。也就是说,GC 只能解决“可回收对象的回收”,无法替你定义“业务上已经没用了”。

9. 全局变量算不算内存泄漏,把列表当一级、二级缓存来用会不会泄漏

全局变量本身不等于泄漏,但如果它长期持有不断增长的对象,且没有明确淘汰策略,就会形成事实上的泄漏。列表拿来做缓存最大的问题不是“语法不对”,而是它没有 TTL、容量上限、淘汰策略和命中统计,最终容易积压大量对象。缓存如果没有边界控制,本质上就是可控性极差的常驻引用集合。

10. Python 文件运行效率怎么提升,除了代码层面,编译层面还能做什么

代码层面主要看算法复杂度、数据结构选择、避免多余对象创建、减少 Python 层循环热点。编译或解释器层面则可以考虑字节码缓存、PyPy、Cython、Numba、C 扩展、mypy/cpython 优化路径、向量化库替代纯 Python 热点。真正高收益往往不是“把一行写得更花”,而是把热点计算从解释器层搬走。

# Cython 示例语法风格(示意)
# cpdef long add_loop(int n):
#     cdef int i
#     cdef long s = 0
#     for i in range(n):
#         s += i
#     return s

11. Python 的对象分配和 pymalloc 机制了解多少,为什么这题能拉开差距

CPython 对小对象有专门的内存分配器 pymalloc,会把内存按 arena、pool、block 分层管理,小对象优先从这些池子里分配,减少频繁向系统申请内存的开销。这样做带来更高性能,但也意味着你看到进程 RSS 不下降,不一定说明泄漏,有可能只是内存被 Python 运行时保留用于后续复用。这个问题的关键在于区分“逻辑对象已释放”和“进程物理内存是否立刻归还系统”是两回事。

12. 进程和线程的核心区别是什么,为什么 Python 里这个问题一定会被继续追问到 GIL

进程是资源分配基本单位,地址空间独立,隔离性好,但创建和通信成本高;线程是调度基本单位,共享进程地址空间,切换轻,但同步复杂。Python 里这个问题不会停在操作系统定义,因为 CPython 有 GIL,导致同一进程内多个 Python 线程无法并行执行字节码密集计算,所以 CPU 密集任务通常要靠多进程或把核心逻辑下沉到释放 GIL 的扩展。

13. 引入线程主要解决什么问题,为什么很多 Python 服务线程开多了反而更慢

线程主要适合 I/O 等待型任务,比如网络请求、磁盘等待、RPC、数据库访问,因为线程在等待时可以切出去让别的任务运行。但如果任务主要是 Python 层 CP

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

AI-Agent面试实战专栏 文章被收录于专栏

本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.

全部评论

相关推荐

评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务