Python文档字符串意外地为None。

3

When I create a class with method like this:

class Foo:
    """ A class """
    def bar():
        " Hello world "
        pass

我希望 __doc__ 返回方法的第一个语句,因为两者都是字符串。不幸的是,这并没有发生:
print(Foo.__doc__)
print(Foo().__doc__)
print(Foo.bar.__doc__)
print(Foo().bar.__doc__)

输出 4 次的是 None。这里的例子

https://docs.python.org/zh-cn/3.8/library/functools.html?highlight=__doc__#functools.wraps

看起来似乎有问题,因为对我来说文档字符串是 None

from functools import wraps
def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        print('Calling decorated function')
        return f(*args, **kwds)
    return wrapper

@my_decorator
def example():
    """Docstring"""
    print('Called example function')
print(example.__doc__)

同时打印 None

为什么 __doc__ 没有设置成指定的字符串?

ideone 代码片段: http://ideone.com/cZNUQe


哇:在我的本地机器上,这些所有输出都符合预期的文档字符串(sys.version == '3.4.3+ (default, Oct 14 2015, 16:03:50) \n[GCC 5.2.1 20151010]')。 - Herbert
1
这个程序在我的机器上运行正常,可能是ideone平台的一个bug。 - Morgan Thrapp
另外,我在我的机器上看到了相同的__doc__None的行为,这发生在一些较少的最小示例(即客户生产代码)中。 - Herbert
好的,没有这些例子,我不知道我们怎么能帮助你。 - Morgan Thrapp
@MorganThrapp 显然,我的意思是我需要一些时间来得到更好的 MWE。 - Herbert
我的问题与装饰器有关。然而,上面的 Ideone 行为仍然很奇怪。 - Herbert
1个回答

3

更新:根据@user2357112的建议,我找到了一种在本地解释器中复制 ideone 行为的方法,这可能是奇怪差异的原因。具体来说,看起来 ideone 在 Python 中运行一个包装脚本,而不使用 -OO 标志,但使用内置函数 compile 编译用户提供的代码,并将 optimize=2 作为参数,然后使用 eval 运行生成的编译代码对象。看起来这会导致先前提到的分裂行为;sys.flags.optimize0,而 __debug__True(与外部解释器的状态相匹配),但 docstrings 和 assert 语句被剥离(与调用 compile 时使用的优化级别相匹配)。

要复制,请在 Python 中运行以下脚本,不要传递 -OO 标志:

source = '''import sys
print(sys.flags)
print("Debug:", __debug__)
if __debug__:
    print("Debug")
else:
    print("Non-debug")
class Foo:
    """abc"""
print("Foo docstring:", Foo.__doc__)
assert True == False
'''

compiled = compile(source, '<test>', 'exec', optimize=2)
eval(compiled)

输出结果为:
sys.flags(debug=0, inspect=0, interactive=0, optimize=0, dont_write_bytecode=0, no_user_site=0, no_site=0, ignore_environment=0, verbose=0, bytes_warning=0, quiet=0, hash_randomization=1, isolated=0)
Debug: True
Non-debug
Foo docstring: None

第一和第二行的行为看起来像是没有使用-OO,而接下来的两行(以及缺少AssertionError)的行为似乎像是启用了-OO。看起来像是在运行时执行sys.flags检查(这意味着它们反映主解释器状态),__debug__可以说是一半一半(if检查和其他条件逻辑可能在编译时执行,而其他用法在运行时执行,注意__debug__打印为True,但测试为False),同时剥离docstrings和assert完全在编译时完成(所以它们反映了compileoptimize标志的状态)。

对于非ideone情况,以下旧答案更加适用:

在非ideone情况下,这样的行为表明您正在使用-OO开关(双重优化)。当使用-OO时,docstrings会在字节码编译期间被剥离,并替换为None;将PYTHONOPTIMIZE环境变量设置为2将产生同样的效果。


1
但是 optimize 标志被设置为 0 http://ideone.com/Zq9TdR - vaultah
我怀疑Ideone的开发人员将Python设置为在编译过程中运行-OO,而没有意识到编译后的代码会继承该-OO设置,即使在执行过程中未指定-OO - user2357112
@vaultah:经过进一步检查,我同意,有些可疑。它的行为有点像设置了-OO,但又像既没有设置-O也没有设置-OO;根据-OOassert和文档字符串被剥离,但是sys.flags和特殊的__debug__变量的行为就像没有设置-O一样。我猜ideone正在使用一些自定义的Python实现,它与预期的文档行为不完全匹配,因为在__debug__True时跳过assert是不可能的,这两者是互斥的,并且都由-O单独控制。 - ShadowRanger
@user2357112:我无法使用 py_compile 进行复现(该功能会生成特定优化级别的字节码文件),但是我可以通过 compile 内置函数获得类似的行为。我正在调查本地 Python 的完整复现。 - ShadowRanger
@ShadowRanger: py_compile通常会写入特定于优化级别的输出位置,但如果您指定一个cfile参数,它将在那里写入输出,即使扩展名和优化级别不匹配。 - user2357112
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接