4

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

某些方法返回了某个类的实例。但我无法获得实例方法的智能感知。

例如:

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'

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

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

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

在此处输入图像描述

4

3 回答 3

2

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

在 C# 等静态类型语言中,方法使用其类型签名进行注释。(顺便说一句:在某些系统中,类型检查器可以推断出类型。)编译器知道函数的返回类型,以及参数应该具有的类型,而无需运行您的代码,因为您将类型写下来了!这使您的工具不仅可以检查程序的类型,还可以建立有关程序中方法及其类型的元数据;Intellisense 通过查询从程序文本中获取的元数据来工作。


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

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

有时spam返回一个字符串;有时它会返回一个整数。Intellisense 应该在调用的返回值上显示哪些方法spam

这个类有哪些可用的属性?

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

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

在这些情况下,没有正确答案。您也许可以自愿进行一些猜测(例如,您可以在 的返回值上显示一个包含str和的方法的列表),但您不能给出始终正确的建议。intspam


因此,用于 Python 的 Intellisense 工具被简化为最佳猜测。在您提供的示例中,您的 IDE 对返回类型的了解不够,get_sheet_by_name无法为您提供有关wsheet1. 但是,它确实知道 的类型,wsheet2因为您只是将它实例化为Worksheet. 在您的第二个示例中,Intellisense 只是f1通过检查其源代码对返回类型进行(正确)猜测。

顺便说一句,像 IPython 这样的交互式 shell 中的自动完成更可靠。这是因为 IPython 实际运行您键入的代码。它可以判断对象的运行时类型是什么,因为分析是在运行时发生的。

于 2016-01-23T12:04:22.333 回答
1

好吧,从技术上讲,在 Python 中,方法可能会返回任何内容,并且操作的结果仅在操作完成时才定义。

考虑这个简单的函数:

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'>

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

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

于 2016-01-23T11:58:02.223 回答
0

您可以使用断言来告诉智能感知您希望它是什么类。当然,如果不是,现在它会报错,但这是一件好事。

assert isinstance(my_variable, class_i_want_it_to_be)

这将为您提供自动完成和 ctrl 单击以跳转到您一直在寻找的功能。(至少这是它现在在 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 库中看到了这种断言,并且一直想知道为什么它们会在强类型语言中受到困扰。但是,现在它变得更有意义了。我还推测添加断言对性能的影响很小(编译器的东西,等等,我会把它留给评论)。

于 2022-01-30T08:08:49.017 回答