动手做科研-day02-如何掌握Python工程流程

01. 拉取一个git仓库

问题1:不知道如何拉取别人的git仓库, 并没有图中的Clone Git Repository


在自己新建的文件夹中不能操作。
解决过程:参考博客, 但是发现并没有..., 需要初始化仓库

初始化后出现..., 按照博客内容进行操作



可以拉取别人的远程仓库, 问题解决

02. 拉取仓库代码运行

问题1: 在点击运行后并不是很理解控制台上的显示是什么意思

[Running] python -u "c:\Users\dell\Desktop\test\hand on reaserch\chapter_02\day_06_git_clone\python-practice\sum_of_list.py"

解决过程:询问GPT

知道了 -u的含义是保证所有的输出得到显示, 问题解决

问题2: 控制台的输出代码不是很懂

Traceback (most recent call last):

解决过程:询问GPT

根据自己以前深度学习跑代码的经验,应该是python开始自动寻找哪个代码出现了问题, 并且从大到小开始寻找问题, 比如先是一个大函数, 然后去里面的具体代码中找哪个地方报了错。

03. debug代码

问题1:在debug过程中的步过和步入有什么区别?

解决过程:
自己在实践中,如果把原来的代码写成一个函数如下:

# 原来的
if __name__ == "__main__":
  a = [0, 1, 2, 3, 4, 5, 6]
    sum = 0
    for i in range(10): # 0-6
        print("i:", i)
        sum = sum + a[i]  # a[7] = ? a[0] = 0, a[1]=1, ... a[6]=6
        print("sum:", sum)


# 改
def main():
    a = [0, 1, 2, 3, 4, 5, 6]
    sum = 0
    for i in range(10): # 0-6
        print("i:", i)
        sum = sum + a[i]  # a[7] = ? a[0] = 0, a[1]=1, ... a[6]=6
        print("sum:", sum)

if __name__ == "__main__":
    main()
将断点打在main()处, 步过
将断点打在main()内部, 步过

可以发现,打在外部的函数调用的地方上,步过不会跳进函数内部执行继续让你执行,而是直接自己执行完返回结果,在外部的地方继续下一步让你执行,如果打断点打在内部则会一步步执行代码。

将断点打在main()处, 步入

实践总结:在范围小的地方,步过和步入基本相同, 在可调用的地方,步过不会具体到里面让你debug,但是步入会进入调用的地方让你继续debug

# GPT回答

在调试(debug)代码时,“步过”(Step Over)和“步入”(Step Into)是两个常用的调试操作,它们用于控制调试器如何执行代码行。它们之间的主要区别在于调试器如何处理函数调用。

### 1. **步过(Step Over)**
- **功能**: “步过”命令会让调试器执行当前行的代码,但如果当前行包含一个函数调用,调试器不会进入该函数的内部,而是直接执行完这个函数后停在下一行代码上。
- **用途**: 当你不关心某个函数的内部实现细节,只想知道它返回了什么,或者你知道该函数已经被充分测试过,不需要再检查它时,可以使用“步过”。

**例子**:
```python
def sum(a, b):
    return a + b

x = sum(2, 3)  # 当前行
y = x * 2
```
如果你在 `x = sum(2, 3)` 这一行使用“步过”,调试器将执行 `sum` 函数,但不会进入 `sum` 函数的内部,而是直接在 `y = x * 2` 这一行暂停。

### 2. **步入(Step Into)**
- **功能**: “步入”命令会让调试器执行当前行的代码,如果当前行包含一个函数调用,调试器会进入该函数的内部,并在函数的第一行代码处暂停。
- **用途**: 当你想要检查函数的内部行为,了解函数内部是如何工作的,或者想要追踪一个可能导致问题的函数时,可以使用“步入”。

**例子**:
```python
def sum(a, b):
    return a + b  # 如果步入,这一行会被暂停

x = sum(2, 3)  # 当前行
y = x * 2
```
如果你在 `x = sum(2, 3)` 这一行使用“步入”,调试器将进入 `sum` 函数,并在 `return a + b` 这一行暂停,允许你逐步调试函数的内部逻辑。

### 总结
- **步过(Step Over)**:执行当前行的代码,不进入函数内部,适用于对函数内部不感兴趣的情况。
- **步入(Step Into)**:执行当前行的代码并进入函数内部,适用于需要调试和检查函数内部逻辑的情况。

和实践类似,问题解决

02. 利用log进行debug

解决:利用GPT查询如何利用log进行debug

# GPT回答
使用日志(log)进行调试是一种非常有效的方式,特别是在处理复杂的系统或无法直接使用调试器的情况下。通过在代码中插入日志语句,你可以跟踪程序的执行流程、变量的值以及发生的异常,帮助你定位和解决问题。

以下是如何使用日志进行调试的详细步骤:

### 1. **导入日志库**
在 Python 中,你可以使用内置的 `logging` 模块进行日志记录。首先需要导入它:

```python
import logging
```

### 2. **配置日志记录**
在使用日志记录之前,你需要配置日志的格式、级别和输出方式(如控制台、文件等)。这是一个简单的配置示例:

```python
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    filename='app.log',  # 如果不需要输出到文件,可以省略 filename 参数
                    filemode='w')  # 'w' 表示每次运行程序时清空日志文件,'a' 表示追加日志
```

- **level**: 设置日志记录的最低级别。常见级别包括:
  - `DEBUG`: 最详细的信息,用于诊断问题。
  - `INFO`: 一般性的信息,如程序正常运行的输出。
  - `WARNING`: 警告信息,表示程序可能有问题,但不影响运行。
  - `ERROR`: 错误信息,表示程序遇到严重问题,可能无法继续运行。
  - `CRITICAL`: 严重错误,表示程序可能会崩溃。
  
- **format**: 指定日志输出的格式。常用的格式占位符包括:
  - `%(asctime)s`: 日志记录的时间戳。
  - `%(levelname)s`: 日志级别。
  - `%(message)s`: 日志消息内容。

- **filename**: 如果你希望将日志输出到文件,可以指定一个文件名。

- **filemode**: 文件模式,'w' 表示每次运行程序时重写日志文件,'a' 表示追加日志到现有文件。

### 3. **插入日志语句**
在需要调试的代码中插入日志语句。根据不同的场景,你可以使用不同的日志级别。例如:

```python
logging.debug('这是一个调试信息,变量 x 的值为: %s', x)
logging.info('程序开始运行')
logging.warning('这是一个警告信息')
logging.error('发生了错误')
logging.critical('这是一个严重错误')
```

### 4. **分析日志输出**
运行程序后,查看生成的日志文件(或控制台输出)来分析程序的行为。通过日志,你可以看到程序执行到哪里、变量的值、是否出现异常等信息。

### 5. **使用日志替代 print 进行调试**
与直接使用 `print` 进行调试不同,使用日志记录有以下优势:
- **可控性**: 通过设置日志级别,可以控制输出的详细程度。例如,在开发阶段使用 `DEBUG` 级别记录详细信息,而在生产环境中使用 `ERROR` 或 `CRITICAL` 级别只记录重要信息。
- **持久性**: 日志可以保存到文件中,方便之后的分析和回溯。
- **格式化输出**: 日志可以带有时间戳、级别信息,帮助更好地理解程序的运行状态。

### 6. **示例代码**
以下是一个使用日志进行调试的完整示例:

```python
import logging

# 配置日志
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def divide(a, b):
    logging.debug("正在执行 divide 函数,a: %s, b: %s", a, b)
    try:
        result = a / b
    except ZeroDivisionError as e:
        logging.error("尝试除以零: %s", e)
        return None
    logging.info("除法结果: %s", result)
    return result

if __name__ == "__main__":
    x = 10
    y = 0
    logging.info("程序开始运行")
    divide(x, y)
    logging.info("程序结束")
```

在这个例子中,`divide` 函数中记录了调试信息和错误处理,而主程序记录了程序的开始和结束。在运行这个程序时,日志将显示出详细的执行过程,并且在发生错误时记录错误信息。

尝试根据给定的示例代码对下拉远程库中的代码进行修改,修改如下:

import logging

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    filename='app.log',  # 如果不需要输出到文件,可以省略 filename 参数
                    filemode='w')  # 'w' 表示每次运行程序时清空日志文件,'a' 表示追加日志


if __name__ == "__main__":
    logging.info("程序开始运行")
    a = [0, 1, 2, 3, 4, 5, 6]
    sum = 0
    for i in range(10):
        logging.debug("i = %d" % i)
        try:
            sum = sum + a[i]
            logging.debug("sum = %d" % sum)
        except IndexError as e:
            logging.error("索引越界")
            break
    logging.info("程序运行结束")

03. 利用log进行debug后记录的.log文件有乱码现象


解决过程:
参考博客得知, 在logging进行记录时默认使用ASCII码进行编码,导致中文无法正常编码,会编成乱码, 需要采用utf-8进行编码。

# 在logging配置处增加编码方式

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    filename='app.log',  # 如果不需要输出到文件,可以省略 filename 参数
                    filemode='w', # 'w' 表示每次运行程序时清空日志文件,'a' 表示追加日志
                    encoding='utf-8')  

结果生成app.log

问题解决

posted @ 2024-08-13 16:41  yiranxie233  阅读(45)  评论(0)    收藏  举报