【问题标题】:PyCharm: 'Function Doesn't Return Anything'PyCharm:'函数不会返回任何东西'
【发布时间】:2017-02-09 02:58:19
【问题描述】:

我今天刚开始使用 PyCharm Community Edition 2016.3.2。每次我从我的函数at_square 分配一个值时,它都会警告我“函数at_square 不会返回任何东西”,但它肯定会在每个实例中返回,除非在执行期间引发错误,并且每次使用该函数表现如预期。我想知道为什么 PyCharm 认为它没有,以及我是否可以做些什么来纠正它。 (我知道有一个选项可以抑制该特定函数的警告,但它是通过在函数上方的代码中插入注释行来实现的,而且我发现必须记住在最后取出它同样烦人项目。)

这是有问题的函数:

def at_square(self, square):
    """ Return the value at the given square """
    if type(square) == str:
        file, rank = Board.tup_from_an(square)
    elif type(square) == tuple:
        file, rank = square
    else:
        raise ValueError("Expected tuple or AN str, got " + str(type(square)))

    if not 0 <= file <= 7:
        raise ValueError("File out of range: " + str(file))
    if not 0 <= rank <= 7:
        raise ValueError("Rank out of range: " + str(rank))

    return self.board[file][rank]

如果重要的话,这更准确地说是一个对象的方法。我坚持使用“函数”一词,因为这是 PyCharm 使用的语言。

我唯一的想法是我使用错误引发可能会混淆 PyCharm,但这似乎太简单了。 (请随时批评我提出的错误,因为我不确定这是不是惯用的做法。)

更新:幽默地说,如果我完全删除返回线,警告会消失并在我把它放回去时立即返回。如果我用像8 这样的常量值替换self.board[file][rank],它也会消失。将filerank 更改为常量值并不会消除警告,因此我认为PyCharm 对self.board 的性质感到困惑,self.board 是其他 8 个列表的列表。

更新:根据@StephenRauch 的建议,我创建了一个最小示例,它反映了与at_square 完成的数据分配相关的所有内容:

class Obj:
    def __init__(self):
        self.nested_list = [[0],[1]]

    @staticmethod
    def tup_method(data):
        return tuple(data)

    def method(self,data):
        x,y = Obj.tup_method(data)
        return self.nested_list[x][y]

    def other_method(self,data):
        value = self.method(data)
        print(value)

x = Obj()
x.other_method([1,2])

PyCharm 对此没有任何警告。在at_square 中,我尝试将每一行注释掉到以下两行:

def at_square(self, square):
    file, rank = Board.tup_from_an(square)
    return self.board[file][rank]

PyCharm 给出了同样的警告。如果我只留下返回线,那么警告才会消失。 PyCharm 似乎对filerank 通过tup_from_an 的同时分配感到困惑。这是该方法的代码:

@staticmethod
def tup_from_an(an):
    """ Convert a square in algebraic notation into a coordinate tuple """
    if an[0] in Board.a_file_dict:
        file = Board.a_file_dict[an[0]]
    else:
        raise ValueError("Invalid an syntax (file out of range a-h): " + str(an))

    if not an[1].isnumeric():
        raise ValueError("Invalid an syntax (rank out of range 1-8): " + str(an))
    elif int(an[1]) - 1 in Board.n_file_dict:
        rank = int(an[1]) - 1
    else:
        raise ValueError("Invalid an syntax (rank out of range 1-8): " + str(an))

    return file, rank

更新:在其构造函数中,类Board(它是所有这些方法的父类)将对该实例的引用保存在静态变量instance 中。 self.at_square(square) 给出警告,而Board.instance.at_square(square) 没有。我仍将在适当的情况下使用前者,但这可能有助于了解 PyCharm 的想法。

【问题讨论】:

  • 这对我来说很好用。也许你编辑太多了?您是否创建了 Minimal, Complete, and Verifiable 示例?旁注,我建议您在将任何时间花在您不理解的警告上之前清除所有警告。例如:你使用file 作为变量名,PyCharm 会抱怨。
  • @StephenRauch 这很有趣。我在切换之前在 IDLE 中编写了该函数,并且警告立即显而易见。自从使用 PyCharm 以来,我对其所做的所有更改都将其名称从混合大小写更改为带有下划线的小写。我一直在处理这些警告,但它没有说明我使用file 作为变量名。那是象棋术语中的文件。感谢您对 Minimal、Complete 和 Verifiable 示例的参考。每次我发帖时,我都会了解更多关于 SO 的信息。我不确定如何重新创建它,但我会尝试一下。
  • @StephenRauch 更新了原来的问题
  • FWIW 我在 min 函数中遇到了这个错误。我实际上是在向 min 传递一个参数,该参数(静态)被标识为 None。
  • self.board的初始值是多少?

标签: python-3.x pycharm


【解决方案1】:

如果返回值静态计算为None,PyCharm 会假定缺少返回值。如果使用 None 初始化值并稍后更改其类型,则可能会发生这种情况。

class Foo:
    def __init__(self):
        self.qux = [None]  # infers type for Foo().qux as List[None]

    def bar(self):
        return self.qux[0]  # infers return type as None

此时,Foo.bar静态推断为(self: Foo) -&gt; None动态地通过副作用改变qux的类型不会更新这个:

foo = Foo()
foo.qux = [2]  # *dynamic* type of foo.bar() is now ``(self: Foo) -> int``
foo_bar = foo.bar()  # Function 'bar' still has same *static* type

问题是您正在通过 动态分配的实例 属性覆盖 静态推断的类 属性。这对于一般的静态分析来说是不可行的。

您可以使用 explicit 类型提示来解决此问题。

import typing


class Foo:
    def __init__(self):
        self.qux = [None]  # type: typing.List[int]

    def bar(self):
        return self.qux[0]  # infers return type as int

从 Python 3.5 开始,您还可以使用inline type hints。这些对于返回类型特别有用。

import typing


class Foo:
    def __init__(self):
        # initial type hint to enable inference
        self.qux: typing.List[int] = [None]

    # explicit return type hint to override inference
    def bar(self) -> int:
        return self.qux[0]  # infers return type as int

请注意,依赖推理在哪里起作用仍然是一个好主意!仅注释 self.qux 可以更轻松地稍后更改类型。注释 bar 主要用于文档和覆盖不正确的推理。

如果需要支持pre-3.5,也可以使用stub files。假设你的班级在foomodule.py,创建一个名为foomodule.pyi 的文件。在里面,只需添加带注释的字段和函数签名;你可以(并且应该)忽略尸体。

import typing


class Foo:
    # type hint for fields
    qux: typing.List[int]

    # explicit return type hint to override inference
    def bar(self) -> int:
        ...

【讨论】:

    【解决方案2】:

    从 Python 3.6 开始的类型提示

    现在推荐下例中的样式:

    from typing import typing
    
    class Board:
        def __init__(self):
            self.board: List[List[int]] = []
    

    快速文档

    如果您输入正确,Pycharm 的“快速文档”会显示。将光标放在感兴趣对象的中间,然后按 Ctrl+Q。我怀疑来自 tup_from_an(an) 的类型不会像预期的那样。您可以尝试输入所有参数和内部对象的提示,但最好只输入函数返回类型的提示。类型提示意味着我不需要搜索外部文档,因此我将精力集中在将由外部用户使用的对象上,并尽量不做太多内部工作。这是 arg 和返回类型的提示:

    @staticmethod
        def tup_from_an(an: List[int]) -> (int, int):
            ...
    

    清除缓存

    Pycharm 可以锁定过时的定义。去帮助>寻找行动...>清除缓存没有什么坏处

    没有完美的身体

    Python 在不断改进(类型提示在 3.7 中更新) Pycharm 也在不断改进。这些相对不成熟的高级功能的快速开发速度意味着检查或提交到他们的问题跟踪器可能是下一个电话。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-10
    • 2019-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-13
    • 2017-01-04
    相关资源
    最近更新 更多