目錄
記錄是必須的
日誌裝飾器
调试崩溃的应用程序
检查堆栈轨迹
在调试期间重新加载模块
首頁 後端開發 Python教學 Python調試的方法是什麼

Python調試的方法是什麼

May 12, 2023 pm 08:13 PM
python

記錄是必須的

如果你編寫應用程式時沒有某種日誌設置,你最終會後悔。如果應用程式中沒有任何日誌,則很難排除任何錯誤。幸運的是,在Python中,設定基本日誌記錄器非常簡單:

import logging
logging.basicConfig(
    filename='application.log',
    level=logging.WARNING,
    format= '[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
    datefmt='%H:%M:%S'
)

logging.error("Some serious error occurred.")
logging.warning('Function you are using is deprecated.')
登入後複製

這就是開始將日誌寫入檔案所需的全部內容,該檔案將如下所示(你可以使用 logging.getLoggerClass().root.handlers[0].baseFilename尋找檔案路徑):

[12:52:35] {<stdin>:1} ERROR - Some serious error occurred.
[12:52:35] {<stdin>:1} WARNING - Function you are using is deprecated.
登入後複製

這種設定似乎已經夠好了(通常情況下也是如此),但配置良好、格式化良好、可讀的日誌可以讓你的生活更輕鬆。改進和擴展配置的一種方法是使用日誌記錄器讀取的.ini.yaml檔案。例如,你可以在配置中執行以下操作:

version: 1
disable_existing_loggers: true

formatters:
  standard:
    format: "[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s"
    datefmt: '%H:%M:%S'

handlers:
  console:  # handler which will log into stdout
    class: logging.StreamHandler
    level: DEBUG
    formatter: standard  # Use formatter defined above
    stream: ext://sys.stdout
  file:  # handler which will log into file
    class: logging.handlers.RotatingFileHandler
    level: WARNING
    formatter: standard  # Use formatter defined above
    filename: /tmp/warnings.log
    maxBytes: 10485760 # 10MB
    backupCount: 10
    encoding: utf8

root:  # Loggers are organized in hierarchy - this is the root logger config
  level: ERROR
  handlers: [console, file]  # Attaches both handler defined above

loggers:  # Defines descendants of root logger
  mymodule:  # Logger for "mymodule"
    level: INFO
    handlers: [file]  # Will only use "file" handler defined above
    propagate: no  # Will not propagate logs to "root" logger
登入後複製

在python程式碼中擁有這種廣泛的配置將很難導航、編輯和維護。將內容保存在YAML檔案中,可以更輕鬆地設定和調整多個日誌記錄程序,這些日誌記錄程序具有非常特定的設置,如上述設定。

如果你想知道所有這些配置欄位的來源,這些都官方文件中記錄,其中大多數只是關鍵字參數,如第一個範例所示。

因此,現在文件中有配置,意味著我們需要以某種方式載入。最簡單的方法是使用YAML檔案:

import yaml
from logging import config

with open("config.yaml", 'rt') as f:
    config_data = yaml.safe_load(f.read())
    config.dictConfig(config_data)
登入後複製

Python logger實際上並不直接支援YAML文件,但它支援字典配置,可以使用yaml.safe_load從YAML輕鬆建立字典設定。如果你傾向於使用舊的.ini文件,那麼我只想指出,根據官方文檔,使用字典配置是新應用程式的建議方法。有關更多範例,請查看官方日誌記錄手冊。

日誌裝飾器

繼續前面的日誌記錄技巧,你可能會遇到需要記錄一些錯誤函數呼叫的情況。你可以使用日誌裝飾器來代替修改所述函數的主體,該裝飾器將使用特定的日誌等級和可選訊息記錄每個函數呼叫。讓我們看看裝飾器:

from functools import wraps, partial
import logging

def attach_wrapper(obj, func=None):  # Helper function that attaches function as attribute of an object
    if func is None:
        return partial(attach_wrapper, obj)
    setattr(obj, func.__name__, func)
    return func

def log(level, message):  # Actual decorator
    def decorate(func):
        logger = logging.getLogger(func.__module__)  # Setup logger
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        handler = logging.StreamHandler()
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        log_message = f"{func.__name__} - {message}"

        @wraps(func)
        def wrapper(*args, **kwargs):  # Logs the message and before executing the decorated function
            logger.log(level, log_message)
            return func(*args, **kwargs)

        @attach_wrapper(wrapper)  # Attaches "set_level" to "wrapper" as attribute
        def set_level(new_level):  # Function that allows us to set log level
            nonlocal level
            level = new_level

        @attach_wrapper(wrapper)  # Attaches "set_message" to "wrapper" as attribute
        def set_message(new_message):  # Function that allows us to set message
            nonlocal log_message
            log_message = f"{func.__name__} - {new_message}"

        return wrapper
    return decorate

# Example Usage
@log(logging.WARN, "example-param")
def somefunc(args):
    return args

somefunc("some args")

somefunc.set_level(logging.CRITICAL)  # Change log level by accessing internal decorator function
somefunc.set_message("new-message")  # Change log message by accessing internal decorator function
somefunc("some args")
登入後複製

毋庸置疑,這可能需要一點時間才能讓你的頭腦清醒(你可能只想複製貼上並使用它)。這裡的想法是,log函數接受參數,並將其提供給內部wrapper函數。然後,透過新增附加到裝飾器的存取器函數,使這些參數可調整。至於functools.wraps裝飾器-若我們在這裡不使用它,函數的名稱( func.__name__)將會被裝飾器的名稱覆寫。但這是個問題,因為我們想印出名字。這可以透過functools.wraps將函數名稱、文件字串和參數清單複製到裝飾器函數來解決。

無論如何,這是上面程式碼的輸出。很整潔,對吧?

2020-05-01 14:42:10,289 - __main__ - WARNING - somefunc - example-param
2020-05-01 14:42:10,289 - __main__ - CRITICAL - somefunc - new-message
登入後複製

__repr__更多可讀日誌

對程式碼的簡單改進使其更易於調試,就是在類別中加入__repr__方法。若你不熟悉這個方法,它所做的只是傳回類別實例的字串表示。使用__repr__方法的最佳實踐是輸出可用於重新建立實例的文字。例如:

class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius

    def __repr__(self):
        return f"Rectangle({self.x}, {self.y}, {self.radius})"

...
c = Circle(100, 80, 30)
repr(c)
# Circle(100, 80, 30)
登入後複製

如果如上所示表示物件是不可取的或不可能的,那麼好的替代方法是使用<...>表示,例如 <_io.TextIOWrapper name='somefile.txt' mode='w' encoding='UTF-8'>

除了__repr__之外,實作__str__方法也是一個好主意,預設情況下,在呼叫print(instance)時使用該方法。使用這兩種方法,只需列印變數即可獲得大量資訊。

__missing__字典的Dunder方法

如果你出於任何原因需要實作自訂字典類,那麼當你嘗試存取一些實際上不存在的鍵時,可能會因為KeyError而出現一些bug。為了避免必須在程式碼中四處查看缺少的,可以實作特殊的__missing__方法,每次引發KeyError時都會呼叫該方法。

class MyDict(dict):
    def __missing__(self, key):
        message = f'{key} not present in the dictionary!'
        logging.warning(message)
        return message  # Or raise some error instead
登入後複製

上面的實作非常簡單,只回傳並​​記錄帶有遺失key的訊息,但你也可以記錄其他有價值的信息,以便為你提供有關程式碼中出現錯誤的更多上下文。

调试崩溃的应用程序

如果你的应用程序在你有机会看到其中发生了什么之前崩溃,你可能会发现这个技巧非常有用。

-i使用参数-i ( python3 -i app.py)运行应用程序会导致它在程序退出后立即启动交互式 shell。此时你可以检查变量和函数。

如果这还不够好,可以使用更大的hammer-pdb-Python调试器。pdb有相当多的特性,可以保证文章的独立性。但这里是一个例子和最重要的部分概要。让我们先看看我们的小崩溃脚本:

# crashing_app.py
SOME_VAR = 42

class SomeError(Exception):
    pass

def func():
    raise SomeError("Something went wrong...")

func()
登入後複製

现在,如果我们使用-i参数运行它,我们就有机会调试它:

# Run crashing application
~ $ python3 -i crashing_app.py
Traceback (most recent call last):
  File "crashing_app.py", line 9, in <module>
    func()
  File "crashing_app.py", line 7, in func
    raise SomeError("Something went wrong...")
__main__.SomeError: Something went wrong...
>>> # We are interactive shell
>>> import pdb
>>> pdb.pm()  # start Post-Mortem debugger
> .../crashing_app.py(7)func()
-> raise SomeError("Something went wrong...")
(Pdb) # Now we are in debugger and can poke around and run some commands:
(Pdb) p SOME_VAR  # Print value of variable
42
(Pdb) l  # List surrounding code we are working with
  2
  3   class SomeError(Exception):
  4       pass
  5
  6   def func():
  7  ->     raise SomeError("Something went wrong...")
  8
  9   func()
[EOF]
(Pdb)  # Continue debugging... set breakpoints, step through the code, etc.
登入後複製

上面的调试会话非常简单地展示了如何使用pdb。程序终止后,我们进入交互式调试会话。首先,我们导入pdb并启动调试器。此时,我们可以使用所有pdb命令。作为上面的示例,我们使用p命令打印变量,使用l命令打印列表代码。大多数情况下,你可能希望设置断点,你可以使用b LINE_NO来设置断点,并运行程序,直到达到断点(c),然后继续使用s单步执行函数,也可以使用w打印堆栈轨迹。有关命令的完整列表,你可以转到官方pdb文档。

检查堆栈轨迹

例如,假设你的代码是在远程服务器上运行的Flask或Django应用程序,你无法在其中获得交互式调试会话。在这种情况下,你可以使用tracebacksys包来了解代码中的错误:

import traceback
import sys

def func():
    try:
        raise SomeError("Something went wrong...")
    except:
        traceback.print_exc(file=sys.stderr)
登入後複製

运行时,上面的代码将打印引发的最后一个异常。除了打印例外,你还可以使用traceback包打印堆栈轨迹(traceback.print_stack())或提取原始堆栈帧,对其进行格式化并进一步检查(traceback.format_list(traceback.extract_stack()))。

在调试期间重新加载模块

有时,你可能正在调试或试验交互式shell中的某些函数,并对其进行频繁更改。为了使运行/测试和修改的循环更容易,你可以运行importlib.reload(module)以避免每次更改后重新启动交互会话:

>>> import func from module
>>> func()
"This is result..."

# Make some changes to "func"
>>> func()
"This is result..."  # Outdated result
>>> from importlib import reload; reload(module)  # Reload "module" after changes made to "func"
>>> func()
"New result..."
登入後複製

这个技巧更多的是关于效率而不是调试。能够跳过一些不必要的步骤,使你的工作流程更快、更高效,这总是很好的。通常,不时地重新加载模块是一个好主意,因为它可以帮助你避免调试同时已经修改过多次的代码。

以上是Python調試的方法是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python vs. JavaScript:學習曲線和易用性 Python vs. JavaScript:學習曲線和易用性 Apr 16, 2025 am 12:12 AM

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

vs code 可以在 Windows 8 中運行嗎 vs code 可以在 Windows 8 中運行嗎 Apr 15, 2025 pm 07:24 PM

VS Code可以在Windows 8上運行,但體驗可能不佳。首先確保系統已更新到最新補丁,然後下載與系統架構匹配的VS Code安裝包,按照提示安裝。安裝後,注意某些擴展程序可能與Windows 8不兼容,需要尋找替代擴展或在虛擬機中使用更新的Windows系統。安裝必要的擴展,檢查是否正常工作。儘管VS Code在Windows 8上可行,但建議升級到更新的Windows系統以獲得更好的開發體驗和安全保障。

sublime怎麼運行代碼python sublime怎麼運行代碼python Apr 16, 2025 am 08:48 AM

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

visual studio code 可以用於 python 嗎 visual studio code 可以用於 python 嗎 Apr 15, 2025 pm 08:18 PM

VS Code 可用於編寫 Python,並提供許多功能,使其成為開發 Python 應用程序的理想工具。它允許用戶:安裝 Python 擴展,以獲得代碼補全、語法高亮和調試等功能。使用調試器逐步跟踪代碼,查找和修復錯誤。集成 Git,進行版本控制。使用代碼格式化工具,保持代碼一致性。使用 Linting 工具,提前發現潛在問題。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

See all articles