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

Python入门者求助:现有脚本是否需用类与函数优化?求问题排查

回答你的问题:优化脚本&问题排查

Hey there! Great job getting a working script up as a total beginner— that's already a huge achievement. Let's tackle your questions one by one, fix the existing issues, and make this script cleaner, more efficient, and easier to maintain.


1. 要不要用类和函数?绝对要!

你的脚本里大量重复的SSH操作逻辑(COR/AP/EFW/LUS的代码几乎一模一样,只是命令和等待时间不同)是最适合用函数来封装的。函数能帮你:

  • 减少重复代码,改一处就能同步所有同类操作
  • 让逻辑更清晰,每个函数只做一件事(比如ping_device负责ping检测,fetch_device_config负责SSH获取配置)
  • 方便后续扩展(比如要加新设备类型,只需要传不同参数给函数,不用复制粘贴一堆代码)

类的话,如果你后续要给每个设备加更多属性/方法(比如记录设备历史状态、批量操作)可以考虑,但现阶段用函数已经足够让脚本专业高效了。


2. 现有脚本的问题排查

这里列出几个关键问题,有些会导致潜在报错,有些是代码风格/可维护性问题:

变量相关问题

  • 多余的字典初始化:开头定义的IP = {}、Type = {}等完全没用,后面直接把这些变量赋值成了字符串(比如IP = item['IP']),既浪费内存又容易混淆,直接删掉这些字典定义,用局部变量即可。
  • 日期变量覆盖:你导入了from datetime import date,然后又写了date = date.today(),这会把date模块对象覆盖成一个具体的日期实例,后续如果想调用date模块的其他方法(比如date.fromisoformat())就会报错,改成today_date = date.today()更安全。
  • 变量命名混乱:比如Host和hostname都是设备名称相关变量,容易搞混,建议用更明确的名字,比如device_hostname、ping_target。

逻辑与异常处理问题

  • Ping部分的socket逻辑漏洞:try: ip = socket.gethostbyname(IP)的except块直接pass,如果解析失败,ip变量会未定义,后续执行f.writerow([hostname] + [data] + [status] + [ip])会报错。应该先给ip设默认值,再尝试更新。
  • SSH操作无异常捕获:如果SSH连接失败(比如IP不可达、密码错误),脚本会直接崩溃,没有任何提示,应该添加try-except捕获paramiko.SSHException、socket.error等异常。
  • 硬编码的Windows ping命令:['ping.exe',hostname]只能在Windows上运行,如果要跨平台,需要调整ping命令参数(比如Linux/macOS用ping -c 4)。

文件操作问题

  • 未使用with语句管理文件:COR/AP等部分的output1 = open(...)没有用with,如果脚本意外崩溃,文件可能无法正确关闭。with语句会自动处理文件的打开和关闭,更安全。
  • Ping输出文件格式混淆:你注释掉了csv输出,却用csv.writer写txt文件,建议统一用csv格式(文件名改成Ping-{today_date}.csv),方便后续用表格工具查看。

3. 重构后的示例脚本

下面是优化后的版本,用函数封装重复逻辑,修复了上述问题:

import csv
import subprocess
import paramiko
import time
import socket
from datetime import date

def ping_device(ip_address, today_date):
    """处理设备ping检测并写入结果"""
    ping_target = ip_address
    status = "Down"
    ip_resolved = "Not Found"
    hostname_resolved = ""

    # 尝试解析IP对应的主机名
    try:
        ip_resolved = socket.gethostbyname(ip_address)
        hostname_resolved = socket.getfqdn(ip_address)
    except socket.error:
        pass

    # 执行ping命令(Windows专用,跨平台需调整)
    response = subprocess.Popen(
        ['ping.exe', ping_target],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    ).communicate()[0].decode()

    # 判断ping状态
    if 'bytes=32' in response:
        status = 'Up'
    elif 'destination host unreachable' in response:
        status = 'Unreachable'

    # 写入结果到CSV
    with open(f'Ping-{today_date}.csv', 'a', newline='', encoding='utf-8') as csv_file:
        writer = csv.writer(csv_file)
        writer.writerow([ping_target, hostname_resolved, status, ip_resolved])
    
    print(f"Ping result for {ping_target}: {status}")

def fetch_device_config(ip_address, username, password, device_hostname, today_date, command, wait_time):
    """通过SSH获取设备配置并保存到文件"""
    try:
        # 初始化SSH客户端
        ssh_client = paramiko.SSHClient()
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh_client.connect(
            ip_address,
            username=username,
            password=password,
            port=22,
            timeout=10
        )
        print(f"SSH connection established to {ip_address}")

        # 开启交互式会话
        shell = ssh_client.invoke_shell()
        print("Interactive SSH session established")

        # 发送命令序列
        shell.send("\n")
        if command != "show full-configuration":  # 部分设备需要设置terminal长度
            shell.send("terminal length 0\n")
            time.sleep(1)
        shell.send(f"{command}\n")
        time.sleep(wait_time)

        # 获取输出并保存
        output = shell.recv(9999999).decode()
        with open(f'{device_hostname}-{today_date}.txt', 'w', encoding='utf-8') as config_file:
            config_file.write(output)
        
        print(f"Configuration saved for {device_hostname}")
        ssh_client.close()

    except Exception as e:
        print(f"Failed to connect to {ip_address}: {str(e)}")

def main():
    today_date = date.today()
    # 读取输入CSV
    with open('Output.csv', 'r', newline='', encoding='utf-8') as csvinput:
        reader = csv.DictReader(csvinput)
        # 初始化Ping CSV文件(写入表头)
        with open(f'Ping-{today_date}.csv', 'w', newline='', encoding='utf-8') as ping_csv:
            writer = csv.writer(ping_csv)
            writer.writerow(["Target IP", "Resolved Hostname", "Status", "Resolved IP"])

        for item in reader:
            ip = item['IP']
            device_hostname = item['Hostname']
            username = item['Username']
            password = item['Password']
            device_type = item['Type']

            print(f"\nProcessing {device_hostname} ({ip})...")

            if 'Ping' in device_type:
                ping_device(ip, today_date)
            elif device_type == 'COR':
                fetch_device_config(ip, username, password, device_hostname, today_date, "sh run", 5)
            elif device_type == 'AP':
                fetch_device_config(ip, username, password, device_hostname, today_date, "sh run", 10)
            elif device_type == 'EFW':
                fetch_device_config(ip, username, password, device_hostname, today_date, "show full-configuration", 10)
            elif device_type == 'LUS':
                fetch_device_config(ip, username, password, device_hostname, today_date, "sh run", 10)

if __name__ == "__main__":
    main()

重构后的优势

  • 代码复用:所有SSH操作都集中在fetch_device_config函数里,修改逻辑只需要改这一个函数
  • 异常安全:SSH部分添加了异常捕获,不会因为单个设备失败导致整个脚本崩溃
  • 可读性强:每个函数职责明确,主逻辑清晰,后续维护/扩展更简单
  • 格式规范:用with管理文件,变量命名更清晰,避免了变量覆盖问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:49:12