'豆包' + 零基础程序员 ,项目明天交付,怎么办

5,880 阅读11分钟

一. 前言

完蛋 ,一觉醒来发现自己穿越到了豆包新世界 ,成为了一个零基础的 Python 程序员

领导让写一个桌面应用 ,用来看 股票 K 线图 ,说对我而言小需求 ,明天就给他, 不然开了我。

可是 ,我不会啊!!!

好在电脑上环境已经配好了 ,还发现一个摸鱼用的 豆包 ,离明天交付还有8小时 ,干了。

二. 儿郎们 ,赶紧操练起来

2.1 先确定项目选型

❓ 问 :Python 有什么好用的桌面端脚手架

image.png

❓ 问 : Git 上面有没有 PyQt5 的 Fluent Design 风格的组件库

image.png

❓ 问 : Python 有什么框架可以查询股票日线行情

image.png

总结 : 选型确定了!!!

很好 ,花了5分钟时间 ,了解了 Python 上有那些组件可以帮助我完成这个需求。

  • 桌面框架 : PyQt-Fluent-Widgets
  • 股票工具 : Tushare
  • 图标工具 : Echats

题外话 : 个人感触

  • 和之前一篇分析来比 ,豆包返回的内容明显更丰富了 。回答的内容也更全面了 。 👍👍👍

2.2 还剩下 7小时55分钟 ,为了避免加班 ,赶紧写代码

❓ 问 :帮我用 PyQt-Fluent-Widgets + Tushare 实现一个查询日线行情的 Demo

image.png

👉 此时心态 : 白花了2分钟 ,回答不是很满意啊

这里虽然我给了明确的提示词 ,但是也由于引用了具体的项目 ,而项目并没有收录到模型中 ,所以回答的内容并不是我期望的内容。

或许 !! 是我用法不对 !!

2.3 摸了 1 小时鱼, 还剩下 6 小时 ,灵感来了

❓ PyQt-Fluent-Widgets 案例有吗 ?

import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
from qfluentwidgets import (
    NavigationInterface,
    NavigationItemPosition,
    NavigationWidget,
    setTheme,
    Theme,
)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt-Fluent-Widgets Example")
        self.resize(800, 600)

        # 创建导航界面
        navigation_interface = NavigationInterface(self)

        # 添加导航项
        home_item = navigation_interface.addItem(
            routeKey="home",
            icon="home",
            text="Home",
            position=NavigationItemPosition.TOP,
        )
        about_item = navigation_interface.addItem(
            routeKey="about",
            icon="info",
            text="About",
            position=NavigationItemPosition.BOTTOM,
        )

        # 设置主题
        setTheme(Theme.LIGHT)

        # 将导航界面添加到主窗口
        self.setCentralWidget(navigation_interface)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
  • PS : 这里还问了要安装什么插件 :
    • pip install PyQt6-Frameless-Window
    • pip install PyQt6-Fluent-Widgets -i pypi.org/simple/
    • pip install PyQt6
    • pip install PyQt6-WebEngine
    • pip install tushare

image.png

  • 结论
    • 当前进度 : 摸鱼一小时 ,工作10分钟 ,界面基本出来了
    • 使用技巧 : 有的时候询问的题目很重要 ,如果加入太多了元素 ,答案反而给的不明朗

2.5 躲在会议室开了10把游戏 ,午休结束 ,还剩4小时,要好好工作啦

❓ 用 Tushare 帮我查询当天的日行情 ,单独放在一个类里面

image.png

  • 结论
    • 不容易啊 ,又工作了两分钟 , 开一把游戏奖励一下自己
    • 可以看到 ,MarsCode 提供的类很好 ,还提供了调用案例

❓Python PyQT 里面如何使用 Echats 框架

image.png

  • 结论

三. 开始聚合代码

3.1 开始进行组件的整合

❓ 问 : 帮我整合上面的 Echat ,Tushare , 和 qfluentwidgets ,让我可以在里面看到 K 线图

image.png

  • 结论
    • 这里 Python

3.2 省略一些小细节 ,我又花了半个小时和它聊了会天

❓问 : 我想要把 HTML 文本单独放在一个 .html 文件里面 ,再发起调用

  • 结论 :
    • 这里细节比较曲折 ,AI 的通病 ,有的时候比较笨,总的来说问了这些问题
      1. 我需要从 OS 里面读取 HTML 文件 ,然后在这里通过地址 load
      • 这里是由于 Demo 没有正确从地址里面加载 ,所以需要明确的告诉他重写
      1. 我需要从 OS 里面读取 JSON 文件,并且进行占位符的替换
      • 如果把操作抽象的更细 ,回答的结论就会更清晰
      1. 遍历替换 JSON 中得到占位符
      1. 我需要替换为 JSON 对象 ,而不是字符串
      1. 我需要... 这里陆陆续续改了很多版,达到了一个初步的效果

❓问 : 给我一个占位符替换代码 ,用于替换 JSON 文本里面的占位符

import json
class JsonPlaceholderReplacer:
    def __init__(self, json_data):
        # 将 JSON 字符串解析为 Python 字典
        self.json_data = json.loads(json_data) if isinstance(json_data, str) else json_data

    def replace_placeholders(self, replacements):
        """
        使用一个包含占位符和对应值的字典批量替换 JSON 数据中的占位符。
        :param replacements: dict, 包含 {占位符: 值} 的键值对
        """
        for placeholder, value in replacements.items():
            self._replace_recursive(self.json_data, placeholder, value)

    def replace_placeholder(self, key, value):
        self._replace_recursive(self.json_data, key, value)

    def _replace_recursive(self, data, placeholder, value):
        """递归地遍历和替换 JSON 数据中的占位符"""
        if isinstance(data, dict):
            for key, val in list(data.items()):
                if isinstance(val, (dict, list)):
                    self._replace_recursive(val, placeholder, value)
                elif val == placeholder:
                    # 如果值完全匹配占位符,将其替换为对象或列表
                    data[key] = value
        elif isinstance(data, list):
            for index, item in enumerate(data):
                if isinstance(item, (dict, list)):
                    self._replace_recursive(item, placeholder, value)
                elif item == placeholder:
                    # 如果列表中的项完全匹配占位符,将其替换为对象或列表
                    data[index] = value

    def get_modified_json(self):
        """返回替换后的 JSON 字符串"""
        return json.dumps(self.json_data, ensure_ascii=False, indent=4)

其中还发生了很多追问行为

  • 更复杂一点 ,传入的是一个JSON 对象
  • 按照方法来 def replace_placeholders(self, replacements) + def get_modified_json(self)
  • 继续优化 ,递归地遍历和替换 JSON 数据中的占位符

3.3 最后冲刺 : 马上就要发版了

问 : 帮我优化一下代码 ,让他可以运行

from datetime import datetime, timedelta
import json
import sys
import os
import traceback  # 用于打印异常栈信息
from typing import List
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtCore import QUrl
import pandas as pd
from qfluentwidgets import (
    NavigationInterface,
    NavigationItemPosition,
    setTheme,
    Theme,
)

# 检查外部依赖是否导入成功
try:
    from JsonReplace import JsonPlaceholderReplacer  # 需确保本地存在该模块
    from tushare_das import getDayStock  # 需确保本地存在该模块
except ImportError:
    print("缺少必要的模块 `JsonReplace` 或 `tushare_das`,请检查后重试。")
    sys.exit(1)


class StockChartWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Stock Chart with PyQt-Fluent-Widgets and Echarts")
        self.resize(1200, 800)

        # 设置主题
        setTheme(Theme.LIGHT)

        # 创建导航界面
        navigation_interface = NavigationInterface(self)
        navigation_interface.addItem(
            routeKey="home",
            icon="home",
            text="Home",
            position=NavigationItemPosition.TOP,
        )
        navigation_interface.addItem(
            routeKey="about",
            icon="info",
            text="About",
            position=NavigationItemPosition.BOTTOM,
        )

        # 创建布局和控件
        layout = QVBoxLayout()
        self.web_view = QWebEngineView()

        # 添加控件到布局
        layout.addWidget(self.web_view)

        # 创建中央控件并设置布局
        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        # 加载HTML文件
        self.do_load_chat()

    def do_load_chat(self):
        try:
            current_dir = os.path.dirname(os.path.abspath(__file__))
            chat_html_file = "stock_day.html"
            chat_path = os.path.join(current_dir, chat_html_file)
            if not os.path.exists(chat_path):
                raise FileNotFoundError(f"{chat_html_file} 文件不存在")

            self.web_view.load(QUrl.fromLocalFile(chat_path))
            self.web_view.loadFinished.connect(self.do_load_data)
        except Exception as e:
            print(f"加载HTML失败: {e}")

    def do_load_data(self):
        try:
            current_dir = os.path.dirname(os.path.abspath(__file__))
            chat_json_file = "stock_day.json"
            local_file_path = os.path.join(current_dir, chat_json_file)

            if not os.path.exists(local_file_path):
                raise FileNotFoundError(f"{chat_json_file} 文件不存在")

            with open(local_file_path, "r", encoding="utf-8") as file:
                json_data = json.load(file)

            # 调用函数方法进行解析
            chat_data = self.data_exchange(json_data)
            js_code = f"setData({chat_data});"
            print(f"最终写入参数: {js_code}")
            self.web_view.page().runJavaScript(js_code, self.echat_callback)
        except Exception as e:
            print(f"加载数据失败: {e}")

    def echat_callback(self, result):
        print(f"JavaScript Result: {result}")

    def data_exchange(self, json_data: dict) -> str:
        try:
            today = datetime.today()
            end_time = today.strftime("%Y%m%d")
            start_time = (today - timedelta(days=180)).strftime("%Y%m%d")
            stock_data = getDayStock("600519.SH", start_time, end_time)

            # 检查数据是否为空
            if stock_data is None or stock_data.empty:
                raise ValueError("股票数据为空,无法解析")

            dates_data = self.get_dates(stock_data)
            kline_data = self.get_kline_data(stock_data)
            point_data = self.get_point_data(stock_data)
            mark_data = self.get_markline_data(stock_data)

            replacer = JsonPlaceholderReplacer(json_data)
            replacer.replace_placeholder("__DATAS__", dates_data)
            replacer.replace_placeholder("__STOCK_NAME__", "写死一个骗骗老板")
            replacer.replace_placeholder("__STOCK_DATA__", kline_data)
            replacer.replace_placeholder("__POINT_DATA__", point_data)
            replacer.replace_placeholder("__MARK_DATA__", mark_data)

            return replacer.get_modified_json()
        except Exception as e:
            # 打印异常栈信息
            print(f"数据处理失败: {e}")
            print("异常栈信息如下:")
            print(traceback.format_exc())
            return "{}"

    def get_kline_data(self, stock_data: pd.DataFrame) -> List[List[float]]:
        """
        从股票数据中提取 K 线图数据。
        :param stock_data: Tushare 返回的 DataFrame,包含股票每日交易数据
        :return: K 线图数据 [[open, close, low, high], ...]
        """
        if stock_data.empty:
            print("股票数据为空,无法生成 K 线数据")
            return []

        required_columns = ["open", "close", "low", "high"]
        missing_columns = [
            col for col in required_columns if col not in stock_data.columns
        ]
        if missing_columns:
            raise ValueError(f"缺少以下必要列,无法生成 K 线数据: {missing_columns}")

        # 使用 DataFrame 的 values 提取多列数据为二维列表
        kline_data = stock_data[["open", "close", "low", "high"]].values.tolist()
        print(f"K 线数据生成成功,共 {len(kline_data)} 条记录")
        return kline_data

    def get_dates(self, stock_data: pd.DataFrame) -> List[str]:
        """
        从股票数据中提取并格式化交易日期。
        :param stock_data: Tushare 返回的 DataFrame,包含股票每日交易数据
        :return: 格式化后的日期列表 ['2023-01-01', '2023-01-02', ...]
        """
        if stock_data.empty:
            print("股票数据为空,无法生成日期列表")
            return []

        if "trade_date" not in stock_data.columns:
            raise ValueError("数据中缺少 'trade_date' 列,无法生成日期列表")

        # 使用 apply 格式化日期列
        dates = (
            stock_data["trade_date"]
            .apply(lambda x: datetime.strptime(str(x), "%Y%m%d").strftime("%Y-%m-%d"))
            .tolist()
        )
        print(f"日期列表生成成功,共 {len(dates)} 条记录")
        return dates

    def get_point_data(self, stock_data) -> List[dict]:
        """
        生成包含最高值和最低值的点数据。

        参数:
            stock_data (DataFrame): 包含股票数据的 DataFrame。

        返回:
            List[dict]: 包含最高值和最低值点数据的列表。
        """
        point_data = [
            {"name": "最高值", "type": "max", "valueDim": "highest"},
            {"name": "最低值", "type": "min", "valueDim": "lowest"},
        ]
        return point_data

    def get_markline_data(self, stock_data: pd.DataFrame) -> dict:
        """
        生成辅助线 (markLine) 数据,用于标注收盘价的平均值。
        :param stock_data: Tushare 返回的 DataFrame,包含股票每日交易数据
        :return: 包含辅助线数据的字典
        """
        if stock_data.empty:
            print("股票数据为空,无法生成平均线数据")
            return {"data": []}

        if "close" not in stock_data.columns:
            raise ValueError("数据中缺少 'close' 列,无法生成平均线数据")

        # 计算收盘价平均值
        average_price = stock_data["close"].mean()
        print(f"收盘价平均值计算成功: {average_price:.2f}")

        # 构造辅助线数据
        return {
            "data": [
                {
                    "yAxis": average_price,
                    "name": "平均值",
                    "lineStyle": {
                        "color": "#FF0000",  # 虚线颜色
                        "type": "dashed",  # 虚线样式
                        "width": 2,  # 线条宽度
                    },
                    "label": {
                        "formatter": f"平均值: {average_price:.2f}",  # 显示均值
                        "position": "end",  # 文本位置
                        "color": "#FF0000",  # 文本颜色
                    },
                }
            ]
        }


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = StockChartWindow()
    window.show()
    sys.exit(app.exec())


3.5 终于写完了 ,还能打几把游戏下班

image.png

四. 我用 AI 写了几万行代码了 ,我有很多建议分享给大家

我最近一直在用 AI 代码工具帮我生成代码 ,我也已经实现了一个开源的框架 ,用来做股票风险评估。

可以说 ,其中 90% 的代码都是 AI 生成的 ,我完全相信 ,未来程序员的就业压力会越来越大.

因为哪怕一个零基础的 Python 程序员 ,也能在一周时间开发出一个看得过去的桌面应用。 甚至于大多数情况下都是睡前写一个小时代码。 这你慌不慌???

但是 , 他并不会替代我们 ,它终究只是工具 ,你需要有一定技巧的和他沟通 ,它才能给你准确的答复。

  • 关键一 : 避免问一些宽泛的东西 , 明确的说明你需要的功能点
  • 关键二 : 避免提问给出太多的内容,拆分成一个个方法问
  • 关键三 : 仿写代码时 ,要提供足够的前置条件 ,可以通过 "记住这个..." 这种语法实现
  • 关键四 : 不满意进行追问 ,追问要指明明确的目的

我真的非常建议大家积极使用 AI 编程, 它就像一个结伴编程一样 ,可以帮我们解决大多数重复性的问题。

比如我正在做的开源项目 , 其中这里面的代码都是 AI 帮忙写的 ,花了30分钟不到 ,大概有十几个类

image.png

五. 未来的展望

当然和 ChatGPT 这些对比差距还是很明显的 ,我最喜欢的一个内容是让 AI 帮我仿写代码。比如 GPT 我会这么问 :

image.png

  • 但是在使用豆包的时候效果就很不理想
  • 甚至我要坦白 ,这个 Demo 里面最后的一些贯穿的环节 ,豆包并没有帮我完成
    • 在此期间我换着很多种方式进行了提问 ,效果都不理想
    • 不论是拆分还是合并问 ,最后变化的结果总是会有新的问题。所以最后还是让 GPT 完成了

image.png

总结

其实这个 Demo 在之前的文章里面就出现过一次 ,里面的东西都是 ChatGPT 提供的。

在这个整体思路下 ,我通过 豆包 再次尝试走了一遍。 也通过指定的方式刻意的引导了回答的方向。

整个过程中 ,豆包帮我完成了 80% 的代码 ,我个人还是改了 5% 左右(主要是把 Demo Copy 进来后 ,要改调用方式) ,剩下了 15% 是在串联运行的过程中一直不成功, 让 ChatGPT 来实现的。

整个过程实际耗时 30 - 40 分钟 ,其中大部分时间都在等结果. 实际写代码的时间应该不超过20分钟。

豆包 和 我年中使用的时候 ,已经能基本满足我的业务需求了。我相信未来会更好,加油!!

而我 ,确实是一个 Python 新人 ,这才是最可怕的!!!

拥抱 AI ,别被抛弃。 ❗❗❗❗❗

最后的最后 ❤️❤️❤️👇👇👇