标签归档:debugging

Python内存泄漏

问题:Python内存泄漏

我有一个长时间运行的脚本,如果让脚本运行足够长的时间,它将消耗系统上的所有内存。

在不详细介绍脚本的情况下,我有两个问题:

  1. 是否有可遵循的“最佳实践”,以防止泄漏发生?
  2. 有什么技术可以调试Python中的内存泄漏?

I have a long-running script which, if let to run long enough, will consume all the memory on my system.

Without going into details about the script, I have two questions:

  1. Are there any “Best Practices” to follow, which will help prevent leaks from occurring?
  2. What techniques are there to debug memory leaks in Python?

回答 0

看看这篇文章:跟踪python内存泄漏

另外,请注意,垃圾收集模块实际上可以设置调试标志。看一下set_debug功能。此外,请查看Gnibbler的这段代码,以确定调用后已创建的对象的类型。

Have a look at this article: Tracing python memory leaks

Also, note that the garbage collection module actually can have debug flags set. Look at the set_debug function. Additionally, look at this code by Gnibbler for determining the types of objects that have been created after a call.


回答 1

我尝试了前面提到的大多数选项,但是发现这个小而直观的软件包是最好的:pympler

跟踪未进行垃圾收集的对象非常简单,请查看以下小示例:

通过安装软件包 pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

输出显示您已添加的所有对象,以及它们消耗的内存。

样本输出:

                                 types |   # objects |   total size
====================================== | =========== | ============
                                  list |        1095 |    160.78 KB
                                   str |        1093 |     66.33 KB
                                   int |         120 |      2.81 KB
                                  dict |           3 |       840 B
      frame (codename: create_summary) |           1 |       560 B
          frame (codename: print_diff) |           1 |       480 B

该软件包提供了更多的功能。查看pympler的文档,特别是确定内存泄漏一节。

I tried out most options mentioned previously but found this small and intuitive package to be the best: pympler

It’s quite straight forward to trace objects that were not garbage-collected, check this small example:

install package via pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

The output shows you all the objects that have been added, plus the memory they consumed.

Sample output:

                                 types |   # objects |   total size
====================================== | =========== | ============
                                  list |        1095 |    160.78 KB
                                   str |        1093 |     66.33 KB
                                   int |         120 |      2.81 KB
                                  dict |           3 |       840 B
      frame (codename: create_summary) |           1 |       560 B
          frame (codename: print_diff) |           1 |       480 B

This package provides a number of more features. Check pympler’s documentation, in particular the section Identifying memory leaks.


回答 2

让我推荐我创建的mem_top工具

它帮助我解决了类似的问题

它只是立即显示Python程序中内存泄漏的主要嫌疑人

Let me recommend mem_top tool I created

It helped me to solve a similar issue

It just instantly shows top suspects for memory leaks in a Python program


回答 3

从Python 3.4开始,Tracemalloc模块已集成为内置模块,并且显然,它也可以作为第三方库用于Python的早期版本(尽管尚未测试)。

该模块能够输出分配最多内存的精确文件和行。恕我直言,此信息比每种类型分配的实例数更有价值(99%的时间最终是很多元组,这是一个线索,但在大多数情况下几乎没有帮助)。

我建议您将tracemalloc与pyrasite结合使用。每10个中有9 pyrasite外壳中运行前10个代码段,将为您提供足够的信息和提示,以在10分钟内修复泄漏。但是,如果仍然无法找到泄漏的原因,pyrasite-shell与该线程中提到的其他工具的组合可能也会为您提供更多提示。您还应该查看吡铁矿提供的所有其他帮助程序(例如内存查看器)。

Tracemalloc module was integrated as a built-in module starting from Python 3.4, and appearently, it’s also available for prior versions of Python as a third-party library (haven’t tested it though).

This module is able to output the precise files and lines that allocated the most memory. IMHO, this information is infinitly more valuable than the number of allocated instances for each type (which ends up being a lot of tuples 99% of the time, which is a clue, but barely helps in most cases).

I recommend you use tracemalloc in combination with pyrasite. 9 times out of 10, running the top 10 snippet in a pyrasite-shell will give you enough information and hints to to fix the leak within 10 minutes. Yet, if you’re still unable to find the leak cause, pyrasite-shell in combination with the other tools mentioned in this thread will probably give you some more hints too. You should also take a look on all the extra helpers provided by pyrasite (such as the memory viewer).


回答 4

您应该特别查看全局数据或静态数据(寿命长的数据)。

当这些数据不受限制地增长时,您也会在Python中遇到麻烦。

垃圾收集器只能收集不再引用的数据。但是您的静态数据可以连接应释放的数据元素。

另一个问题可能是内存循环,但是至少从理论上讲,垃圾收集器应该找到并消除循环-至少只要不将其挂接到某些长期存在的数据上即可。

什么样的长期数据特别麻烦?在所有列表和字典上都有一个很好的外观-它们可以不受限制地增长。在字典中,您甚至可能看不到麻烦,因为当您访问字典时,字典中的键数可能对您来说不是很明显。

You should specially have a look on your global or static data (long living data).

When this data grows without restriction, you can also get troubles in Python.

The garbage collector can only collect data, that is not referenced any more. But your static data can hookup data elements that should be freed.

Another problem can be memory cycles, but at least in theory the Garbage collector should find and eliminate cycles — at least as long as they are not hooked on some long living data.

What kinds of long living data are specially troublesome? Have a good look on any lists and dictionaries — they can grow without any limit. In dictionaries you might even don’t see the trouble coming since when you access dicts, the number of keys in the dictionary might not be of big visibility to you …


回答 5

为了检测和定位长时间运行的进程(例如在生产环境中)的内存泄漏,现在可以使用stackimpact。它在下面使用tracemalloc这篇文章中的更多信息。

To detect and locate memory leaks for long running processes, e.g. in production environments, you can now use stackimpact. It uses tracemalloc underneath. More info in this post.


回答 6

就最佳实践而言,请留意递归函数。就我而言,我遇到了递归问题(不需要递归)。我正在做什么的简化示例:

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    if my_flag:  # restart the function if a certain flag is true
        my_function()

def main():
    my_function()

以这种递归方式进行操作不会触发垃圾回收并清除功能的其余部分,因此每次通过内存使用的时间都在增长。

我的解决方案是将递归调用从my_function()中拉出,并在再次调用时让main()处理。这样,功能自然结束,并在运行后自动清理。

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    .....
    return my_flag

def main():
    result = my_function()
    if result:
        my_function()

As far as best practices, keep an eye for recursive functions. In my case I ran into issues with recursion (where there didn’t need to be). A simplified example of what I was doing:

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    if my_flag:  # restart the function if a certain flag is true
        my_function()

def main():
    my_function()

operating in this recursive manner won’t trigger the garbage collection and clear out the remains of the function, so every time through memory usage is growing and growing.

My solution was to pull the recursive call out of my_function() and have main() handle when to call it again. this way the function ends naturally and cleans up after itself.

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    .....
    return my_flag

def main():
    result = my_function()
    if result:
        my_function()

回答 7

不确定python中内存泄漏的“最佳实践”,但是python应该通过垃圾回收器清除它自己的内存。因此,主要是我从检查一些简短的循环列表开始,因为它们不会被垃圾收集器接收。

Not sure about “Best Practices” for memory leaks in python, but python should clear it’s own memory by it’s garbage collector. So mainly I would start by checking for circular list of some short, since they won’t be picked up by the garbage collector.


回答 8

这绝不是详尽的建议。但是在编写时要避免将来的内存泄漏(循环),要记住的第一件事是要确保接受回调引用的任何内容都应将该回调存储为弱引用。

This is by no means exhaustive advice. But number one thing to keep in mind when writing with the thought of avoiding future memory leaks (loops) is to make sure that anything which accepts a reference to a call-back, should store that call-back as a weak reference.


从Python代码中的方法打印当前调用堆栈

问题:从Python代码中的方法打印当前调用堆栈

在Python中,如何从方法中打印当前调用堆栈(出于调试目的)。

In Python, how can I print the current call stack from within a method (for debugging purposes).


回答 0

这是通过traceback模块获取堆栈并进行打印的示例:

import traceback

def f():
    g()

def g():
    for line in traceback.format_stack():
        print(line.strip())

f()

# Prints:
# File "so-stack.py", line 10, in <module>
#     f()
# File "so-stack.py", line 4, in f
#     g()
# File "so-stack.py", line 7, in g
#     for line in traceback.format_stack():

如果您真的只想将堆栈打印到stderr,则可以使用:

traceback.print_stack()

或打印到标准输出(如果要一起保留重定向输出很有用),请使用:

traceback.print_stack(file=sys.stdout)

但是通过获取它可以traceback.format_stack()让您随心所欲地使用它。

Here’s an example of getting the stack via the traceback module, and printing it:

import traceback

def f():
    g()

def g():
    for line in traceback.format_stack():
        print(line.strip())

f()

# Prints:
# File "so-stack.py", line 10, in <module>
#     f()
# File "so-stack.py", line 4, in f
#     g()
# File "so-stack.py", line 7, in g
#     for line in traceback.format_stack():

If you really only want to print the stack to stderr, you can use:

traceback.print_stack()

Or to print to stdout (useful if want to keep redirected output together), use:

traceback.print_stack(file=sys.stdout)

But getting it via traceback.format_stack() lets you do whatever you like with it.


回答 1

import traceback
traceback.print_stack()
import traceback
traceback.print_stack()

回答 2

inspect.stack() 返回当前堆栈,而不是异常回溯:

import inspect
print inspect.stack()

请参阅https://gist.github.com/FredLoney/5454553以获取log_stack实用程序功能。

inspect.stack() returns the current stack rather than the exception traceback:

import inspect
print inspect.stack()

See https://gist.github.com/FredLoney/5454553 for a log_stack utility function.


回答 3

如果使用python调试器,则不仅可以进行变量的交互式探测,还可以使用“ where”命令或“ w”获得调用堆栈。

因此,在程序顶部

import pdb

然后在代码中您要查看发生了什么

pdb.set_trace()

并提示您

If you use python debugger, not only interactive probing of variables but you can get the call stack with the “where” command or “w”.

So at the top of your program

import pdb

Then in the code where you want to see what is happening

pdb.set_trace()

and you get dropped into a prompt


回答 4

对于那些在使用pdb时需要打印调用堆栈的用户,只需执行

(Pdb) where

for those who need to print the call stack while using pdb, just do

(Pdb) where

回答 5

这是@RichieHindle出色答案的一个变体,它实现了一个装饰器,该装饰器可以根据需要有选择地应用于函数。适用于Python 2.7.14和3.6.4。

from __future__ import print_function
import functools
import traceback
import sys

INDENT = 4*' '

def stacktrace(func):
    @functools.wraps(func)
    def wrapped(*args, **kwds):
        # Get all but last line returned by traceback.format_stack()
        # which is the line below.
        callstack = '\n'.join([INDENT+line.strip() for line in traceback.format_stack()][:-1])
        print('{}() called:'.format(func.__name__))
        print(callstack)
        return func(*args, **kwds)

    return wrapped

@stacktrace
def test_func():
    return 42

print(test_func())

样本输出:

test_func() called:
    File "stacktrace_decorator.py", line 28, in <module>
    print(test_func())
42

Here’s a variation of @RichieHindle’s excellent answer which implements a decorator that can be selectively applied to functions as desired. Works with Python 2.7.14 and 3.6.4.

from __future__ import print_function
import functools
import traceback
import sys

INDENT = 4*' '

def stacktrace(func):
    @functools.wraps(func)
    def wrapped(*args, **kwds):
        # Get all but last line returned by traceback.format_stack()
        # which is the line below.
        callstack = '\n'.join([INDENT+line.strip() for line in traceback.format_stack()][:-1])
        print('{}() called:'.format(func.__name__))
        print(callstack)
        return func(*args, **kwds)

    return wrapped

@stacktrace
def test_func():
    return 42

print(test_func())

Output from sample:

test_func() called:
    File "stacktrace_decorator.py", line 28, in <module>
    print(test_func())
42

回答 6

安装检查

pip3 install inspect-it --user

import inspect;print(*['\n\x1b[0;36;1m| \x1b[0;32;1m{:25}\x1b[0;36;1m| \x1b[0;35;1m{}'.format(str(x.function), x.filename+'\x1b[0;31;1m:'+str(x.lineno)+'\x1b[0m') for x in inspect.stack()])

你可以做这行的片段

它会向您显示函数调用堆栈的列表,其中包含文件名和行号

从头到尾放置此行的列表

Install Inspect-it

pip3 install inspect-it --user

Code

import inspect;print(*['\n\x1b[0;36;1m| \x1b[0;32;1m{:25}\x1b[0;36;1m| \x1b[0;35;1m{}'.format(str(x.function), x.filename+'\x1b[0;31;1m:'+str(x.lineno)+'\x1b[0m') for x in inspect.stack()])

you can Make a snippet of this line

it will show you a list of the function call stack with a filename and line number

list from start to where you put this line


如何查看Python应用程序发送的整个HTTP请求?

问题:如何查看Python应用程序发送的整个HTTP请求?

就我而言,我正在使用该requests库通过HTTPS调用PayPal的API。不幸的是,我从贝宝(PayPal)收到错误消息,贝宝(PayPal)支持人员无法弄清错误是什么或由什么引起的。他们要我“请提供整个请求,包括标头”。

我怎样才能做到这一点?

In my case, I’m using the requests library to call PayPal’s API over HTTPS. Unfortunately, I’m getting an error from PayPal, and PayPal support cannot figure out what the error is or what’s causing it. They want me to “Please provide the entire request, headers included”.

How can I do that?


回答 0

一种简单的方法:启用登录请求的最新版本(1.x及更高版本)。

请求用途http.clientlogging模块配置到控制日志记录级别,如所描述这里

示范

从链接文档中摘录的代码:

import requests
import logging

# These two lines enable debugging at httplib level (requests->urllib3->http.client)
# You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.
# The only thing missing will be the response.body which is not logged.
try:
    import http.client as http_client
except ImportError:
    # Python 2
    import httplib as http_client
http_client.HTTPConnection.debuglevel = 1

# You must initialize logging, otherwise you'll not see debug output.
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True

requests.get('https://httpbin.org/headers')

示例输出

$ python requests-logging.py 
INFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): httpbin.org
send: 'GET /headers HTTP/1.1\r\nHost: httpbin.org\r\nAccept-Encoding: gzip, deflate, compress\r\nAccept: */*\r\nUser-Agent: python-requests/1.2.0 CPython/2.7.3 Linux/3.2.0-48-generic\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/json
header: Date: Sat, 29 Jun 2013 11:19:34 GMT
header: Server: gunicorn/0.17.4
header: Content-Length: 226
header: Connection: keep-alive
DEBUG:requests.packages.urllib3.connectionpool:"GET /headers HTTP/1.1" 200 226

A simple method: enable logging in recent versions of Requests (1.x and higher.)

Requests uses the http.client and logging module configuration to control logging verbosity, as described here.

Demonstration

Code excerpted from the linked documentation:

import requests
import logging

# These two lines enable debugging at httplib level (requests->urllib3->http.client)
# You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.
# The only thing missing will be the response.body which is not logged.
try:
    import http.client as http_client
except ImportError:
    # Python 2
    import httplib as http_client
http_client.HTTPConnection.debuglevel = 1

# You must initialize logging, otherwise you'll not see debug output.
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True

requests.get('https://httpbin.org/headers')

Example Output

$ python requests-logging.py 
INFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): httpbin.org
send: 'GET /headers HTTP/1.1\r\nHost: httpbin.org\r\nAccept-Encoding: gzip, deflate, compress\r\nAccept: */*\r\nUser-Agent: python-requests/1.2.0 CPython/2.7.3 Linux/3.2.0-48-generic\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/json
header: Date: Sat, 29 Jun 2013 11:19:34 GMT
header: Server: gunicorn/0.17.4
header: Content-Length: 226
header: Connection: keep-alive
DEBUG:requests.packages.urllib3.connectionpool:"GET /headers HTTP/1.1" 200 226

回答 1

r = requests.get('https://api.github.com', auth=('user', 'pass'))

r是回应。它具有一个request属性,其中包含您需要的信息。

r.request.allow_redirects  r.request.headers          r.request.register_hook
r.request.auth             r.request.hooks            r.request.response
r.request.cert             r.request.method           r.request.send
r.request.config           r.request.params           r.request.sent
r.request.cookies          r.request.path_url         r.request.session
r.request.data             r.request.prefetch         r.request.timeout
r.request.deregister_hook  r.request.proxies          r.request.url
r.request.files            r.request.redirect         r.request.verify

r.request.headers 给出标题:

{'Accept': '*/*',
 'Accept-Encoding': 'identity, deflate, compress, gzip',
 'Authorization': u'Basic dXNlcjpwYXNz',
 'User-Agent': 'python-requests/0.12.1'}

然后r.request.data将主体作为映射。urllib.urlencode如果他们愿意,可以将其转换为:

import urllib
b = r.request.data
encoded_body = urllib.urlencode(b)

根据响应的类型,.data-attribute可能会丢失,而.body-attribute会在那里。

r = requests.get('https://api.github.com', auth=('user', 'pass'))

r is a response. It has a request attribute which has the information you need.

r.request.allow_redirects  r.request.headers          r.request.register_hook
r.request.auth             r.request.hooks            r.request.response
r.request.cert             r.request.method           r.request.send
r.request.config           r.request.params           r.request.sent
r.request.cookies          r.request.path_url         r.request.session
r.request.data             r.request.prefetch         r.request.timeout
r.request.deregister_hook  r.request.proxies          r.request.url
r.request.files            r.request.redirect         r.request.verify

r.request.headers gives the headers:

{'Accept': '*/*',
 'Accept-Encoding': 'identity, deflate, compress, gzip',
 'Authorization': u'Basic dXNlcjpwYXNz',
 'User-Agent': 'python-requests/0.12.1'}

Then r.request.data has the body as a mapping. You can convert this with urllib.urlencode if they prefer:

import urllib
b = r.request.data
encoded_body = urllib.urlencode(b)

depending on the type of the response the .data-attribute may be missing and a .body-attribute be there instead.


回答 2

您可以使用HTTP Toolkit来做到这一点。

如果您需要在不更改代码的情况下快速执行此操作,则特别有用:您可以从HTTP Toolkit打开终端,从那里正常运行任何Python代码,并且可以查看每个HTTP / HTTPS的全部内容。立即提出要求。

有一个免费版本可以满足您的所有需求,并且它是100%开放源代码。

我是HTTP Toolkit的创建者;我实际上是自己构建的,以便不久前为我解决完全相同的问题!我也曾尝试调试付款集成,但他们的SDK无法正常工作,我无法告知原因,我需要知道实际情况如何对其进行正确修复。这非常令人沮丧,但是能够看到原始流量确实有所帮助。

You can use HTTP Toolkit to do exactly this.

It’s especially useful if you need to do this quickly, with no code changes: you can open a terminal from HTTP Toolkit, run any Python code from there as normal, and you’ll be able to see the full content of every HTTP/HTTPS request immediately.

There’s a free version that can do everything you need, and it’s 100% open source.

I’m the creator of HTTP Toolkit; I actually built it myself to solve the exact same problem for me a while back! I too was trying to debug a payment integration, but their SDK didn’t work, I couldn’t tell why, and I needed to know what was actually going on to properly fix it. It’s very frustrating, but being able to see the raw traffic really helps.


回答 3

如果您使用的是Python 2.x,请尝试安装urllib2打开器。尽管您可能必须将其与您用来击中HTTPS的其他开启器结合使用,但这应该可以打印出标题。

import urllib2
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPHandler(debuglevel=1)))
urllib2.urlopen(url)

If you’re using Python 2.x, try installing a urllib2 opener. That should print out your headers, although you may have to combine that with other openers you’re using to hit the HTTPS.

import urllib2
urllib2.install_opener(urllib2.build_opener(urllib2.HTTPHandler(debuglevel=1)))
urllib2.urlopen(url)

回答 4

verbose配置选项可以让你看到你想要的东西。文档中一个示例

注意:请阅读以下注释:详细的配置选项似乎不再可用。

The verbose configuration option might allow you to see what you want. There is an example in the documentation.

NOTE: Read the comments below: The verbose config options doesn’t seem to be available anymore.


显示正在运行的Python应用程序的堆栈跟踪

问题:显示正在运行的Python应用程序的堆栈跟踪

我有这个Python应用程序,它有时会卡住,我找不到位置。

有什么方法可以让Python解释器向您显示正在运行的确切代码吗?

某种动态堆栈跟踪?

相关问题:

I have this Python application that gets stuck from time to time and I can’t find out where.

Is there any way to signal Python interpreter to show you the exact code that’s running?

Some kind of on-the-fly stacktrace?

Related questions:


回答 0

我有用于以下情况的模块-进程将长时间运行,但有时由于未知且不可复制的原因而卡住。它有点hacky,并且只能在unix上运行(需要信号):

import code, traceback, signal

def debug(sig, frame):
    """Interrupt running process, and provide a python prompt for
    interactive debugging."""
    d={'_frame':frame}         # Allow access to frame object.
    d.update(frame.f_globals)  # Unless shadowed by global
    d.update(frame.f_locals)

    i = code.InteractiveConsole(d)
    message  = "Signal received : entering python shell.\nTraceback:\n"
    message += ''.join(traceback.format_stack(frame))
    i.interact(message)

def listen():
    signal.signal(signal.SIGUSR1, debug)  # Register handler

要使用它,只需在程序启动时在某个时候调用listen()函数(您甚至可以将其粘贴在site.py中,以使所有python程序都使用它),然后使其运行。在任何时候,使用kill或在python中向进程发送SIGUSR1信号:

    os.kill(pid, signal.SIGUSR1)

这将导致程序在当前位置中断到python控制台,向您显示堆栈跟踪,并允许您操作变量。使用control-d(EOF)继续运行(尽管请注意,您可能会在发出信号的那一刻中断任何I / O等,因此它并不是完全非侵入式的。

我有另一个执行相同功能的脚本,除了它通过管道与正在运行的进程通信(允许调试后台进程等)。它在这里发布有点大,但我已将其添加为python食谱

I have module I use for situations like this – where a process will be running for a long time but gets stuck sometimes for unknown and irreproducible reasons. Its a bit hacky, and only works on unix (requires signals):

import code, traceback, signal

def debug(sig, frame):
    """Interrupt running process, and provide a python prompt for
    interactive debugging."""
    d={'_frame':frame}         # Allow access to frame object.
    d.update(frame.f_globals)  # Unless shadowed by global
    d.update(frame.f_locals)

    i = code.InteractiveConsole(d)
    message  = "Signal received : entering python shell.\nTraceback:\n"
    message += ''.join(traceback.format_stack(frame))
    i.interact(message)

def listen():
    signal.signal(signal.SIGUSR1, debug)  # Register handler

To use, just call the listen() function at some point when your program starts up (You could even stick it in site.py to have all python programs use it), and let it run. At any point, send the process a SIGUSR1 signal, using kill, or in python:

    os.kill(pid, signal.SIGUSR1)

This will cause the program to break to a python console at the point it is currently at, showing you the stack trace, and letting you manipulate the variables. Use control-d (EOF) to continue running (though note that you will probably interrupt any I/O etc at the point you signal, so it isn’t fully non-intrusive.

I’ve another script that does the same thing, except it communicates with the running process through a pipe (to allow for debugging backgrounded processes etc). Its a bit large to post here, but I’ve added it as a python cookbook recipe.


回答 1

安装信号处理程序的建议是一个不错的建议,我经常使用它。例如,默认情况下,bzr安装一个SIGQUIT处理程序,该处理程序pdb.set_trace()将立即调用以将您放入pdb提示符。(有关详细信息,请参见bzrlib.breakin模块的源代码。)使用pdb,您不仅可以获取当前的堆栈跟踪信息,还可以检查变量等。

但是,有时我需要调试一个没有先见之明的进程来安装信号处理程序。在linux上,您可以将gdb附加到该进程并获取带有某些gdb宏的python堆栈跟踪。将http://svn.python.org/projects/python/trunk/Misc/gdbinit放在中~/.gdbinit,然后:

  • 附加gdb: gdb -p PID
  • 获取python堆栈跟踪: pystack

不幸的是,它并不是完全可靠的,但是大多数情况下它都可以工作。

最后,附加strace通常可以使您很好地了解流程在做什么。

The suggestion to install a signal handler is a good one, and I use it a lot. For example, bzr by default installs a SIGQUIT handler that invokes pdb.set_trace() to immediately drop you into a pdb prompt. (See the bzrlib.breakin module’s source for the exact details.) With pdb you can not only get the current stack trace but also inspect variables, etc.

However, sometimes I need to debug a process that I didn’t have the foresight to install the signal handler in. On linux, you can attach gdb to the process and get a python stack trace with some gdb macros. Put http://svn.python.org/projects/python/trunk/Misc/gdbinit in ~/.gdbinit, then:

  • Attach gdb: gdb -p PID
  • Get the python stack trace: pystack

It’s not totally reliable unfortunately, but it works most of the time.

Finally, attaching strace can often give you a good idea what a process is doing.


回答 2

我几乎总是与多个线程打交道,而主线程通常不会做很多事情,所以最有趣的是转储所有堆栈(这更像Java的转储)。这是基于此博客的实现:

import threading, sys, traceback

def dumpstacks(signal, frame):
    id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
    code = []
    for threadId, stack in sys._current_frames().items():
        code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

I am almost always dealing with multiple threads and main thread is generally not doing much, so what is most interesting is to dump all the stacks (which is more like the Java’s dump). Here is an implementation based on this blog:

import threading, sys, traceback

def dumpstacks(signal, frame):
    id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
    code = []
    for threadId, stack in sys._current_frames().items():
        code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

回答 3

可以使用pyrasite来获取未经准备的 python程序的堆栈跟踪,并在没有调试符号的情况下在现有的python中运行。在Ubuntu Trusty上对我来说就像是一种魅力:

$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
$ sudo pyrasite 16262 dump_stacks.py # dumps stacks to stdout/stderr of the python program

(有关@Albert的提示,以及其他工具,其答案中包含指向此的指针。)

Getting a stack trace of an unprepared python program, running in a stock python without debugging symbols can be done with pyrasite. Worked like a charm for me in on Ubuntu Trusty:

$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
$ sudo pyrasite 16262 dump_stacks.py # dumps stacks to stdout/stderr of the python program

(Hat tip to @Albert, whose answer contained a pointer to this, among other tools.)


回答 4

>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]

您还可以很好地格式化堆栈跟踪,请参阅docs

编辑:要模拟Java的行为,如@Douglas Leeder所建议,请添加以下内容:

import signal
import traceback

signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))

应用程序中的启动代码。然后,您可以通过发送SIGUSR1到正在运行的Python进程来打印堆栈。

>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]

You can also nicely format the stack trace, see the docs.

Edit: To simulate Java’s behavior, as suggested by @Douglas Leeder, add this:

import signal
import traceback

signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))

to the startup code in your application. Then you can print the stack by sending SIGUSR1 to the running Python process.


回答 5

回溯模块有一些不错的功能,其中包括:print_stack:

import traceback

traceback.print_stack()

The traceback module has some nice functions, among them: print_stack:

import traceback

traceback.print_stack()

回答 6

您可以尝试使用Faulthandler模​​块。使用安装pip install faulthandler并添加:

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)

在程序开始时。然后将SIGUSR1发送到您的进程(例如:)以kill -USR1 42显示所有线程到标准输出的Python追溯。阅读文档以了解更多选项(例如:登录文件)和其他显示回溯的方式。

该模块现在是Python 3.3的一部分。对于Python 2,请参见http://faulthandler.readthedocs.org/

You can try the faulthandler module. Install it using pip install faulthandler and add:

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)

at the beginning of your program. Then send SIGUSR1 to your process (ex: kill -USR1 42) to display the Python traceback of all threads to the standard output. Read the documentation for more options (ex: log into a file) and other ways to display the traceback.

The module is now part of Python 3.3. For Python 2, see http://faulthandler.readthedocs.org/


回答 7

真正帮助我的是spiv的技巧(如果我没有信誉点,我会投票并提出评论),以获取未经准备的 Python进程的堆栈跟踪。直到我修改了gdbinit脚本起作用。所以:

  • 下载http://svn.python.org/projects/python/trunk/Misc/gdbinit并将其放入~/.gdbinit

  • 编辑它,更改PyEval_EvalFramePyEval_EvalFrameEx[编辑:不再需要;截至2010年1月14日,链接文件已具有此更改]

  • 附加gdb: gdb -p PID

  • 获取python堆栈跟踪: pystack

What really helped me here is spiv’s tip (which I would vote up and comment on if I had the reputation points) for getting a stack trace out of an unprepared Python process. Except it didn’t work until I modified the gdbinit script. So:

  • download http://svn.python.org/projects/python/trunk/Misc/gdbinit and put it in ~/.gdbinit

  • edit it, changing PyEval_EvalFrame to PyEval_EvalFrameEx [edit: no longer needed; the linked file already has this change as of 2010-01-14]

  • Attach gdb: gdb -p PID

  • Get the python stack trace: pystack


回答 8

我会将其添加为haridsv的评论,但我缺乏这样做的声誉:

我们中有些人仍然停留在2.6之前的python版本(Thread.ident必需)上,因此我得到的代码在Python 2.5中工作(尽管未显示线程名称),如下所示:

import traceback
import sys
def dumpstacks(signal, frame):
    code = []
    for threadId, stack in sys._current_frames().items():
            code.append("\n# Thread: %d" % (threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

I would add this as a comment to haridsv’s response, but I lack the reputation to do so:

Some of us are still stuck on a version of Python older than 2.6 (required for Thread.ident), so I got the code working in Python 2.5 (though without the thread name being displayed) as such:

import traceback
import sys
def dumpstacks(signal, frame):
    code = []
    for threadId, stack in sys._current_frames().items():
            code.append("\n# Thread: %d" % (threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

回答 9

python -dv yourscript.py

这将使解释器以调试模式运行,并为您提供解释器正在执行的操作的轨迹。

如果要交互式调试代码,则应按以下方式运行它:

python -m pdb yourscript.py

这告诉python解释器使用模块“ pdb”(即python调试器)运行脚本,如果您像这样运行解释器,则解释器将以交互模式执行,就像GDB一样

python -dv yourscript.py

That will make the interpreter to run in debug mode and to give you a trace of what the interpreter is doing.

If you want to interactively debug the code you should run it like this:

python -m pdb yourscript.py

That tells the python interpreter to run your script with the module “pdb” which is the python debugger, if you run it like that the interpreter will be executed in interactive mode, much like GDB


回答 10

看一下faulthandlerPython 3.3中的新模块。一个faulthandler反向移植了在Python 2使用可PyPI上。

Take a look at the faulthandler module, new in Python 3.3. A faulthandler backport for use in Python 2 is available on PyPI.


回答 11

在Solaris上,可以使用pstack(1)无需更改python代码。例如。

# pstack 16000 | grep : | head
16000: /usr/bin/python2.6 /usr/lib/pkg.depotd --cfg svc:/application/pkg/serv
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:282 (_wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:295 (wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:242 (block) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/_init_.py:249 (quickstart) ]
[ /usr/lib/pkg.depotd:890 (<module>) ]
[ /usr/lib/python2.6/threading.py:256 (wait) ]
[ /usr/lib/python2.6/Queue.py:177 (get) ]
[ /usr/lib/python2.6/vendor-packages/pkg/server/depot.py:2142 (run) ]
[ /usr/lib/python2.6/threading.py:477 (run)
etc.

On Solaris, you can use pstack(1) No changes to the python code are necessary. eg.

# pstack 16000 | grep : | head
16000: /usr/bin/python2.6 /usr/lib/pkg.depotd --cfg svc:/application/pkg/serv
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:282 (_wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:295 (wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:242 (block) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/_init_.py:249 (quickstart) ]
[ /usr/lib/pkg.depotd:890 (<module>) ]
[ /usr/lib/python2.6/threading.py:256 (wait) ]
[ /usr/lib/python2.6/Queue.py:177 (get) ]
[ /usr/lib/python2.6/vendor-packages/pkg/server/depot.py:2142 (run) ]
[ /usr/lib/python2.6/threading.py:477 (run)
etc.

回答 12

如果您使用的是Linux系统,请结合使用gdbPython调试扩展(可以在包装中python-dbgpython-debuginfo打包中)使用。它还对多线程应用程序,GUI应用程序和C模块有帮助。

使用以下命令运行程序:

$ gdb -ex r --args python <programname>.py [arguments]

这指示gdb准备python <programname>.py <arguments>r取消准备。

现在,当程序挂起时,切换到gdb控制台,按Ctr+C并执行:

(gdb) thread apply all py-list

在此处此处查看示例会话和更多信息。

If you’re on a Linux system, use the awesomeness of gdb with Python debug extensions (can be in python-dbg or python-debuginfo package). It also helps with multithreaded applications, GUI applications and C modules.

Run your program with:

$ gdb -ex r --args python <programname>.py [arguments]

This instructs gdb to prepare python <programname>.py <arguments> and run it.

Now when you program hangs, switch into gdb console, press Ctr+C and execute:

(gdb) thread apply all py-list

See example session and more info here and here.


回答 13

我一直在寻找调试我的线程的解决方案,由于使用haridsv,我在这里找到了它。我使用采用traceback.print_stack()的稍微简化的版本:

import sys, traceback, signal
import threading
import os

def dumpstacks(signal, frame):
  id2name = dict((th.ident, th.name) for th in threading.enumerate())
  for threadId, stack in sys._current_frames().items():
    print(id2name[threadId])
    traceback.print_stack(f=stack)

signal.signal(signal.SIGQUIT, dumpstacks)

os.killpg(os.getpgid(0), signal.SIGQUIT)

对于我的需求,我还按名称过滤线程。

I was looking for a while for a solution to debug my threads and I found it here thanks to haridsv. I use slightly simplified version employing the traceback.print_stack():

import sys, traceback, signal
import threading
import os

def dumpstacks(signal, frame):
  id2name = dict((th.ident, th.name) for th in threading.enumerate())
  for threadId, stack in sys._current_frames().items():
    print(id2name[threadId])
    traceback.print_stack(f=stack)

signal.signal(signal.SIGQUIT, dumpstacks)

os.killpg(os.getpgid(0), signal.SIGQUIT)

For my needs I also filter threads by name.


回答 14

值得一看的是Pydb,“基于gdb命令集宽松地扩展了Python调试器的版本”。它包括信号管理器,该管理器可以在发送指定信号时负责启动调试器。

2006年夏天的Code项目研究了在名为mpdb的模块中向pydb添加远程调试功能。

It’s worth looking at Pydb, “an expanded version of the Python debugger loosely based on the gdb command set”. It includes signal managers which can take care of starting the debugger when a specified signal is sent.

A 2006 Summer of Code project looked at adding remote-debugging features to pydb in a module called mpdb.


回答 15

我一起破解了一些附加到正在运行的Python进程中的工具,并注入了一些代码来获取Python Shell。

看到这里:https : //github.com/albertz/pydbattach

I hacked together some tool which attaches into a running Python process and injects some code to get a Python shell.

See here: https://github.com/albertz/pydbattach


回答 16

可以使用出色的py-spy来完成。它是Python程序的采样分析器,因此它的工作是附加到Python进程并对其调用堆栈进行采样。因此,py-spy dump --pid $SOME_PID您需要做的就是转储$SOME_PID进程中所有线程的调用堆栈。通常,它需要升级的特权(以读取目标进程的内存)。

这是一个线程化Python应用程序的外观示例。

$ sudo py-spy dump --pid 31080
Process 31080: python3.7 -m chronologer -e production serve -u www-data -m
Python v3.7.1 (/usr/local/bin/python3.7)

Thread 0x7FEF5E410400 (active): "MainThread"
    _wait (cherrypy/process/wspbus.py:370)
    wait (cherrypy/process/wspbus.py:384)
    block (cherrypy/process/wspbus.py:321)
    start (cherrypy/daemon.py:72)
    serve (chronologer/cli.py:27)
    main (chronologer/cli.py:84)
    <module> (chronologer/__main__.py:5)
    _run_code (runpy.py:85)
    _run_module_as_main (runpy.py:193)
Thread 0x7FEF55636700 (active): "_TimeoutMonitor"
    run (cherrypy/process/plugins.py:518)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
Thread 0x7FEF54B35700 (active): "HTTPServer Thread-2"
    accept (socket.py:212)
    tick (cherrypy/wsgiserver/__init__.py:2075)
    start (cherrypy/wsgiserver/__init__.py:2021)
    _start_http_thread (cherrypy/process/servers.py:217)
    run (threading.py:865)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
...
Thread 0x7FEF2BFFF700 (idle): "CP Server Thread-10"
    wait (threading.py:296)
    get (queue.py:170)
    run (cherrypy/wsgiserver/__init__.py:1586)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)  

It can be done with excellent py-spy. It’s a sampling profiler for Python programs, so its job is to attach to a Python processes and sample their call stacks. Hence, py-spy dump --pid $SOME_PID is all you need to do to dump call stacks of all threads in the $SOME_PID process. Typically it needs escalated privileges (to read the target process’ memory).

Here’s an example of how it looks like for a threaded Python application.

$ sudo py-spy dump --pid 31080
Process 31080: python3.7 -m chronologer -e production serve -u www-data -m
Python v3.7.1 (/usr/local/bin/python3.7)

Thread 0x7FEF5E410400 (active): "MainThread"
    _wait (cherrypy/process/wspbus.py:370)
    wait (cherrypy/process/wspbus.py:384)
    block (cherrypy/process/wspbus.py:321)
    start (cherrypy/daemon.py:72)
    serve (chronologer/cli.py:27)
    main (chronologer/cli.py:84)
    <module> (chronologer/__main__.py:5)
    _run_code (runpy.py:85)
    _run_module_as_main (runpy.py:193)
Thread 0x7FEF55636700 (active): "_TimeoutMonitor"
    run (cherrypy/process/plugins.py:518)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
Thread 0x7FEF54B35700 (active): "HTTPServer Thread-2"
    accept (socket.py:212)
    tick (cherrypy/wsgiserver/__init__.py:2075)
    start (cherrypy/wsgiserver/__init__.py:2021)
    _start_http_thread (cherrypy/process/servers.py:217)
    run (threading.py:865)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)
...
Thread 0x7FEF2BFFF700 (idle): "CP Server Thread-10"
    wait (threading.py:296)
    get (queue.py:170)
    run (cherrypy/wsgiserver/__init__.py:1586)
    _bootstrap_inner (threading.py:917)
    _bootstrap (threading.py:885)  

回答 17

pyringe是一个调试器,可以与正在运行的python进程,打印堆栈跟踪,变量等进行交互,而无需任何先验设置。

尽管我过去经常使用信号处理程序解决方案,但在某些环境中重现问题仍然常常很困难。

pyringe is a debugger that can interact with running python processes, print stack traces, variables, etc. without any a priori setup.

While I’ve often used the signal handler solution in the past, it can still often be difficult to reproduce the issue in certain environments.


回答 18

没有办法挂接到正在运行的python进程中并获得合理的结果。如果进程锁定,我该怎么做就挂勾strace并试图弄清楚到底发生了什么。

不幸的是,strace经常是观察者“修复”竞态条件,因此输出在那里也无用。

There is no way to hook into a running python process and get reasonable results. What I do if processes lock up is hooking strace in and trying to figure out what exactly is happening.

Unfortunately often strace is the observer that “fixes” race conditions so that the output is useless there too.


回答 19

您可以使用PuDB(具有curses接口的Python调试器)来执行此操作。只需添加

from pudb import set_interrupt_handler; set_interrupt_handler()

您的代码,并在需要中断时使用Ctrl-C。c如果您错过了它并想再试一次,则可以继续并多次中断。

You can use PuDB, a Python debugger with a curses interface to do this. Just add

from pudb import set_interrupt_handler; set_interrupt_handler()

to your code and use Ctrl-C when you want to break. You can continue with c and break again multiple times if you miss it and want to try again.


回答 20

我在python扩展的GDB阵营中。跟随https://wiki.python.org/moin/DebuggingWithGdb,这意味着

  1. dnf install gdb python-debuginfo 要么 sudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt

同时考虑info threadsthread apply all py-bt

I am in the GDB camp with the python extensions. Follow https://wiki.python.org/moin/DebuggingWithGdb, which means

  1. dnf install gdb python-debuginfo or sudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt

Also consider info threads and thread apply all py-bt.


回答 21

如何在控制台中调试任何功能:

使用pdb.set_trace()的地方创建函数,然后创建要调试的函数。

>>> import pdb
>>> import my_function

>>> def f():
...     pdb.set_trace()
...     my_function()
... 

然后调用创建的函数:

>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb) 

调试愉快:)

How to debug any function in console:

Create function where you use pdb.set_trace(), then function you want debug.

>>> import pdb
>>> import my_function

>>> def f():
...     pdb.set_trace()
...     my_function()
... 

Then call created function:

>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb) 

Happy debugging :)


回答 22

我不知道任何类似于Java对SIGQUIT的响应,因此您可能必须将其内置到应用程序中。也许您可以在另一个线程中创建服务器,以便在响应某种消息时获得堆栈跟踪?

I don’t know of anything similar to java’s response to SIGQUIT, so you might have to build it in to your application. Maybe you could make a server in another thread that can get a stacktrace on response to a message of some kind?


回答 23

使用检查模块。

导入检查帮助(inspect.stack)模块检查中的功能堆栈帮助:

stack(context = 1)返回调用者框架上方的堆栈的记录列表。

我发现它确实很有帮助。

use the inspect module.

import inspect help(inspect.stack) Help on function stack in module inspect:

stack(context=1) Return a list of records for the stack above the caller’s frame.

I find it very helpful indeed.


回答 24

在Python 3中,第一次在调试器中使用c(ont(inue))时,pdb将自动安装信号处理程序。之后按Control-C将使您回到原来的位置。在Python 2中,这是一个单行代码,即使在相对较旧的版本中也可以使用(在2.7中进行了测试,但我检查了Python源代码回到2.4,看起来还可以):

import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))

如果您花费大量时间调试Python,pdb值得学习。该界面有点晦涩难懂,但使用过类似工具(例如gdb)的任何人都应该熟悉。

In Python 3, pdb will automatically install a signal handler the first time you use c(ont(inue)) in the debugger. Pressing Control-C afterwards will drop you right back in there. In Python 2, here’s a one-liner which should work even in relatively old versions (tested in 2.7 but I checked Python source back to 2.4 and it looked okay):

import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))

pdb is worth learning if you spend any amount of time debugging Python. The interface is a bit obtuse but should be familiar to anyone who has used similar tools, such as gdb.


回答 25

如果您需要使用uWSGI进行此操作,它内置了Python Tracebacker,只需在配置中启用它即可(每个工作人员的姓名均附有数字):

py-tracebacker=/var/run/uwsgi/pytrace

完成此操作后,只需连接到套接字即可打印回溯:

uwsgi --connect-and-read /var/run/uwsgi/pytrace1

In case you need to do this with uWSGI, it has Python Tracebacker built-in and it’s just matter of enabling it in the configuration (number is attached to the name for each worker):

py-tracebacker=/var/run/uwsgi/pytrace

Once you have done this, you can print backtrace simply by connecting to the socket:

uwsgi --connect-and-read /var/run/uwsgi/pytrace1

回答 26

在运行代码的那一点,您可以插入此小片段,以查看格式正确的打印堆栈跟踪。假定您logs在项目的根目录中有一个名为的文件夹。

# DEBUG: START DEBUG -->
import traceback

with open('logs/stack-trace.log', 'w') as file:
    traceback.print_stack(file=file)
# DEBUG: END DEBUG --!

At the point where the code is run, you can insert this small snippet to see a nicely formatted printed stack trace. It assumes that you have a folder called logs at your project’s root directory.

# DEBUG: START DEBUG -->
import traceback

with open('logs/stack-trace.log', 'w') as file:
    traceback.print_stack(file=file)
# DEBUG: END DEBUG --!

如何在Django中进行调试,好方法?[关闭]

问题:如何在Django中进行调试,好方法?[关闭]

因此,我开始学习使用Python和更高版本的Django进行编码。第一次很难查看回溯,实际上要弄清楚我做错了什么以及语法错误在哪里。现在已经过去了一段时间,而且我想我已经有了调试Django代码的例程。由于这是我编码经验的早期,所以我坐下来,想知道自己的做法是否无效,并且可以更快地完成。我通常设法找到并纠正代码中的错误,但是我想知道是否应该更快地执行该操作?

我通常只使用Django启用时提供的调试信息。当事情按我预期的那样结束时,我因语法错误而使代码流中断很多,然后查看流中该点的变量以找出其中的代码在做什么,而不是我想要的。

但这可以改善吗?是否有一些好的工具或更好的方法来调试Django代码?

So, I started learning to code in Python and later Django. The first times it was hard looking at tracebacks and actually figure out what I did wrong and where the syntax error was. Some time has passed now and some way along the way, I guess I got a routine in debugging my Django code. As this was done early in my coding experience, I sat down and wondered if how I was doing this was ineffective and could be done faster. I usually manage to find and correct the bugs in my code, but I wonder if I should be doing it faster?

I usually just use the debug info Django gives when enabled. When things do end up as I thought it would, I break the code flow a lot with a syntax error, and look at the variables at that point in the flow to figure out, where the code does something other than what I wanted.

But can this be improved? Are there some good tools or better ways to debug your Django code?


回答 0

有很多方法可以做到,但是最直接的方法就是简单地使用Python调试器。只需将以下行添加到Django视图函数中:

import pdb; pdb.set_trace()

要么

breakpoint()  #from Python3.7

如果尝试在浏览器中加载该页面,浏览器将挂起,并且提示您对实际执行的代码进行调试。

但是,还有其他选择(我不推荐使用):

* return HttpResponse({variable to inspect})

* print {variable to inspect}

* raise Exception({variable to inspect})

但是强烈建议所有类型的Python代码都使用Python调试器(pdb)。如果你已经到PDB,你想也想看看IPDB使用IPython中进行调试。

对pdb的一些更有用的扩展是

pdb ++,由 Antash建议。

pudb,由 PatDuJour建议。

Seafangs建议在Django中使用Python调试器

There are a bunch of ways to do it, but the most straightforward is to simply use the Python debugger. Just add following line in to a Django view function:

import pdb; pdb.set_trace()

or

breakpoint()  #from Python3.7

If you try to load that page in your browser, the browser will hang and you get a prompt to carry on debugging on actual executing code.

However there are other options (I am not recommending them):

* return HttpResponse({variable to inspect})

* print {variable to inspect}

* raise Exception({variable to inspect})

But the Python Debugger (pdb) is highly recommended for all types of Python code. If you are already into pdb, you’d also want to have a look at IPDB that uses ipython for debugging.

Some more useful extension to pdb are

pdb++, suggested by Antash.

pudb, suggested by PatDuJour.

Using the Python debugger in Django, suggested by Seafangs.


回答 1

我真的很喜欢Werkzeug的交互式调试器。它与Django的调试页面相似,不同之处在于您在回溯的每个级别上都获得了一个交互式shell。如果使用django-extensions,则会得到一个runserver_plus管理命令,该命令启动开发服务器,并为您提供Werkzeug的异常调试器。

当然,您只应在本地运行此命令,因为它使拥有浏览器的任何人都有权在服务器的上下文中执行任意python代码。

I really like Werkzeug‘s interactive debugger. It’s similar to Django’s debug page, except that you get an interactive shell on every level of the traceback. If you use the django-extensions, you get a runserver_plus managment command which starts the development server and gives you Werkzeug’s debugger on exceptions.

Of course, you should only run this locally, as it gives anyone with a browser the rights to execute arbitrary python code in the context of the server.


回答 2

模板标记的小工具:

@register.filter 
def pdb(element):
    import pdb; pdb.set_trace()
    return element

现在,您可以在模板内执行{{ template_var|pdb }}并输入一个pdb会话(假设您正在运行本地devel服务器),您可以在其中检查element您的心脏内容。

这是查看对象到达模板时发生了什么情况的一种很好的方法。

A little quickie for template tags:

@register.filter 
def pdb(element):
    import pdb; pdb.set_trace()
    return element

Now, inside a template you can do {{ template_var|pdb }} and enter a pdb session (given you’re running the local devel server) where you can inspect element to your heart’s content.

It’s a very nice way to see what’s happened to your object when it arrives at the template.


回答 3

有一些工具可以很好地协作,并且可以使您的调试任务更轻松。

最重要的是Django调试工具栏

然后,您需要使用Python 日志记录工具进行良好的日志记录。您可以将日志输出发送到日志文件,但是更简单的选择是将日志输出发送到firepython。要使用此功能,您需要使用带有firebug扩展名的Firefox浏览器。Firepython包含一个firebug插件,该插件将在Firebug标签中显示所有服务器端日志。

Firebug本身对于调试您开发的任何应用程序的Javascript方面也至关重要。(假设您当然有一些JS代码)。

我还喜欢使用django-viewtools通过pdb交互式地调试视图,但是我并没有那么多地使用它。

还有诸如推土机之类的更有用的工具来跟踪内存泄漏(SO的答案中也提供了其他一些很好的建议来进行内存跟踪)。

There are a few tools that cooperate well and can make your debugging task easier.

Most important is the Django debug toolbar.

Then you need good logging using the Python logging facility. You can send logging output to a log file, but an easier option is sending log output to firepython. To use this you need to use the Firefox browser with the firebug extension. Firepython includes a firebug plugin that will display any server-side logging in a Firebug tab.

Firebug itself is also critical for debugging the Javascript side of any app you develop. (Assuming you have some JS code of course).

I also liked django-viewtools for debugging views interactively using pdb, but I don’t use it that much.

There are more useful tools like dozer for tracking down memory leaks (there are also other good suggestions given in answers here on SO for memory tracking).


回答 4

我使用PyCharm(与Eclipse相同的pydev引擎)。确实可以帮助我直观地逐步查看我的代码并查看正在发生的事情。

I use PyCharm (same pydev engine as eclipse). Really helps me to visually be able to step through my code and see what is happening.


回答 5

到目前为止,几乎所有内容都已提及,因此,我仅补充一点,而不是pdb.set_trace()可以使用ipdb.set_trace()(它使用iPython),因此功能更强大(自动完成功能和其他功能)。这需要ipdb包,因此您只需要pip install ipdb

Almost everything has been mentioned so far, so I’ll only add that instead of pdb.set_trace() one can use ipdb.set_trace() which uses iPython and therefore is more powerful (autocomplete and other goodies). This requires ipdb package, so you only need to pip install ipdb


回答 6

我推django-pdbPyPI中。这是一个简单的应用程序,这意味着您不必每次想进入pdb都需要编辑源代码。

安装只是…

  1. pip install django-pdb
  2. 添加'django_pdb'到您的INSTALLED_APPS

您现在可以运行:manage.py runserver --pdb在每个视图的开始处进入pdb …

bash: manage.py runserver --pdb
Validating models...

0 errors found
Django version 1.3, using settings 'testproject.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

GET /
function "myview" in testapp/views.py:6
args: ()
kwargs: {}

> /Users/tom/github/django-pdb/testproject/testapp/views.py(7)myview()
-> a = 1
(Pdb)

并运行:manage.py test --pdb在测试失败/错误时进入pdb …

bash: manage.py test testapp --pdb
Creating test database for alias 'default'...
E
======================================================================
>>> test_error (testapp.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../django-pdb/testproject/testapp/tests.py", line 16, in test_error
    one_plus_one = four
NameError: global name 'four' is not defined
======================================================================

> /Users/tom/github/django-pdb/testproject/testapp/tests.py(16)test_error()
-> one_plus_one = four
(Pdb)

该项目托管在GitHub上,当然欢迎您贡献。

I’ve pushed django-pdb to PyPI. It’s a simple app that means you don’t need to edit your source code every time you want to break into pdb.

Installation is just…

  1. pip install django-pdb
  2. Add 'django_pdb' to your INSTALLED_APPS

You can now run: manage.py runserver --pdb to break into pdb at the start of every view…

bash: manage.py runserver --pdb
Validating models...

0 errors found
Django version 1.3, using settings 'testproject.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

GET /
function "myview" in testapp/views.py:6
args: ()
kwargs: {}

> /Users/tom/github/django-pdb/testproject/testapp/views.py(7)myview()
-> a = 1
(Pdb)

And run: manage.py test --pdb to break into pdb on test failures/errors…

bash: manage.py test testapp --pdb
Creating test database for alias 'default'...
E
======================================================================
>>> test_error (testapp.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../django-pdb/testproject/testapp/tests.py", line 16, in test_error
    one_plus_one = four
NameError: global name 'four' is not defined
======================================================================

> /Users/tom/github/django-pdb/testproject/testapp/tests.py(16)test_error()
-> one_plus_one = four
(Pdb)

The project’s hosted on GitHub, contributions are welcome of course.


回答 7

调试python的最简单方法是使用PTVS(适用于Visual Studio的Python工具),尤其是对于使用Visual Studio的程序员而言。步骤很简单:

  1. http://pytools.codeplex.com/下载并安装
  2. 设置断点并按F5。
  3. 您的断点被击中,您可以像调试C#/ C ++程序一样轻松地查看/更改变量。
  4. 就这样 :)

如果要使用PTVS调试Django,则需要执行以下操作:

  1. 在“项目设置”的“常规”选项卡中,将“启动文件”设置为“ manage.py”,这是Django程序的入口点。
  2. 在项目设置-调试选项卡中,将“脚本参数”设置为“ runserver –noreload”。关键是这里的“ –noreload”。如果您不设置它,您的断点将不会被击中。
  3. 好好享受。

The easiest way to debug python – especially for programmers that are used to Visual Studio – is using PTVS (Python Tools for Visual Studio). The steps are simple:

  1. Download and install it from http://pytools.codeplex.com/
  2. Set breakpoints and press F5.
  3. Your breakpoint is hit, you can view/change the variables as easy as debugging C#/C++ programs.
  4. That’s all :)

If you want to debug Django using PTVS, you need to do the following:

  1. In Project settings – General tab, set “Startup File” to “manage.py”, the entry point of the Django program.
  2. In Project settings – Debug tab, set “Script Arguments” to “runserver –noreload”. The key point is the “–noreload” here. If you don’t set it, your breakpoints won’t be hit.
  3. Enjoy it.

回答 8

我将pyDev与Eclipse 搭配使用非常好,设置断点,进入代码,查看任何对象和变量的值,然后尝试。

I use pyDev with Eclipse really good, set break points, step into code, view values on any objects and variables, try it.


回答 9

我使用PyCharm并一直坚持下去。它花了我一点钱,但我不得不说,我摆脱它的好处是无价的。我尝试从控制台进行调试,但我确实给予了很多荣誉,他们可以做到这一点,但是对我而言,能够直观地调试我的应用程序真是太好了。

我不得不说,PyCharm确实占用了大量内存。但是话又说回来,生活中没有什么是免费的。他们只是带有最新版本3。它在Django,Flask和Google AppEngine中也能很好地发挥作用。因此,总而言之,对于任何开发人员来说,这都是一个非常方便的工具。

如果您尚未使用它,建议您试用30天,以了解PyCharm的功能。我确定还有其他工具,例如Aptana。但是我想我也喜欢PyCharm的外观。我在那里调试我的应用程序感到非常自在。

I use PyCharm and stand by it all the way. It cost me a little but I have to say the advantage that I get out of it is priceless. I tried debugging from console and I do give people a lot of credit who can do that, but for me being able to visually debug my application(s) is great.

I have to say though, PyCharm does take a lot of memory. But then again, nothing good is free in life. They just came with their latest version 3. It also plays very well with Django, Flask and Google AppEngine. So, all in all, I’d say it’s a great handy tool to have for any developer.

If you are not using it yet, I’d recommend to get the trial version for 30 days to take a look at the power of PyCharm. I’m sure there are other tools also available, such as Aptana. But I guess I just also like the way PyCharm looks. I feel very comfortable debugging my apps there.


回答 10

有时候,当我想探索一种特定的方法并且召唤pdb太麻烦时,我会添加:

import IPython; IPython.embed()

IPython.embed() 启动一个IPython shell,该shell可以从调用它的地方访问局部变量。

Sometimes when I wan to explore around in a particular method and summoning pdb is just too cumbersome, I would add:

import IPython; IPython.embed()

IPython.embed() starts an IPython shell which have access to the local variables from the point where you call it.


回答 11

从我的角度来看,我们可以将常见的代码调试任务分解为三种不同的使用模式:

  1. 出现了一个异常runserver_plus’Werkzeug调试器进行了救援。在所有跟踪级别上运行自定义代码的能力是一个杀手er。而且,如果您完全陷入困境,则可以创建一个Gist,只需单击即可共享。
  2. 页面被渲染,但是结果是错误的:再次,Werkzeug摇摆不定。要在代码中创建断点,只需键入assert False要停止的位置。
  3. 代码工作不正确,但快速浏览无济于事。最有可能是算法问题。叹。然后,我通常火了调试器控制台PuDBimport pudb; pudb.set_trace()。与[i] pdb相比,主要优点是PuDB(看起来像80年代)使设置自定义监视表达式变得轻而易举。使用GUI调试一堆嵌套循环要简单得多。

啊,是的,模板的问题。最常见的问题(对我和我的同事而言)是错误的上下文:您没有变量,或者您的变量没有某些属性。如果您使用的是调试工具栏,则只需在“模板”部分检查上下文,或者,如果不够用的话,可以在填充完上下文后在视图代码中设置一个中断。

这样吧。

From my perspective, we could break down common code debugging tasks into three distinct usage patterns:

  1. Something has raised an exception: runserver_plus‘ Werkzeug debugger to the rescue. The ability to run custom code at all the trace levels is a killer. And if you’re completely stuck, you can create a Gist to share with just a click.
  2. Page is rendered, but the result is wrong: again, Werkzeug rocks. To make a breakpoint in code, just type assert False in the place you want to stop at.
  3. Code works wrong, but the quick look doesn’t help. Most probably, an algorithmic problem. Sigh. Then I usually fire up a console debugger PuDB: import pudb; pudb.set_trace(). The main advantage over [i]pdb is that PuDB (while looking as you’re in 80’s) makes setting custom watch expressions a breeze. And debugging a bunch of nested loops is much simpler with a GUI.

Ah, yes, the templates’ woes. The most common (to me and my colleagues) problem is a wrong context: either you don’t have a variable, or your variable doesn’t have some attribute. If you’re using debug toolbar, just inspect the context at the “Templates” section, or, if it’s not sufficient, set a break in your views’ code just after your context is filled up.

So it goes.


回答 12

我强烈建议使用epdb(扩展的Python调试器)。

https://bitbucket.org/dugan/epdb

我喜欢epdb用于调试Django或其他Python网络服务器的一件事是epdb.serve()命令。这将设置一个跟踪并将其提供给您可以连接到的本地端口。典型用例:

我认为我要逐步进行。我将在要设置跟踪的位置插入以下内容。

import epdb; epdb.serve()

执行此代码后,我将打开一个Python解释器并连接到服务实例。我可以分析所有值,并使用标准的pdb命令(如n,s等)逐步执行代码。

In [2]: import epdb; epdb.connect()
(Epdb) request
<WSGIRequest
path:/foo,
GET:<QueryDict: {}>, 
POST:<QuestDict: {}>,
...
>
(Epdb) request.session.session_key
'i31kq7lljj3up5v7hbw9cff0rga2vlq5'
(Epdb) list
 85         raise some_error.CustomError()
 86 
 87     # Example login view
 88     def login(request, username, password):
 89         import epdb; epdb.serve()
 90  ->     return my_login_method(username, password)
 91
 92     # Example view to show session key
 93     def get_session_key(request):
 94         return request.session.session_key
 95

您可以随时了解有关键入epdb help的更多信息。

如果要同时服务或连接到多个epdb实例,则可以指定要监听的端口(默认为8080)。即

import epdb; epdb.serve(4242)

>> import epdb; epdb.connect(host='192.168.3.2', port=4242)

如果未指定,则主机默认为“ localhost”。我在这里进行了演示,以演示如何使用它来调试本地实例以外的其他东西,例如本地LAN上的开发服务器。显然,如果执行此操作,请注意设置的跟踪永远不会进入生产服务器!

快速说明一下,您仍然可以使用epdb(import epdb; epdb.set_trace())做与已接受答案相同的操作,但是由于我发现它非常有用,因此我想强调它的服务功能。

I highly recommend epdb (Extended Python Debugger).

https://bitbucket.org/dugan/epdb

One thing I love about epdb for debugging Django or other Python webservers is the epdb.serve() command. This sets a trace and serves this on a local port that you can connect to. Typical use case:

I have a view that I want to go through step-by-step. I’ll insert the following at the point I want to set the trace.

import epdb; epdb.serve()

Once this code gets executed, I open a Python interpreter and connect to the serving instance. I can analyze all the values and step through the code using the standard pdb commands like n, s, etc.

In [2]: import epdb; epdb.connect()
(Epdb) request
<WSGIRequest
path:/foo,
GET:<QueryDict: {}>, 
POST:<QuestDict: {}>,
...
>
(Epdb) request.session.session_key
'i31kq7lljj3up5v7hbw9cff0rga2vlq5'
(Epdb) list
 85         raise some_error.CustomError()
 86 
 87     # Example login view
 88     def login(request, username, password):
 89         import epdb; epdb.serve()
 90  ->     return my_login_method(username, password)
 91
 92     # Example view to show session key
 93     def get_session_key(request):
 94         return request.session.session_key
 95

And tons more that you can learn about typing epdb help at any time.

If you want to serve or connect to multiple epdb instances at the same time, you can specify the port to listen on (default is 8080). I.e.

import epdb; epdb.serve(4242)

>> import epdb; epdb.connect(host='192.168.3.2', port=4242)

host defaults to ‘localhost’ if not specified. I threw it in here to demonstrate how you can use this to debug something other than a local instance, like a development server on your local LAN. Obviously, if you do this be careful that the set trace never makes it onto your production server!

As a quick note, you can still do the same thing as the accepted answer with epdb (import epdb; epdb.set_trace()) but I wanted to highlight the serve functionality since I’ve found it so useful.


回答 13

我刚刚找到wdb(http://www.rkblog.rk.edu.pl/w/p/debugging-python-code-browser-wdb-debugger/?goback=%2Egde_25827_member_255996401)。它有一个非常漂亮的用户界面/ GUI,带有所有的提示。作者说了有关wdb的内容-

“有些像PyCharm这样的IDE都有自己的调试器。它们提供相似或相同的功能集…但是,要使用它们,您必须使用那些特定的IDE(其中有些是非免费的,或者可能不适用于所有平台)。根据您的需求选择合适的工具。”

以为我会继续下去。

也是有关python调试器的非常有用的文章: https : //zapier.com/engineering/debugging-python-boss/

最后,如果您想在Django中看到调用堆栈的漂亮图形输出,请结帐:https : //github.com/joerick/pyinstrument。只需将pyinstrument.middleware.ProfilerMiddleware添加到MIDDLEWARE_CLASSES,然后在请求URL的末尾添加?profile即可激活分析器。

也可以从命令行或作为模块导入来运行pyinstrument。

I just found wdb (http://www.rkblog.rk.edu.pl/w/p/debugging-python-code-browser-wdb-debugger/?goback=%2Egde_25827_member_255996401). It has a pretty nice user interface / GUI with all the bells and whistles. Author says this about wdb –

“There are IDEs like PyCharm that have their own debuggers. They offer similar or equal set of features … However to use them you have to use those specific IDEs (and some of then are non-free or may not be available for all platforms). Pick the right tool for your needs.”

Thought i’d just pass it on.

Also a very helpful article about python debuggers: https://zapier.com/engineering/debugging-python-boss/

Finally, if you’d like to see a nice graphical printout of your call stack in Django, checkout: https://github.com/joerick/pyinstrument. Just add pyinstrument.middleware.ProfilerMiddleware to MIDDLEWARE_CLASSES, then add ?profile to the end of the request URL to activate the profiler.

Can also run pyinstrument from command line or by importing as a module.


回答 14

在Python代码的相应行中添加import pdb; pdb.set_trace()breakpoint() (形成python3.7)并执行它。执行将以交互式外壳程序停止。在外壳程序中,您可以执行Python代码(即打印变量)或使用以下命令:

  • c 继续执行
  • n 跳到同一功能的下一行
  • s 转到此函数或被调用函数的下一行
  • q 退出调试器/执行

另请参阅:https : //poweruser.blog/setting-a-breakpoint-in-python-438e23fe6b28

Add import pdb; pdb.set_trace() or breakpoint() (form python3.7) at the corresponding line in the Python code and execute it. The execution will stop with an interactive shell. In the shell you can execute Python code (i.e. print variables) or use commands such as:

  • c continue execution
  • n step to the next line within the same function
  • s step to the next line in this function or a called function
  • q quit the debugger/execution

Also see: https://poweruser.blog/setting-a-breakpoint-in-python-438e23fe6b28


回答 15

调试Django代码的最佳选择之一是通过wdb:https : //github.com/Kozea/wdb

wdb可与python 2(2.6、2.7),python 3(3.2、3.3、3.4、3.5)和pypy一起使用。甚至更好的是,可以用在python 3上运行的wdb服务器来调试python 2程序,反之亦然,或者用在第三台计算机的网页内的另一台计算机上运行的调试服务器来调试在计算机上运行的程序!更好的是,现在可以使用Web界面中的代码注入来暂停当前正在运行的python进程/线程。(这需要启用gdb和ptrace)换句话说,它是pdb的非常增强的版本,直接在浏览器中具有不错的功能。

安装并运行服务器,并在代码中添加:

import wdb
wdb.set_trace()

根据作者的说法,主要区别在于pdb

对于那些不了解项目的人,wdb是像pdb这样的python调试器,但是具有光滑的Web前端和许多其他功能,例如:

  • 源语法突出显示
  • 视觉断点
  • 使用jedi的交互式代码完成
  • 持久断点
  • 使用鼠标多线程/多处理支持进行深层对象检查
  • 远程调试
  • 观看表情
  • 在调试器代码版中
  • 流行的Web服务器集成可打破错误
  • 例如,在跟踪过程中发生异常中断(不是事后检验),与werkzeug调试器相反
  • 通过代码注入(在受支持的系统上)破坏当前正在运行的程序

它具有基于浏览器的出色用户界面。使用愉快!:)

One of your best option to debug Django code is via wdb: https://github.com/Kozea/wdb

wdb works with python 2 (2.6, 2.7), python 3 (3.2, 3.3, 3.4, 3.5) and pypy. Even better, it is possible to debug a python 2 program with a wdb server running on python 3 and vice-versa or debug a program running on a computer with a debugging server running on another computer inside a web page on a third computer! Even betterer, it is now possible to pause a currently running python process/thread using code injection from the web interface. (This requires gdb and ptrace enabled) In other words it’s a very enhanced version of pdb directly in your browser with nice features.

Install and run the server, and in your code add:

import wdb
wdb.set_trace()

According to the author, main differences with respect to pdb are:

For those who don’t know the project, wdb is a python debugger like pdb, but with a slick web front-end and a lot of additional features, such as:

  • Source syntax highlighting
  • Visual breakpoints
  • Interactive code completion using jedi
  • Persistent breakpoints
  • Deep objects inspection using mouse Multithreading / Multiprocessing support
  • Remote debugging
  • Watch expressions
  • In debugger code edition
  • Popular web servers integration to break on error
  • In exception breaking during trace (not post-mortem) in contrary to the werkzeug debugger for instance
  • Breaking in currently running programs through code injection (on supported systems)

It has a great browser-based user interface. A joy to use! :)


回答 16

我使用PyCharm和其他调试工具。也有一篇不错的文章集,关于为新手轻松设置这些东西。您可以从这里开始。它介绍了有关Django项目的PDB和GUI调试的一般信息。希望有人能从中受益。

I use PyCharm and different debug tools. Also have a nice articles set about easy set up those things for novices. You may start here. It tells about PDB and GUI debugging in general with Django projects. Hope someone would benefit from them.


回答 17

如果使用Aptana进行Django开发,请观看以下内容:http : //www.youtube.com/watch?v= qQh-UQFltJQ

如果没有,请考虑使用它。

If using Aptana for django development, watch this: http://www.youtube.com/watch?v=qQh-UQFltJQ

If not, consider using it.


回答 18

提到大多数选项。为了打印模板上下文,我为此创建了一个简单的库。参见https://github.com/edoburu/django-debugtools

您可以使用它来打印模板上下文,而无需任何{% load %}构造:

{% print var %}   prints variable
{% print %}       prints all

它使用定制的pprint格式在<pre>标签中显示变量。

Most options are alredy mentioned. To print template context, I’ve created a simple library for that. See https://github.com/edoburu/django-debugtools

You can use it to print template context without any {% load %} construct:

{% print var %}   prints variable
{% print %}       prints all

It uses a customized pprint format to display the variables in a <pre> tag.


回答 19

我发现Visual Studio Code非常适合调试Django应用。标准的python launch.json参数python manage.py与附加的调试器一起运行,因此您可以根据需要设置断点并逐步执行代码。

I find Visual Studio Code is awesome for debugging Django apps. The standard python launch.json parameters run python manage.py with the debugger attached, so you can set breakpoints and step through your code as you like.


回答 20

对于那些可能意外将pdb添加到实时提交中的人,我可以建议#Koobz答案的扩展名:

@register.filter 
def pdb(element):
    from django.conf import settings
    if settings.DEBUG:    
        import pdb
        pdb.set_trace()
    return element

For those that can accidentally add pdb into live commits, I can suggest this extension of #Koobz answer:

@register.filter 
def pdb(element):
    from django.conf import settings
    if settings.DEBUG:    
        import pdb
        pdb.set_trace()
    return element

回答 21

根据我自己的经验,有两种方法:

  1. 使用ipdb,它是像pdb一样的增强调试器。

    import ipdb;ipdb.set_trace()breakpoint() (来自python3.7)

  2. 使用django shell,只需使用下面的命令。在开发新视图时,这非常有帮助。

    python manage.py shell

From my own experience , there are two way:

  1. use ipdb,which is a enhanced debugger likes pdb.

    import ipdb;ipdb.set_trace() or breakpoint() (from python3.7)

  2. use django shell ,just use the command below. This is very helpfull when you are developing a new view.

    python manage.py shell


回答 22

我强烈建议使用PDB。

import pdb
pdb.set_trace()

您可以检查所有变量值,进入函数等等。 https://docs.python.org/2/library/pdb.html

用于检查对数据库的各种请求,响应和命中率。我正在使用django-debug-toolbar https://github.com/django-debug-toolbar/django-debug-toolbar

i highly suggest to use PDB.

import pdb
pdb.set_trace()

You can inspect all the variables values, step in to the function and much more. https://docs.python.org/2/library/pdb.html

for checking out the all kind of request,response and hits to database.i am using django-debug-toolbar https://github.com/django-debug-toolbar/django-debug-toolbar


回答 23

如此处其他文章所述-在代码中设置断点并遍历代码以查看其行为是否符合您的预期,这是学习Django之类的好方法,直到您对所有行为的方式有了很好的了解-以及代码的含义是在做。

为此,我建议使用WingIde。就像其他提到的IDE一样,它们易于使用,易于布局,易于设置断点,可以评估/修改堆栈等。非常适合可视化代码在逐步执行时的工作。我是它的忠实粉丝。

我也使用PyCharm-它具有出色的静态代码分析功能,有时可以帮助您在发现问题之前就发现问题。

如前所述,django-debug-toolbar是必不可少的-https: //github.com/django-debug-toolbar/django-debug-toolbar

尽管不是明确的调试或分析工具,但我最喜欢的工具之一是可从Django Snippets获得的SQL Printing Middleware网址https://djangosnippets.org/snippets/290/

这将显示您的视图已生成的SQL查询。这将使您对ORM的工作以及查询是否有效还是需要重新编写代码(或添加缓存)有一个很好的了解。

我发现它在开发和调试应用程序时密切关注查询性能非常有用。

另一个提示-我稍作修改,只为显示摘要而不显示SQL语句…。因此,在开发和测试时我总是使用它。我还补充说,如果len(connection.queries)大于预定义的阈值,它将显示一个额外的警告。

然后,如果发现不良情况(从性能或查询数量的角度来看),我将重新打开SQL语句的完整显示以查看发生了什么。当您与多个开发人员一起从事大型Django项目时,非常方便。

As mentioned in other posts here – setting breakpoints in your code and walking thru the code to see if it behaves as you expected is a great way to learn something like Django until you have a good sense of how it all behaves – and what your code is doing.

To do this I would recommend using WingIde. Just like other mentioned IDEs nice and easy to use, nice layout and also easy to set breakpoints evaluate / modify the stack etc. Perfect for visualizing what your code is doing as you step through it. I’m a big fan of it.

Also I use PyCharm – it has excellent static code analysis and can help sometimes spot problems before you realize they are there.

As mentioned already django-debug-toolbar is essential – https://github.com/django-debug-toolbar/django-debug-toolbar

And while not explicitly a debug or analysis tool – one of my favorites is SQL Printing Middleware available from Django Snippets at https://djangosnippets.org/snippets/290/

This will display the SQL queries that your view has generated. This will give you a good sense of what the ORM is doing and if your queries are efficient or you need to rework your code (or add caching).

I find it invaluable for keeping an eye on query performance while developing and debugging my application.

Just one other tip – I modified it slightly for my own use to only show the summary and not the SQL statement…. So I always use it while developing and testing. I also added that if the len(connection.queries) is greater than a pre-defined threshold it displays an extra warning.

Then if I spot something bad (from a performance or number of queries perspective) is happening I turn back on the full display of the SQL statements to see exactly what is going on. Very handy when you are working on a large Django project with multiple developers.


回答 24

使用pdbipdb。两者之间的区别在于ipdb支持自动完成。

对于pdb

import pdb
pdb.set_trace()

对于ipdb

import ipdb
ipdb.set_trace()

执行换行n击键,继续c击键。通过使用检查更多选项help(pdb)

use pdb or ipdb. Diffrence between these two is ipdb supports auto complete.

for pdb

import pdb
pdb.set_trace()

for ipdb

import ipdb
ipdb.set_trace()

For executing new line hit n key, for continue hit c key. check more options by using help(pdb)


回答 25

另一个建议。

您可以一起使用鼻子测试pdb,而不是pdb.set_trace()手动插入视图。这样做的好处是,您可以在首次启动时观察错误情况,可能会使用第三方代码。

今天对我来说是个错误。

TypeError at /db/hcm91dmo/catalog/records/

render_option() argument after * must be a sequence, not int

....


Error during template rendering

In template /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/crispy_forms/templates/bootstrap3/field.html, error at line 28
render_option() argument after * must be a sequence, not int
18  
19          {% if field|is_checkboxselectmultiple %}
20              {% include 'bootstrap3/layout/checkboxselectmultiple.html' %}
21          {% endif %}
22  
23          {% if field|is_radioselect %}
24              {% include 'bootstrap3/layout/radioselect.html' %}
25          {% endif %}
26  
27          {% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
28  

      {% if field|is_checkbox and form_show_labels %}

现在,我知道这意味着我烦恼了表单的构造函数,甚至我对哪个字段是个问题也有了很好的认识。但是,我可以使用pdb来查看模板中抱怨的脆性表单吗?

我可以。在鼻子测试中使用–pdb选项:

tests$ nosetests test_urls_catalog.py --pdb

一旦我遇到任何异常(包括正常处理的异常),pdb就会在发生异常的地方停止,我可以四处看看。

  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/forms.py", line 537, in __str__
    return self.as_widget()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/forms.py", line 593, in as_widget
    return force_text(widget.render(name, self.value(), attrs=attrs))
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py", line 513, in render
    options = self.render_options(choices, [value])
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py", line 543, in render_options
    output.append(self.render_option(selected_choices, *option))
TypeError: render_option() argument after * must be a sequence, not int
INFO lib.capture_middleware log write_to_index(http://localhost:8082/db/hcm91dmo/catalog/records.html)
INFO lib.capture_middleware log write_to_index:end
> /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py(543)render_options()
-> output.append(self.render_option(selected_choices, *option))
(Pdb) import pprint
(Pdb) pprint.PrettyPrinter(indent=4).pprint(self)
<django.forms.widgets.Select object at 0x115fe7d10>
(Pdb) pprint.PrettyPrinter(indent=4).pprint(vars(self))
{   'attrs': {   'class': 'select form-control'},
    'choices': [[('_', 'any type'), (7, (7, 'type 7', 'RECTYPE_TABLE'))]],
    'is_required': False}
(Pdb)         

现在,很明显,我对crispyfield构造函数的选择参数是因为它是列表中的列表,而不是元组的列表/元组。

 'choices': [[('_', 'any type'), (7, (7, 'type 7', 'RECTYPE_TABLE'))]]

整洁的是,此pdb发生在crispy的代码中,而不是我的,并且我不需要手动插入它。

An additional suggestion.

You can leverage nosetests and pdb together, rather injecting pdb.set_trace() in your views manually. The advantage is that you can observe error conditions when they first start, potentially in 3rd party code.

Here’s an error for me today.

TypeError at /db/hcm91dmo/catalog/records/

render_option() argument after * must be a sequence, not int

....


Error during template rendering

In template /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/crispy_forms/templates/bootstrap3/field.html, error at line 28
render_option() argument after * must be a sequence, not int
18  
19          {% if field|is_checkboxselectmultiple %}
20              {% include 'bootstrap3/layout/checkboxselectmultiple.html' %}
21          {% endif %}
22  
23          {% if field|is_radioselect %}
24              {% include 'bootstrap3/layout/radioselect.html' %}
25          {% endif %}
26  
27          {% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
28  

      {% if field|is_checkbox and form_show_labels %}

Now, I know this means that I goofed the constructor for the form, and I even have good idea of which field is a problem. But, can I use pdb to see what crispy forms is complaining about, within a template?

Yes, I can. Using the –pdb option on nosetests:

tests$ nosetests test_urls_catalog.py --pdb

As soon as I hit any exception (including ones handled gracefully), pdb stops where it happens and I can look around.

  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/forms.py", line 537, in __str__
    return self.as_widget()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/forms.py", line 593, in as_widget
    return force_text(widget.render(name, self.value(), attrs=attrs))
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py", line 513, in render
    options = self.render_options(choices, [value])
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py", line 543, in render_options
    output.append(self.render_option(selected_choices, *option))
TypeError: render_option() argument after * must be a sequence, not int
INFO lib.capture_middleware log write_to_index(http://localhost:8082/db/hcm91dmo/catalog/records.html)
INFO lib.capture_middleware log write_to_index:end
> /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py(543)render_options()
-> output.append(self.render_option(selected_choices, *option))
(Pdb) import pprint
(Pdb) pprint.PrettyPrinter(indent=4).pprint(self)
<django.forms.widgets.Select object at 0x115fe7d10>
(Pdb) pprint.PrettyPrinter(indent=4).pprint(vars(self))
{   'attrs': {   'class': 'select form-control'},
    'choices': [[('_', 'any type'), (7, (7, 'type 7', 'RECTYPE_TABLE'))]],
    'is_required': False}
(Pdb)         

Now, it’s clear that my choices argument to the crispy field constructor was as it was a list within a list, rather than a list/tuple of tuples.

 'choices': [[('_', 'any type'), (7, (7, 'type 7', 'RECTYPE_TABLE'))]]

The neat thing is that this pdb is taking place within crispy’s code, not mine and I didn’t need to insert it manually.


回答 26

在开发过程中,快速添加

assert False, value

可以帮助诊断视图中或其他任何地方的问题,而无需使用调试器。

During development, adding a quick

assert False, value

can help diagnose problems in views or anywhere else, without the need to use a debugger.


是否有内置功能可以打印对象的所有当前属性和值?

问题:是否有内置功能可以打印对象的所有当前属性和值?

所以我在这里寻找的是类似PHP的print_r函数。

这样一来,我可以通过查看问题对象的状态来调试脚本。

So what I’m looking for here is something like PHP’s print_r function.

This is so I can debug my scripts by seeing what’s the state of the object in question.


回答 0

您实际上是将两种不同的东西混合在一起。

使用dir()vars()inspect模块来得到你所感兴趣的是(我用__builtins__作为一个例子,你可以使用任何对象,而不是)。

>>> l = dir(__builtins__)
>>> d = __builtins__.__dict__

随心所欲地打印该词典:

>>> print l
['ArithmeticError', 'AssertionError', 'AttributeError',...

要么

>>> from pprint import pprint
>>> pprint(l)
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'DeprecationWarning',
...

>>> pprint(d, indent=2)
{ 'ArithmeticError': <type 'exceptions.ArithmeticError'>,
  'AssertionError': <type 'exceptions.AssertionError'>,
  'AttributeError': <type 'exceptions.AttributeError'>,
...
  '_': [ 'ArithmeticError',
         'AssertionError',
         'AttributeError',
         'BaseException',
         'DeprecationWarning',
...

交互式调试器中还可以作为命令提供漂亮的打印:

(Pdb) pp vars()
{'__builtins__': {'ArithmeticError': <type 'exceptions.ArithmeticError'>,
                  'AssertionError': <type 'exceptions.AssertionError'>,
                  'AttributeError': <type 'exceptions.AttributeError'>,
                  'BaseException': <type 'exceptions.BaseException'>,
                  'BufferError': <type 'exceptions.BufferError'>,
                  ...
                  'zip': <built-in function zip>},
 '__file__': 'pass.py',
 '__name__': '__main__'}

You are really mixing together two different things.

Use dir(), vars() or the inspect module to get what you are interested in (I use __builtins__ as an example; you can use any object instead).

>>> l = dir(__builtins__)
>>> d = __builtins__.__dict__

Print that dictionary however fancy you like:

>>> print l
['ArithmeticError', 'AssertionError', 'AttributeError',...

or

>>> from pprint import pprint
>>> pprint(l)
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'DeprecationWarning',
...

>>> pprint(d, indent=2)
{ 'ArithmeticError': <type 'exceptions.ArithmeticError'>,
  'AssertionError': <type 'exceptions.AssertionError'>,
  'AttributeError': <type 'exceptions.AttributeError'>,
...
  '_': [ 'ArithmeticError',
         'AssertionError',
         'AttributeError',
         'BaseException',
         'DeprecationWarning',
...

Pretty printing is also available in the interactive debugger as a command:

(Pdb) pp vars()
{'__builtins__': {'ArithmeticError': <type 'exceptions.ArithmeticError'>,
                  'AssertionError': <type 'exceptions.AssertionError'>,
                  'AttributeError': <type 'exceptions.AttributeError'>,
                  'BaseException': <type 'exceptions.BaseException'>,
                  'BufferError': <type 'exceptions.BufferError'>,
                  ...
                  'zip': <built-in function zip>},
 '__file__': 'pass.py',
 '__name__': '__main__'}

回答 1

您要vars()pprint()

from pprint import pprint
pprint(vars(your_object))

You want vars() mixed with pprint():

from pprint import pprint
pprint(vars(your_object))

回答 2

def dump(obj):
  for attr in dir(obj):
    print("obj.%s = %r" % (attr, getattr(obj, attr)))

有很多第三方函数可以根据其作者的喜好添加诸如异常处理,国家/特殊字符打印,递归到嵌套对象等功能。但他们基本上都归结为这一点。

def dump(obj):
  for attr in dir(obj):
    print("obj.%s = %r" % (attr, getattr(obj, attr)))

There are many 3rd-party functions out there that add things like exception handling, national/special character printing, recursing into nested objects etc. according to their authors’ preferences. But they all basically boil down to this.


回答 3

已经提到了dir,但这只会为您提供属性的名称。如果还需要它们的值,请尝试__dict__。

class O:
   def __init__ (self):
      self.value = 3

o = O()

这是输出:

>>> o.__dict__

{'value': 3}

dir has been mentioned, but that’ll only give you the attributes’ names. If you want their values as well try __dict__.

class O:
   def __init__ (self):
      self.value = 3

o = O()

Here is the output:

>>> o.__dict__

{'value': 3}

回答 4

您可以使用“ dir()”函数执行此操作。

>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__', '__stdo
t__', '_current_frames', '_getframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder
, 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'exc_clear', 'exc_info'
 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'getcheckinterval', 'getdefault
ncoding', 'getfilesystemencoding', 'getrecursionlimit', 'getrefcount', 'getwindowsversion', 'he
version', 'maxint', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_
ache', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit
, 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoption
', 'winver']
>>>

另一个有用的功能是帮助。

>>> help(sys)
Help on built-in module sys:

NAME
    sys

FILE
    (built-in)

MODULE DOCS
    http://www.python.org/doc/current/lib/module-sys.html

DESCRIPTION
    This module provides access to some objects used or maintained by the
    interpreter and to functions that interact strongly with the interpreter.

    Dynamic objects:

    argv -- command line arguments; argv[0] is the script pathname if known

You can use the “dir()” function to do this.

>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__', '__stdo
t__', '_current_frames', '_getframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder
, 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'exc_clear', 'exc_info'
 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'getcheckinterval', 'getdefault
ncoding', 'getfilesystemencoding', 'getrecursionlimit', 'getrefcount', 'getwindowsversion', 'he
version', 'maxint', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_
ache', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit
, 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoption
', 'winver']
>>>

Another useful feature is help.

>>> help(sys)
Help on built-in module sys:

NAME
    sys

FILE
    (built-in)

MODULE DOCS
    http://www.python.org/doc/current/lib/module-sys.html

DESCRIPTION
    This module provides access to some objects used or maintained by the
    interpreter and to functions that interact strongly with the interpreter.

    Dynamic objects:

    argv -- command line arguments; argv[0] is the script pathname if known

回答 5

要打印对象的当前状态,您可以:

>>> obj # in an interpreter

要么

print repr(obj) # in a script

要么

print obj

为您的类定义__str____repr__方法。从Python文档中

__repr__(self)repr()内置函数和字符串转换(反引号)调用以计算对象的“正式”字符串表示形式。如果可能的话,这应该看起来像一个有效的Python表达式,可以用来重新创建具有相同值的对象(在适当的环境下)。如果无法做到这一点,则应返回“ <…一些有用的说明…>”形式的字符串。返回值必须是一个字符串对象。如果一个类定义了repr()而不是__str__(),那么__repr__()当需要该类实例的“非正式”字符串表示形式时,也可以使用该类。这通常用于调试,因此重要的是,表示形式必须信息丰富且明确。

__str__(self)str()内置函数和print语句调用,以计算对象的“非正式”字符串表示形式。区别__repr__()在于它不必是有效的Python表达式:相反,可以使用更方便或更简洁的表示形式。返回值必须是一个字符串对象。

To print the current state of the object you might:

>>> obj # in an interpreter

or

print repr(obj) # in a script

or

print obj

For your classes define __str__ or __repr__ methods. From the Python documentation:

__repr__(self) Called by the repr() built-in function and by string conversions (reverse quotes) to compute the “official” string representation of an object. If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment). If this is not possible, a string of the form “<…some useful description…>” should be returned. The return value must be a string object. If a class defines repr() but not __str__(), then __repr__() is also used when an “informal” string representation of instances of that class is required. This is typically used for debugging, so it is important that the representation is information-rich and unambiguous.

__str__(self) Called by the str() built-in function and by the print statement to compute the “informal” string representation of an object. This differs from __repr__() in that it does not have to be a valid Python expression: a more convenient or concise representation may be used instead. The return value must be a string object.


回答 6

可能值得一看-

是否有与Perl的Data :: Dumper等效的Python?

我的建议是

https://gist.github.com/1071857

请注意,perl有一个称为Data :: Dumper的模块,该模块将对象数据转换回perl源代码(注意:它不会将代码转换回源代码,并且几乎始终不希望输出中的对象方法函数)。可以将其用于持久性,但通用目的是用于调试。

标准python pprint有很多无法实现的功能,特别是当它看到一个对象的实例并为您提供该对象的内部十六进制指针时,它只会停止下降(错误,该指针不是很多使用方式)。简而言之,python就是关于这个伟大的面向对象范例的全部,但是您开箱即用的工具是为处理对象以外的东西而设计的。

perl Data :: Dumper允许您控制要深入的深度,还可以检测圆形链接结构(这很重要)。从根本上讲,此过程在perl中更容易实现,因为对象没有祝福以外的任何魔力(普遍定义良好的过程)。

Might be worth checking out —

Is there a Python equivalent to Perl’s Data::Dumper?

My recommendation is this —

https://gist.github.com/1071857

Note that perl has a module called Data::Dumper which translates object data back to perl source code (NB: it does NOT translate code back to source, and almost always you don’t want to the object method functions in the output). This can be used for persistence, but the common purpose is for debugging.

There are a number of things standard python pprint fails to achieve, in particular it just stops descending when it sees an instance of an object and gives you the internal hex pointer of the object (errr, that pointer is not a whole lot of use by the way). So in a nutshell, python is all about this great object oriented paradigm, but the tools you get out of the box are designed for working with something other than objects.

The perl Data::Dumper allows you to control how deep you want to go, and also detects circular linked structures (that’s really important). This process is fundamentally easier to achieve in perl because objects have no particular magic beyond their blessing (a universally well defined process).


回答 7

我建议使用help(your_object)

help(dir)

 If called without an argument, return the names in the current scope.
 Else, return an alphabetized list of names comprising (some of) the attributes
 of the given object, and of attributes reachable from it.
 If the object supplies a method named __dir__, it will be used; otherwise
 the default dir() logic is used and returns:
 for a module object: the module's attributes.
 for a class object:  its attributes, and recursively the attributes
 of its bases.
 for any other object: its attributes, its class's attributes, and
 recursively the attributes of its class's base classes.

help(vars)

Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.

I recommend using help(your_object).

help(dir)

 If called without an argument, return the names in the current scope.
 Else, return an alphabetized list of names comprising (some of) the attributes
 of the given object, and of attributes reachable from it.
 If the object supplies a method named __dir__, it will be used; otherwise
 the default dir() logic is used and returns:
 for a module object: the module's attributes.
 for a class object:  its attributes, and recursively the attributes
 of its bases.
 for any other object: its attributes, its class's attributes, and
 recursively the attributes of its class's base classes.

help(vars)

Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.

回答 8

在大多数情况下,使用__dict__dir()将获得所需的信息。如果您碰巧需要更多细节,则标准库包含检查模块,可让您获得一些令人印象深刻的细节。真正真正的信息包括:

  • 函数名称和方法参数
  • 类层次结构
  • 函数/类对象的实现源代码
  • 框架对象外的局部变量

如果你只是寻找“难道我的对象有什么属性值?”,然后dir()__dict__可能是足够的。如果您真的想深入研究任意对象的当前状态(请记住,在python中几乎所有对象都是对象),那么inspect值得考虑。

In most cases, using __dict__ or dir() will get you the info you’re wanting. If you should happen to need more details, the standard library includes the inspect module, which allows you to get some impressive amount of detail. Some of the real nuggests of info include:

  • names of function and method parameters
  • class hierarchies
  • source code of the implementation of a functions/class objects
  • local variables out of a frame object

If you’re just looking for “what attribute values does my object have?”, then dir() and __dict__ are probably sufficient. If you’re really looking to dig into the current state of arbitrary objects (keeping in mind that in python almost everything is an object), then inspect is worthy of consideration.


回答 9

是否有内置功能可以打印对象的所有当前属性和值?

不可以。最受好评的答案不包括某些类型的属性,被接受的答案显示了如何获取所有属性,包括非公共api的方法和部分。但是,没有为此提供良好的内置函数。

因此,简短的推论是您可以编写自己的脚本,但是它将计算属性和其他计算的数据描述符(它们是公共API的一部分),并且您可能不希望这样做:

from pprint import pprint
from inspect import getmembers
from types import FunctionType

def attributes(obj):
    disallowed_names = {
      name for name, value in getmembers(type(obj)) 
        if isinstance(value, FunctionType)}
    return {
      name: getattr(obj, name) for name in dir(obj) 
        if name[0] != '_' and name not in disallowed_names and hasattr(obj, name)}

def print_attributes(obj):
    pprint(attributes(obj))

其他答案的问题

在具有许多不同类型的数据成员的类上观察当前投票最高的答案的应用:

from pprint import pprint

class Obj:
    __slots__ = 'foo', 'bar', '__dict__'
    def __init__(self, baz):
        self.foo = ''
        self.bar = 0
        self.baz = baz
    @property
    def quux(self):
        return self.foo * self.bar

obj = Obj('baz')
pprint(vars(obj))

仅打印:

{'baz': 'baz'}

由于vars 返回__dict__对象的,而并非副本,因此,如果您修改vars返回的dict,那么您也将修改__dict__对象本身的。

vars(obj)['quux'] = 'WHAT?!'
vars(obj)

返回:

{'baz': 'baz', 'quux': 'WHAT?!'}

-这很糟糕,因为quux是我们不应该设置的属性,也不应该在命名空间中…

在当前接受的答案(和其他答案)中应用建议并没有多大好处:

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar', 'baz', 'foo', 'quux']

如我们所见,dir仅返回与一个对象关联的所有(实际上只是大多数)名称。

inspect.getmembers注释中提到的,也存在类似缺陷-它返回所有名称值。

从Class

在教学时,我让我的学生创建一个函数,该函数提供对象的语义公共API:

def api(obj):
    return [name for name in dir(obj) if name[0] != '_']

我们可以扩展它以提供对象的语义命名空间的副本,但是我们需要排除__slots__未分配的内容,并且如果我们认真对待“当前属性”的请求,则需要排除计算出的属性(如它们可能变得昂贵,并且可以解释为不是“当前”):

from types import FunctionType
from inspect import getmembers

def attrs(obj):
     disallowed_properties = {
       name for name, value in getmembers(type(obj)) 
         if isinstance(value, (property, FunctionType))}
     return {
       name: getattr(obj, name) for name in api(obj) 
         if name not in disallowed_properties and hasattr(obj, name)}

现在我们不计算或显示属性quux:

>>> attrs(obj)
{'bar': 0, 'baz': 'baz', 'foo': ''}

注意事项

但是也许我们确实知道我们的财产并不昂贵。我们可能想要更改逻辑以使其也包括在内。也许我们想排除其他 自定义数据描述符。

然后,我们需要进一步自定义此功能。因此,我们不能拥有一个内在的功能,就可以神奇地准确地知道我们想要什么并提供它,这是有道理的。这是我们需要创建自己的功能。

结论

没有内置函数可以执行此操作,因此您应该执行最适合您情况的语义上的操作。

Is there a built-in function to print all the current properties and values of an object?

No. The most upvoted answer excludes some kinds of attributes, and the accepted answer shows how to get all attributes, including methods and parts of the non-public api. But there is no good complete builtin function for this.

So the short corollary is that you can write your own, but it will calculate properties and other calculated data-descriptors that are part of the public API, and you might not want that:

from pprint import pprint
from inspect import getmembers
from types import FunctionType

def attributes(obj):
    disallowed_names = {
      name for name, value in getmembers(type(obj)) 
        if isinstance(value, FunctionType)}
    return {
      name: getattr(obj, name) for name in dir(obj) 
        if name[0] != '_' and name not in disallowed_names and hasattr(obj, name)}

def print_attributes(obj):
    pprint(attributes(obj))

Problems with other answers

Observe the application of the currently top voted answer on a class with a lot of different kinds of data members:

from pprint import pprint

class Obj:
    __slots__ = 'foo', 'bar', '__dict__'
    def __init__(self, baz):
        self.foo = ''
        self.bar = 0
        self.baz = baz
    @property
    def quux(self):
        return self.foo * self.bar

obj = Obj('baz')
pprint(vars(obj))

only prints:

{'baz': 'baz'}

Because vars only returns the __dict__ of an object, and it’s not a copy, so if you modify the dict returned by vars, you’re also modifying the __dict__ of the object itself.

vars(obj)['quux'] = 'WHAT?!'
vars(obj)

returns:

{'baz': 'baz', 'quux': 'WHAT?!'}

— which is bad because quux is a property that we shouldn’t be setting and shouldn’t be in the namespace…

Applying the advice in the currently accepted answer (and others) is not much better:

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar', 'baz', 'foo', 'quux']

As we can see, dir only returns all (actually just most) of the names associated with an object.

inspect.getmembers, mentioned in the comments, is similarly flawed – it returns all names and values.

From class

When teaching I have my students create a function that provides the semantically public API of an object:

def api(obj):
    return [name for name in dir(obj) if name[0] != '_']

We can extend this to provide a copy of the semantic namespace of an object, but we need to exclude __slots__ that aren’t assigned, and if we’re taking the request for “current properties” seriously, we need to exclude calculated properties (as they could become expensive, and could be interpreted as not “current”):

from types import FunctionType
from inspect import getmembers

def attrs(obj):
     disallowed_properties = {
       name for name, value in getmembers(type(obj)) 
         if isinstance(value, (property, FunctionType))}
     return {
       name: getattr(obj, name) for name in api(obj) 
         if name not in disallowed_properties and hasattr(obj, name)}

And now we do not calculate or show the property, quux:

>>> attrs(obj)
{'bar': 0, 'baz': 'baz', 'foo': ''}

Caveats

But perhaps we do know our properties aren’t expensive. We may want to alter the logic to include them as well. And perhaps we want to exclude other custom data descriptors instead.

Then we need to further customize this function. And so it makes sense that we cannot have a built-in function that magically knows exactly what we want and provides it. This is functionality we need to create ourselves.

Conclusion

There is no built-in function that does this, and you should do what is most semantically appropriate for your situation.


回答 10

一个带有魔术的元编程示例Dump对象

$ cat dump.py
#!/usr/bin/python
import sys
if len(sys.argv) > 2:
    module, metaklass  = sys.argv[1:3]
    m = __import__(module, globals(), locals(), [metaklass])
    __metaclass__ = getattr(m, metaklass)

class Data:
    def __init__(self):
        self.num = 38
        self.lst = ['a','b','c']
        self.str = 'spam'
    dumps   = lambda self: repr(self)
    __str__ = lambda self: self.dumps()

data = Data()
print data

没有参数:

$ python dump.py
<__main__.Data instance at 0x00A052D8>

带有Gnosis实用程序

$ python dump.py gnosis.magic MetaXMLPickler
<?xml version="1.0"?>
<!DOCTYPE PyObject SYSTEM "PyObjects.dtd">
<PyObject module="__main__" class="Data" id="11038416">
<attr name="lst" type="list" id="11196136" >
  <item type="string" value="a" />
  <item type="string" value="b" />
  <item type="string" value="c" />
</attr>
<attr name="num" type="numeric" value="38" />
<attr name="str" type="string" value="spam" />
</PyObject>

它有点过时了,但仍然可以使用。

A metaprogramming example Dump object with magic:

$ cat dump.py
#!/usr/bin/python
import sys
if len(sys.argv) > 2:
    module, metaklass  = sys.argv[1:3]
    m = __import__(module, globals(), locals(), [metaklass])
    __metaclass__ = getattr(m, metaklass)

class Data:
    def __init__(self):
        self.num = 38
        self.lst = ['a','b','c']
        self.str = 'spam'
    dumps   = lambda self: repr(self)
    __str__ = lambda self: self.dumps()

data = Data()
print data

Without arguments:

$ python dump.py
<__main__.Data instance at 0x00A052D8>

With Gnosis Utils:

$ python dump.py gnosis.magic MetaXMLPickler
<?xml version="1.0"?>
<!DOCTYPE PyObject SYSTEM "PyObjects.dtd">
<PyObject module="__main__" class="Data" id="11038416">
<attr name="lst" type="list" id="11196136" >
  <item type="string" value="a" />
  <item type="string" value="b" />
  <item type="string" value="c" />
</attr>
<attr name="num" type="numeric" value="38" />
<attr name="str" type="string" value="spam" />
</PyObject>

It is a bit outdated but still working.


回答 11

如果您正在使用它进行调试,并且只想递归地转储所有内容,那么可接受的答案将不令人满意,因为这要求您的类已经具有良好的__str__实现。如果不是这种情况,那么效果会更好:

import json
print(json.dumps(YOUR_OBJECT, 
                 default=lambda obj: vars(obj),
                 indent=1))

If you’re using this for debugging, and you just want a recursive dump of everything, the accepted answer is unsatisfying because it requires that your classes have good __str__ implementations already. If that’s not the case, this works much better:

import json
print(json.dumps(YOUR_OBJECT, 
                 default=lambda obj: vars(obj),
                 indent=1))

回答 12

尝试ppretty

from ppretty import ppretty


class A(object):
    s = 5

    def __init__(self):
        self._p = 8

    @property
    def foo(self):
        return range(10)


print ppretty(A(), show_protected=True, show_static=True, show_properties=True)

输出:

__main__.A(_p = 8, foo = [0, 1, ..., 8, 9], s = 5)

Try ppretty

from ppretty import ppretty


class A(object):
    s = 5

    def __init__(self):
        self._p = 8

    @property
    def foo(self):
        return range(10)


print ppretty(A(), show_protected=True, show_static=True, show_properties=True)

Output:

__main__.A(_p = 8, foo = [0, 1, ..., 8, 9], s = 5)

回答 13

from pprint import pprint

def print_r(the_object):
    print ("CLASS: ", the_object.__class__.__name__, " (BASE CLASS: ", the_object.__class__.__bases__,")")
    pprint(vars(the_object))
from pprint import pprint

def print_r(the_object):
    print ("CLASS: ", the_object.__class__.__name__, " (BASE CLASS: ", the_object.__class__.__bases__,")")
    pprint(vars(the_object))

回答 14

这将以json或yaml缩进格式递归打印所有对象内容:

import jsonpickle # pip install jsonpickle
import json
import yaml # pip install pyyaml

serialized = jsonpickle.encode(obj, max_depth=2) # max_depth is optional
print json.dumps(json.loads(serialized), indent=4)
print yaml.dump(yaml.load(serialized), indent=4)

This prints out all the object contents recursively in json or yaml indented format:

import jsonpickle # pip install jsonpickle
import json
import yaml # pip install pyyaml

serialized = jsonpickle.encode(obj, max_depth=2) # max_depth is optional
print json.dumps(json.loads(serialized), indent=4)
print yaml.dump(yaml.load(serialized), indent=4)

回答 15

我赞成仅提及pprint的答案。明确地说,如果要查看复杂数据结构中的所有,请执行以下操作:

from pprint import pprint
pprint(my_var)

其中my_var是您感兴趣的变量。当我使用时,pprint(vars(my_var))我什么也没得到,这里的其他答案也无济于事,或者该方法看起来不必要地冗长。顺便说一句,在我的特定情况下,我正在检查的代码具有字典词典。

值得指出的是,对于某些自定义类,您可能只会得到无用<someobject.ExampleClass object at 0x7f739267f400>的输出。在这种情况下,您可能必须实现一个__str__方法或尝试其他解决方案。我仍然想找到没有第三方库就可以在所有情况下使用的简单方法。

I’ve upvoted the answer that mentions only pprint. To be clear, if you want to see all the values in a complex data structure, then do something like:

from pprint import pprint
pprint(my_var)

Where my_var is your variable of interest. When I used pprint(vars(my_var)) I got nothing, and other answers here didn’t help or the method looked unnecessarily long. By the way, in my particular case, the code I was inspecting had a dictionary of dictionaries.

Worth pointing out that with some custom classes you may just end up with an unhelpful <someobject.ExampleClass object at 0x7f739267f400> kind of output. In that case, you might have to implement a __str__ method, or try some of the other solutions. I’d still like to find something simple that works in all scenarios, without third party libraries.


回答 16

我需要在一些日志中打印DEBUG信息,并且无法使用pprint,因为它将破坏它。相反,我这样做了,并且得到了几乎相同的东西。

DO = DemoObject()

itemDir = DO.__dict__

for i in itemDir:
    print '{0}  :  {1}'.format(i, itemDir[i])

I was needing to print DEBUG info in some logs and was unable to use pprint because it would break it. Instead I did this and got virtually the same thing.

DO = DemoObject()

itemDir = DO.__dict__

for i in itemDir:
    print '{0}  :  {1}'.format(i, itemDir[i])

回答 17

要转储“ myObject”:

from bson import json_util
import json

print(json.dumps(myObject, default=json_util.default, sort_keys=True, indent=4, separators=(',', ': ')))

我尝试了vars()和dir(); 都因为我要找的东西而失败了。vars()无效,因为对象没有__dict__(exceptions.TypeError:vars()参数必须具有__dict__属性)。dir()并不是我要找的东西:它只是字段名的列表,不提供值或对象结构。

我认为json.dumps()适用于没有default = json_util.default的大多数对象,但是我在对象中有一个datetime字段,因此标准json序列化程序失败。请参阅如何克服python中的“ datetime.datetime无法JSON序列化”?

To dump “myObject”:

from bson import json_util
import json

print(json.dumps(myObject, default=json_util.default, sort_keys=True, indent=4, separators=(',', ': ')))

I tried vars() and dir(); both failed for what I was looking for. vars() didn’t work because the object didn’t have __dict__ (exceptions.TypeError: vars() argument must have __dict__ attribute). dir() wasn’t what I was looking for: it’s just a listing of field names, doesn’t give the values or the object structure.

I think json.dumps() would work for most objects without the default=json_util.default, but I had a datetime field in the object so the standard json serializer failed. See How to overcome “datetime.datetime not JSON serializable” in python?


回答 18

为什么不简单一些:

for key,value in obj.__dict__.iteritems():
    print key,value

Why not something simple:

for key,value in obj.__dict__.iteritems():
    print key,value

回答 19

pprint包含一个“漂亮打印机”,用于生成美观的数据结构表示。格式化程序产生的数据结构可以由解释器正确解析,并且易于阅读。如果可能的话,输出保持在一行上,并在分成多行时缩进。

pprint contains a “pretty printer” for producing aesthetically pleasing representations of your data structures. The formatter produces representations of data structures that can be parsed correctly by the interpreter, and are also easy for a human to read. The output is kept on a single line, if possible, and indented when split across multiple lines.


回答 20

只需尝试beeprint

它不仅可以帮助您打印对象变量,而且还可以帮助您输出漂亮的输出,例如:

class(NormalClassNewStyle):
  dicts: {
  },
  lists: [],
  static_props: 1,
  tupl: (1, 2)

Just try beeprint.

It will help you not only with printing object variables, but beautiful output as well, like this:

class(NormalClassNewStyle):
  dicts: {
  },
  lists: [],
  static_props: 1,
  tupl: (1, 2)

回答 21

对于每个奋斗的人

  • vars() 不返回所有属性。
  • dir() 不返回属性的值。

以下代码显示带有的所有属性obj及其值:

for attr in dir(obj):
        try:
            print("obj.{} = {}".format(attr, getattr(obj, attr)))
        except AttributeError:
            print("obj.{} = ?".format(attr))

For everybody struggling with

  • vars() not returning all attributes.
  • dir() not returning the attributes’ values.

The following code prints all attributes of obj with their values:

for attr in dir(obj):
        try:
            print("obj.{} = {}".format(attr, getattr(obj, attr)))
        except AttributeError:
            print("obj.{} = ?".format(attr))

回答 22

您可以尝试Flask调试工具栏。
https://pypi.python.org/pypi/Flask-DebugToolbar

from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)

# the toolbar is only enabled in debug mode:
app.debug = True

# set a 'SECRET_KEY' to enable the Flask session cookies
app.config['SECRET_KEY'] = '<replace with a secret key>'

toolbar = DebugToolbarExtension(app)

You can try the Flask Debug Toolbar.
https://pypi.python.org/pypi/Flask-DebugToolbar

from flask import Flask
from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)

# the toolbar is only enabled in debug mode:
app.debug = True

# set a 'SECRET_KEY' to enable the Flask session cookies
app.config['SECRET_KEY'] = '<replace with a secret key>'

toolbar = DebugToolbarExtension(app)

回答 23

我喜欢使用python对象内置类型keysvalues

对于属性,无论它们是方法还是变量:

o.keys()

对于这些属性的值:

o.values()

I like working with python object built-in types keys or values.

For attributes regardless they are methods or variables:

o.keys()

For values of those attributes:

o.values()

回答 24

无论在类中,__init__或外部如何定义变量,该方法都有效。

your_obj = YourObj()
attrs_with_value = {attr: getattr(your_obj, attr) for attr in dir(your_obj)}

This works no matter how your varibles are defined within a class, inside __init__ or outside.

your_obj = YourObj()
attrs_with_value = {attr: getattr(your_obj, attr) for attr in dir(your_obj)}