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
相关产品推荐
相关产品推荐

