【问题标题】:Intellisense for returned python object's methods用于返回 python 对象方法的 Intellisense
【发布时间】:2016-01-23 11:40:42
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: python intellisense static-analysis tooling


    【解决方案1】:

    这是因为 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 的实例显示什么?

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


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

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

    【讨论】:

      【解决方案2】:

      嗯,从技术上讲,在 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

      【讨论】:

        【解决方案3】:

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

        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 库中看到了这种断言,并且一直想知道为什么它们会在强类型语言中受到困扰。但是,现在它变得更有意义了。我还推测添加断言对性能的影响很小(编译器的东西,等等,我会把它留给 cmets)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-03-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-18
          相关资源
          最近更新 更多