Python3.8 TypeError: 'type'对象不可订阅错误排查求助
问题修复:Python 3.8类型提示导致的TypeError
错误根源
报错来自三个Python 3.8类型提示的不兼容问题:
- 泛型类型下标不支持:Python 3.8中,
tuple等内置泛型不能直接用[...]下标,需要从typing模块导入对应泛型类,或启用延后类型解析。 - 模块作为类型注解错误:
log: logging里的logging是模块而非类型,正确类型应为logging.Logger。 - 可选返回值写法不规范:
-> tuple[...] or None不符合PEP类型提示规范,需用Optional[...]表示可选类型。
具体修复步骤
1. 修改module.py的类型提示
在文件顶部导入typing模块的Optional,并调整函数注解:
# coding=utf-8 import logging import re import unicodedata from typing import Optional # 新增导入 from mailbox import MaildirMessage from bs4 import BeautifulSoup, ResultSet, Tag def filter_mail(mail: MaildirMessage, log: logging.Logger) -> Optional[tuple[object, MaildirMessage]]: """ Filter the mail to only include messages that have need to be sent :rtype: Optional[tuple[object, MaildirMessage]] :param mail: MaildirMessage :param log: logging.Logger """ try: parsed_email: BeautifulSoup = BeautifulSoup(mail._payload, 'html.parser') original_from_email_id: object = \ re.findall(r"([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)", mail['From'])[0] # 提取原始邮件发送方 from_element: str = parsed_email.find_all("br")[0].parent.text from_element = unicodedata.normalize("NFKD", from_element) from_element = from_element.replace("\n", " ") from_element = from_element.replace("=2E", ".") emails_from: list = re.findall(r"([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)", from_element) # 修改邮件头 email_subject: str = mail['Subject'].replace("Fw: ", "") mail.replace_header("From", emails_from[0]) mail.replace_header("Subject", email_subject) # 移除邮件内容中的HTML块 html_block_to_be_removed_element: ResultSet[Tag] = parsed_email.select('div[style=\'3D"margin:0\']') html_block_to_be_removed: str = str(html_block_to_be_removed_element).removeprefix('[').removesuffix(']') mail._payload = str(parsed_email).replace(html_block_to_be_removed, '') return original_from_email_id, mail except Exception as e: log.critical(f"Failed to Filter email, Exception Message - " + str(e)) return None
2. 修复main.py中的类型提示
同样导入Optional,并调整filtered_mail的类型注解,同时修正filter_mail的调用参数:
#!/usr/bin/env python3 # coding=utf-8 import logging import mailbox import module import os import sys from typing import Optional # 新增导入 from mailbox import Maildir, MaildirMessage from kafka import KafkaProducer from kafka.errors import KafkaError from kafka.producer.future import FutureRecordMetadata # 原有环境变量配置代码不变... def read_mails() -> None: """ :rtype: None """ mbox: Maildir = mailbox.Maildir(MAILBOX_DIRECTORY) if not len(mbox.items()): logging.info('no new emails found in mbox') return processed_email_count: int = 0 kafka_producer: KafkaProducer = KafkaProducer( bootstrap_servers=KAFKA_SERVERS_LIST, compression_type='gzip' ) mbox.lock() try: item_keys_to_remove: list = [] for key, mail in mbox.iteritems(): logging.info(f"new mail from {mail['From']} with subject: {mail['Subject']}") # 修改类型注解,并传入正确的Logger实例 filtered_mail: Optional[tuple[object, MaildirMessage]] = module.filter_mail(mail, logging.getLogger(__name__)) if filtered_mail is not None: logging.info(f"Filtered mail from {mail['From']} with subject: {mail['Subject']}") if put_mail_in_kafka_topic(filtered_mail[0], filtered_mail[1].as_string(), kafka_producer): item_keys_to_remove.append(key) backup_mail(mail) processed_email_count += 1 else: logging.critical(f"Failed to filter mail or publish mail on kafka topic") # 原有邮件处理代码不变...
3. 可选方案:启用延后类型解析
如果不想导入typing模块,可在两个文件的最顶部添加:
from __future__ import annotations
这会让Python延后解析类型提示,允许直接使用tuple[...]写法,但仍需修正log的类型和返回值的Optional写法。
额外优化建议
original_from_email_id的类型可从object改为str,因为正则匹配返回的是字符串,类型更精确。emails_from: list可改为emails_from: list[str],提升类型标注精度。
内容的提问来源于stack exchange,提问作者Manav Mehta
相关产品推荐
相关产品推荐

