You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Python exec执行多类脚本时出现NameError问题求助

问题:exec执行多类Python脚本时触发NameError

错误信息

执行exec(script)时出现如下错误:

exec(script)
File "<string>", line 73, in <module>
File "<string>", line 79, in EthernetFrame
NameError: name 'EthernetFrameHeader' is not defined

问题描述

我有一个包含大量类定义的Python脚本,需要通过exec函数执行,但运行时触发上述错误。曾怀疑exec不支持多类代码,尝试多种方法均无效,请求解决。

待执行的脚本代码

import socket
import struct
from enum import Enum
from typing import Callable

class NetworkParseError(Exception):
    pass

class NetworkLevel(Enum):
    DATALINK = 'DataLink layer'
    NETWORK = 'Network layer'
    TRANSPORT = 'Transport layer'
    APPLICATION = 'Application layer'

class NetworkProtocol:
    def __init__(self, raw_data: bytes):
        self.raw_data = raw_data
        self.data_length = len(self.raw_data)

    def get_proto_info(self) -> str:
        raise NetworkParseError('Override function: %s in class: %s' % (self.get_proto_info.__name__,
                                                                           self.__class__.__name__))

    def __str__(self) -> str:
        return self.get_proto_info()

    @staticmethod
    def get_format_address(raw_addr: bytes, *, sep: str = '', function: Callable = str.upper) -> str:
        return f'{sep}'.join(map(function, raw_addr))

    def get_data(self, offset: int, total_length: int = None) -> bytes:
        if self.data_length:
            return self.raw_data[offset:] if total_length is None else self.raw_data[offset:total_length]
        else:
            raise NetworkParseError('No raw data in %s object' % self.__class__.__name__)

    def get_json_proto_header(self):
        raise NotImplementedError('Override function')

class EthernetFrameHeader:
    def __init__(self, src_mac: bytes, dst_mac: bytes, encapsulated_proto: int) -> None:
        self.__src_mac: bytes = src_mac
        self.__dst_mac: bytes = dst_mac
        self.__encapsulated_proto: int = encapsulated_proto

    @property
    def format_src_mac(self) -> str:
        return NetworkProtocol.get_format_address(self.__src_mac, sep=':', function="{:02x}".format).upper()

    @property
    def format_dst_mac(self) -> str:
        return NetworkProtocol.get_format_address(self.__dst_mac, sep=':', function="{:02x}".format).upper()

    @property
    def encapsulated_proto(self) -> int:
        return socket.ntohs(self.__encapsulated_proto)

    @property
    def source_mac(self) -> bytes:
        return self.__src_mac

    @property
    def destination_mac(self) -> bytes:
        return self.__dst_mac

    def __str__(self) -> str:
        result = f'{self.__level.value}\tEthernetProtocol:\n'
        result += f'Src MAC: {self.format_src_mac}\tDst MAC: {self.format_dst_mac}\t' \
                  f'Eth Proto: {self.encapsulated_proto}\n'
        return result


class EthernetFrame(NetworkProtocol):
    def __init__(self, raw_data: bytes):
        NetworkProtocol.__init__(self, raw_data)
        self.__header = self.__parse_data()
        self.__offset: int = 14

    def __parse_data(self) -> EthernetFrameHeader:
        if self.data_length:
            try:
                dst_mac, src_mac, proto = struct.unpack('!6s6sH', self.raw_data[:14])
                return EthernetFrameHeader(src_mac, dst_mac, proto)
            except struct.error:
                raise NetworkParseError("Incorrect Ethernet frame format")
        else:
            raise NetworkParseError(f"No {self.__class__.__name__} header")

    @property
    def header(self) -> EthernetFrameHeader:
        return self.__header

    @property
    def source_mac(self) -> bytes:
        return self.__header.source_mac

    @property
    def destination_mac(self) -> bytes:
        return self.__header.destination_mac

    @property
    def encapsulated_proto(self) -> int:
        return self.__header.encapsulated_proto

    def get_encapsulated_data(self) -> bytes:
        return NetworkProtocol.get_data(self, self.__offset)

    def get_proto_info(self) -> str:
        return str(self.__header)

    def get_json_proto_header(self):
        pass


sniffer = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3))

raw_data = sniffer.recvfrom(65535)[0]

print(EthernetFrame(raw_data))

执行方式

...
exec(script_code)

其中script_code包含上述完整脚本。

解决方法

exec完全支持执行包含多个类的代码,问题根源在脚本本身的bug:

1. 修复EthernetFrameHeader的__str__方法错误

在EthernetFrameHeader的__str__方法中,你使用了未定义的self.__level属性,这会导致实例化该类时抛出AttributeError,异常中断了类的初始化流程,最终表现为EthernetFrameHeader未定义的假象。

修复方案二选一:

  • 方案一:在EthernetFrameHeader的__init__方法中添加__level属性的初始化:
    def __init__(self, src_mac: bytes, dst_mac: bytes, encapsulated_proto: int) -> None:
        self.__src_mac: bytes = src_mac
        self.__dst_mac: bytes = dst_mac
        self.__encapsulated_proto: int = encapsulated_proto
        self.__level = NetworkLevel.DATALINK  # 添加这行
    
  • 方案二:直接修改__str__方法中的对应行,使用枚举常量:
    def __str__(self) -> str:
        result = f'{NetworkLevel.DATALINK.value}\tEthernetProtocol:\n'  # 修改这行
        result += f'Src MAC: {self.format_src_mac}\tDst MAC: {self.format_dst_mac}\t' \
                  f'Eth Proto: {self.encapsulated_proto}\n'
        return result
    

2. 验证脚本正确性

先在普通Python环境中直接运行脚本(不用exec),会发现同样的错误,确认问题与exec无关。修复后再用exec执行即可正常运行。

内容的提问来源于stack exchange,提问作者KeisyKeij

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 15:10:00