运行PySpark PageRank报错TypeError: ABCMeta不可下标
问题现象
运行Apache Spark官方仓库提供的Python版PageRank示例代码时,抛出TypeError: 'ABCMeta' object is not subscriptable异常。
涉及的核心代码片段:
import re import sys from operator import add from typing import Iterable, Tuple from pyspark.resultiterable import ResultIterable from pyspark.sql import SparkSession def computeContribs(urls: ResultIterable[str], rank: float) -> Iterable[Tuple[str, float]]: """Calculates URL contributions to the rank of other URLs.""" num_urls = len(urls) for url in urls: yield (url, rank / num_urls) def parseNeighbors(urls: str) -> Tuple[str, str]: """Parses a urls pair string into urls pair.""" parts = re.split(r'\s+', urls) return parts[0], parts[1]
完整报错信息:
10 from collections.abc import Mapping 11 ---> 12 def computeContribs(urls: ResultIterable[str], rank: float) -> Iterable[Tuple[str, float]]: 13 """Calculates URL contributions to the rank of other URLs.""" 14 num_urls = len(urls) TypeError: 'ABCMeta' object is not subscriptable
原因说明
关于函数类型声明
Python是动态类型语言,函数定义时的类型声明是3.5版本后新增的*类型注解(Type Hints)*特性,属于可选语法:
- 作用是给IDE、静态代码检查工具提供类型参考,提升代码可读性,提前发现类型不匹配的潜在问题
- 类型注解不会在运行时做强制校验,不会影响代码的实际执行逻辑,不写也可以正常运行
报错触发原因
报错本质是泛型下标语法的版本兼容问题:
- 代码里写的
ResultIterable[str]是泛型类型标注写法,意思是参数urls是元素为字符串类型的ResultIterable对象 - 在Python 3.9版本之前,只有
typing模块下的类型对象支持[类型参数]的下标写法,普通类、抽象基类直接使用这种下标语法会被当成普通的对象下标取值操作 - 当前使用的PySpark版本中,
ResultIterable是继承自ABCMeta的抽象类,没有实现对应的下标处理逻辑,因此执行到ResultIterable[str]这一步时就抛出了类型错误。
修复方案
根据实际环境选择对应方案即可:
- 方案1:升级环境适配官方代码
将Python升级到3.9及以上版本,同时将PySpark升级到3.2.0及以上版本,新版本已经原生支持泛型下标写法,官方示例代码不需要做任何修改即可正常运行。 - 方案2:修改类型注解兼容旧环境(最简便)
去掉ResultIterable后的泛型下标参数,因为类型注解不影响运行逻辑,修改后代码可以直接在旧版本环境运行,修改后的对应代码如下:
def computeContribs(urls: ResultIterable, rank: float) -> Iterable[Tuple[str, float]]: """Calculates URL contributions to the rank of other URLs.""" num_urls = len(urls) for url in urls: yield (url, rank / num_urls)
如果不需要类型注解,甚至可以直接去掉所有类型标注,代码也能正常运行。
- 方案3:保留完整泛型提示(兼顾静态检查和旧环境兼容)
借助TYPE_CHECKING常量实现静态检查和运行时逻辑分离,既可以在IDE里保留完整的类型提示,也不会在运行时触发错误:
from typing import Iterable, Tuple, TYPE_CHECKING # TYPE_CHECKING常量仅在做静态类型检查时为True,运行时为False if TYPE_CHECKING: from pyspark.resultiterable import ResultIterable else: # 运行时将ResultIterable替换为typing模块的Iterable,避免下标报错 ResultIterable = Iterable # 后续函数定义不需要修改 def computeContribs(urls: ResultIterable[str], rank: float) -> Iterable[Tuple[str, float]]: num_urls = len(urls) for url in urls: yield (url, rank / num_urls)
内容的提问来源于stack exchange,提问作者abbassix
相关产品推荐
相关产品推荐

