返回 python 对象方法的智能感知

Intellisense for returned python object's methods

我是 Python 的新手,我非常喜欢这门语言。但是我最近在 eclipse 中使用 PyDev 时遇到了一个恼人的问题。

某些方法 returned 某些 class 的实例。但是我无法获得实例方法的智能感知。

例如:

import openpyxl
from openpyxl.reader.excel import load_workbook
from openpyxl.worksheet import Worksheet


xlsFile='hello.xlsx'
wbook = load_workbook(xlsFile)

wsheet1=wbook.get_sheet_by_name('mysheet')
wsheet1.cell('A9').hyperlink=r'\sharefolder'

wsheet2=Worksheet()
wsheet2.cell('A1').hyperlink=r'\sharefolder'

在这段代码中,我可以使用 wsheet2 获得方法 cell() 的提示,但不能使用 wsheet1。虽然它们都是我已经导入的 Worksheet 类型。似乎 python 或 PyDev 无法正确检测 returned 对象的类型。

这是语言限制吗?还是我做错了什么?现在,我必须深入研究源代码,看看 return 值的真实类型是什么。然后检查该类型中定义的方法。非常乏味。

我写了一个小测试来重现这个问题。奇怪,智能感知似乎起作用了。

嗯,从技术上讲,在 Python 中,方法可以 return 任何东西,并且只有在操作完成时才定义操作的结果。

考虑这个简单的函数:

def f(a):
    if a == 1:
        return 1 # returns int
    elif a == 2:
        return "2" # returns string
    else:
        return object() # returns an `object` instance

该函数对于 Python 非常有效,其结果是严格定义的,但仅在函数执行的末尾 定义。确实:

>>> type(f(1))
<type 'int'>
>>> type(f(2))
<type 'str'>
>>> type(f(3))
<type 'object'>

当然,这种灵活性并不是一直都需要的,而且大多数方法 return 都是可以先验预测的。聪明的 IDE 可以分析代码(以及一些其他提示,例如可以指定参数和 return 类型的文档字符串),但这始终是具有一定置信度的猜测。还有 PEP0484 在语言级别引入了类型提示,但它是可选的,相对较新,所有遗留代码肯定不会使用它。

如果 PyDev 不适用于特定情况,那很遗憾,但是如果您选择像 Python 这样的动态语言,这是您应该接受的事情。也许值得尝试不同的、更智能的 IDE 或在 IDE 旁边打开一个带有交互式 Python 提示的控制台来即时测试您的代码。我建议使用像 bpython

这样复杂的 python shell

这是 Python 是动态类型的结果。

在 statically-typed 语言(例如 C#)中,方法 注释 带有它们的类型签名。 (另外:在某些系统中,类型检查器可以 推断 类型。)编译器知道函数的 return 类型,以及参数的类型,没有 运行 你的代码,因为你写下了类型!这使您的工具不仅可以检查程序的类型,还可以构建有关程序中的方法及其类型的元数据; Intellisense 通过查询从程序文本中收集的元数据来工作。


Python 语言中没有内置静态类型系统。这使得工具更难在没有 运行 代码的情况下为您提供提示。比如这个函数的return类型是什么?

def spam(eggs):
    if eggs:
        return "ham"
    return 42

有时spam return是一个字符串;有时它 return 是一个整数。 Intellisense 应在调用 spam 的 return 值上显示哪些方法?

这个class有哪些可用属性?

class Spam:
    def __getattr__(self, name):
        if len(name) > 5:
            return "foo"
        return super().__getattr__(name)

Spam 有时会动态生成属性:Intellisense 应该为 Spam 的实例显示什么?

在这些情况下没有正确答案。您也许可以自愿进行一些猜测(例如,您可以显示一个列表,其中包含 strintspam 的 return 值的方法,但是你不能给出永远正确的建议。


因此 Python 的 Intellisense 工具被缩减为 best-guesses。在您给出的示例中,您的 IDE 对 get_sheet_by_name 的 return 类型了解不够,无法为您提供有关 wsheet1 的信息。但是,它确实知道 wsheet2 的类型,因为您只是将它实例化为 Worksheet。在您的第二个示例中,Intellisense 只是通过检查其源代码对 f1 的 return 类型做出(正确的)猜测。

顺便说一下,auto-completion 在像 IPython 这样的交互式 shell 中更可靠。这是因为 IPython 实际上运行您键入的代码。它可以告诉对象的运行时类型是什么,因为分析是在运行时发生的。

您可以使用断言告诉智能感知 class 您想要它是什么。当然现在如果不是,它会抛出一个错误,但这是一件好事。

assert isinstance(my_variable, class_i_want_it_to_be)

这将为您提供 auto-complete 和 ctrl-click 以跳转到您一直在寻找的功能。 (至少这是现在 2022 年的运作方式,其他一些答案已有 5 年历史了)。

这是一个简单的例子。

#!/usr/bin/python3

class FooMaker():
    def make_foo():
        return "foo"

#this makes a list of constants
list1 = [FooMaker(),FooMaker()]

#Even if the result is the same. These are not constants
list2 = []
for i in range(2):
    list2.append(FooMaker)


#intellisense knows this is a FooMaker
m1 = list1[0]

#now intellisense isn't sure what this object is
m2 = list2[0]

# Make_foo is highlighted for m1 and not for m2
m1.make_foo()
m2.make_foo()

# now make_foo is highlighted for M2
assert isinstance(m2, FooMaker)
m2.make_foo()

我的 vs 代码中的色差很微妙。但无论如何,这是一个屏幕截图。

tldr: 网上那么多答案都是说“不”,以至于我花了一段时间才说:“这太荒谬了,我不必在 C 中处理这个问题,一定有更好的方法”。

是的,python 是动态类型的,但这并不意味着必须禁止智能提示“您可能想要这个”。 这也不意味着,你必须“处理它”,因为你选择了python。

此外,放弃大量断言函数是一种很好的做法,当事情开始变得复杂时,它会缩短您的开发时间。在出现类型错误之前,您可能正要将变量传递到函数列表的很长一段距离。然后你必须挖很长的路才能找到它。当你决定它是什么时就说它是什么,当出现问题时它会抛出错误。

向其他开发人员展示您正在尝试做的事情也容易得多。我什至在 C 库中看到了这个断言,并且一直想知道为什么他们会在强类型语言中烦恼。但是,现在它更有意义了。我还推测添加断言对性能影响很小(编译器的东西,等等,我会把它留给评论)。