在Python编程中,调试是一个非常重要的技能,可以帮助我们快速发现和解决代码中的问题。下面是几种常用的Python调试技巧:
以上是一些常用的Python调试技巧,可以根据实际情况选择合适的方式进行调试。同时,还需要注意代码的可读性和可维护性,以便更好地进行调试和修改。
下面给出几个Python调试和日志的例子:
def add(a, b):
print(f"Adding {a} and {b}")
return a + b
result = add(1, 2)
print(f"Result: {result}")
在上面的例子中,我们在add函数中添加了一条print语句,输出调用该函数时传入的参数,方便我们查看程序的运行情况。
import pdb
def add(a, b):
pdb.set_trace() # 设置断点
return a + b
result = add(1, 2)
print(f"Result: {result}")
在上面的例子中,我们使用pdb模块设置了一个断点,当程序执行到该行时会停下来,方便我们进行调试。在停下来后,我们可以使用一些pdb提供的命令如p(打印变量)、n(执行下一行代码)、c(继续执行程序)等进行调试。
import logging
logging.basicConfig(filename='example.log', level=logging.DEBUG)
def add(a, b):
logging.debug(f"Adding {a} and {b}")
return a + b
result = add(1, 2)
logging.info(f"Result: {result}")
在上面的例子中,我们使用Python自带的logging模块记录程序运行过程中的信息。在代码中,我们使用basicConfig函数设置了日志的输出文件和日志级别,使用debug和info函数记录了程序的运行情况。运行程序后,程序运行的过程和结果会被记录到example.log文件中。
这些例子展示了一些Python调试和日志的常用技巧,可以根据实际需求进行调整和扩展。需要注意的是,在实际开发中,要尽可能保证代码的可读性和可维护性,以方便后续的调试和修改。
在Python编程中,常用的调试方法有print语句、断点调试、日志记录等。下面对这些方法进行比较:
优点:
缺点:
优点:
缺点:
优点:
缺点:
综上所述,不同的调试方法有各自的优缺点,需要根据实际情况选择合适的方法。在实际开发中,可以根据需要灵活使用这些调试方法,以提高程序的可靠性和性能。
inspect
是Python标准库中提供的一个模块,可以帮助我们获取和处理对象的信息,包括模块、类、函数、方法等。
inspect
模块提供了很多有用的函数,下面是一些常用函数的介绍:
inspect.getmembers(object[, predicate])
: 获取对象中的所有成员,返回一个由(name, value)元组组成的列表。predicate参数可以用来过滤成员,如果指定了该参数,则只返回满足条件的成员。inspect.getmodule(object)
: 获取对象所在的模块。inspect.ismodule(object)
: 判断一个对象是否是模块。inspect.isclass(object)
: 判断一个对象是否是类。inspect.isfunction(object)
: 判断一个对象是否是函数。inspect.getargspec(func)
: 获取函数的参数信息,返回一个包含四个元素的元组,分别是:args(函数的位置参数)、varargs(函数的可变位置参数)、keywords(函数的关键字参数)、defaults(函数的默认参数)。inspect.signature(func)
: 获取函数的签名信息,返回一个Signature对象,可以用来获取函数的参数名称、默认值、注解等信息。使用inspect
模块可以方便地获取和处理对象信息,可以用于调试、文档生成、代码分析等场景。需要注意的是,由于Python的动态性,一些对象的信息可能在运行时才能确定,使用inspect
可能会有一定的限制和不确定性。
下面给出一些使用inspect
模块的例子:
import inspect
def add(a, b=1, *args, **kwargs):
pass
argspec = inspect.getargspec(add)
print(argspec.args) # ['a', 'b']
print(argspec.varargs) # 'args'
print(argspec.keywords) # 'kwargs'
print(argspec.defaults) # (1,)
在上面的例子中,我们定义了一个函数add
,使用inspect.getargspec
获取了该函数的参数信息,包括位置参数、可变位置参数、关键字参数和默认参数。
import inspect
class Person:
name = 'Tom'
age = 20
def say_hello(self):
print(f"Hello, my name is {self.name}.")
members = inspect.getmembers(Person)
for name, value in members:
print(f"{name}: {value}")
Person
,使用inspect.getmembers
获取了该类的成员信息,包括类属性和实例方法。通过遍历返回的成员列表,我们可以输出每个成员的名称和值。假设我们有一个名为example_module.py
的模块,其中包含两个函数add
和multiply
,并且这个模块位于当前工作目录下:
# example_module.py
def add(a, b):
"""
Returns the sum of two numbers.
"""
return a + b
def multiply(a, b):
"""
Returns the product of two numbers.
"""
return a * b
现在我们将编写一个脚本,使用inspect
模块获取example_module.py
模块中函数的信息:
# main.py
import inspect
import example_module
# 获取模块中所有函数的信息
functions = inspect.getmembers(example_module, inspect.isfunction)
# 打印每个函数的名称和参数信息
for function in functions:
print("Function name:", function[0])
print("Function arguments:", inspect.signature(function[1]))
在运行这个脚本之前,需要确保example_module.py
和main.py
位于同一目录下。然后在终端中运行main.py
:
$ python main.py
输出结果将是:
Function name: add
Function arguments: (a, b)
Function name: multiply
Function arguments: (a, b)
这个例子演示了如何使用inspect
模块获取模块中函数的信息。
inspect
模块还可以用于获取源代码、类继承关系、堆栈信息,可以用于检查Python对象的各个方面,包括:
inspect.getsource()
函数可以用于获取Python对象的源代码。inspect.getmro()
函数可以用于获取类的继承关系。inspect.stack()
函数可以用于获取当前Python程序的堆栈信息,包括函数调用栈和文件名。下面是一个使用inspect
模块获取类继承关系的示例:
在这个示例中,我们定义了三个类A
、B
和C
,其中C
是B
的子类,B
是A
的子类。然后,使用getmro
函数获取C
类的继承关系,并将其打印出来。输出结果将是:
(, , , )
这个结果表明,C
类首先继承自B
类,然后继承自A
类,最后继承自object
类。
import inspect
class A:
pass
class A2:
pass
class B(A):
pass
class C(A,A2):
pass
class D(B):
pass
mro = inspect.getmro(D)
print(mro)
mro = inspect.getmro(C)
print(mro)
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.A2'>, <class 'object'>)
locals()
Update and return a dictionary representing the current local symbol table. Free variables are returned by locals()
when it is called in function blocks, but not in class blocks. Note that at the module level, locals()
and globals()
are the same dictionary.
Note:The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.
def fun(a, b, c):
d = locals()
print(d)
fun(1,2,3)
#输出
#{'a': 1, 'b': 2, 'c': 3}
def prt(*args,**kwargs):
# print(args)
qq="ttt"
print(locals())
# print(vars())
for i in args:
print(i)
a=1
b=3
prt(a,b,test=a)
#输出
# {'args': (1, 3), 'kwargs': {'test': 1}, 'qq': 'ttt'}
# 1
# 3
def prt(*args, **kwargs):
v=locals()
print(v['args'])
print(v['kwargs'])
# for i in args:
# print(i)
prt(123,a=a, b=b)
#输出
# (123,)
# {'a': 1, 'b': 3}
f-string debugging in Python 3.8 (tirkarthi.github.io)
2. Lexical analysis — Python 3.11.1 documentation
'='
is provided, the output will have the expression text, the '='
and the evaluated value. Spaces after the opening brace '{'
, within the expression and after the '='
are all retained in the output. By default, the '='
causes the repr()
of the expression to be provided, unless there is a format specified. When a format is specified it defaults to the str()
of the expression unless a conversion '!r'
is declared.在K&R的<>中提到过调试宏,可以方便我查看变量的值
在python中,可以用f-string的debug语法:
var1="varable1"
var2="varable2"
print(f'{var1=},{var2=}')
print(f'{var1=}\n{var2=}')
结果:
var1='varable1',var2='varable2'
var1='varable1'
var2='varable2'
可见,我们不再需要重复打印变量名,输出结果自带变量值的归属
import logging
# 配置日志记录器
logging.basicConfig(filename='example.log', level=logging.DEBUG,
format='%(asctime)s %(levelname)s: %(message)s')
def add(a, b):
"""
Returns the sum of two numbers.
"""
logging.debug("Adding %s and %s", a, b)
result = a + b
logging.debug("Result: %s", result)
return result
def multiply(a, b):
"""
Returns the product of two numbers.
"""
logging.debug("Multiplying %s and %s", a, b)
result = a * b
logging.debug("Result: %s", result)
return result
# 使用函数记录日志
x = 2
y = 3
logging.info("Starting calculation with %s and %s", x, y)
result1 = add(x, y)
result2 = multiply(x, y)
logging.info("Calculation finished: %s %s", result1, result2)
在这个例子中,我们首先使用basicConfig
函数配置了一个日志记录器,将日志记录到文件example.log
中,设置日志级别为DEBUG
,并指定了日志记录的格式。
然后,定义了两个函数add
和multiply
,这些函数在执行时会记录一些调试信息。最后,我们在主程序中调用这些函数,并使用info
级别记录了一条日志,指示程序已经开始计算。
同时,在当前工作目录下也会生成一个名为example.log
的日志文件,其中包含所有的日志记录:
2023-04-11 11:28:49,675 INFO: Starting calculation with 2 and 3
2023-04-11 11:28:49,675 DEBUG: Adding 2 and 3
2023-04-11 11:28:49,675 DEBUG: Result: 5
2023-04-11 11:28:49,675 DEBUG: Multiplying 2 and 3
2023-04-11 11:28:49,675 DEBUG: Result: 6
2023-04-11 11:28:49,675 INFO: Calculation finished: 5 6
这个例子演示了如何使用logging
模块记录日志,以及如何配置日志记录器和指定日志级别。