实现一个 Jedi 可以理解的惰性 属性
Implementing a lazy property that Jedi can understand
我使用 lazy-属性 库 (https://pypi.org/project/lazy-property/) 有一段时间了。它工作得很好,但在我的编辑器中,这些惰性属性不提供任何自动完成。
我的设置是 Atom,使用 ide-python 包 (https://github.com/lgeiger/ide-python), which is driven by the python-language-server (https://github.com/palantir/python-language-server), which uses Jedi (https://github.com/davidhalter/jedi) 进行自动完成。
基本上,这个问题应该可以在任何基于 Jedi 的自动完成上下文中重现。
我一直想知道是否有一种方法可以重写 lazy-属性 中的代码(可能使用类型提示等等),这样 Jedi 就可以理解来自 lazy-属性-装饰方法应该和没有装饰器一样。
这个装饰器的实现其实超级简单,基本上就是:
class lazy_property(property):
def __init__(self, method, fget=None, fset=None, fdel=None, doc=None):
self.method = method
self.cache_name = f"_{self.method.__name__}"
doc = doc or method.__doc__
super().__init__(fget=fget, fset=fset, fdel=fdel, doc=doc)
update_wrapper(self, method)
def __get__(self, instance, owner):
if instance is None:
return self
if hasattr(instance, self.cache_name):
result = getattr(instance, self.cache_name)
else:
if self.fget is not None:
result = self.fget(instance)
else:
result = self.method(instance)
setattr(instance, self.cache_name, result)
return result
有没有人知道 ide我如何重构这个 class 以使 Jedi 明白它应该假定装饰器不会改变 [=31] 的类型=] 值?
非常感谢任何帮助,干杯。
你的问题是 Jedi 无法真正对付 super().__init__(fget=fget, fset=fset, fdel=fdel, doc=doc)
。它并不真正理解你在那里做什么。如果您在该行之后写 self.fget = fget
,Jedi 会理解您的示例。
为了更深入地挖掘,Jedi 尝试了解分支。在您的情况下,它认为 result = self.fget(instance)
的结果是它必须推断的结果,因为 self.fget is not None
推断为 True。它推断为 True
,因为 property
的 self.fget
在类型化存根中定义为 def fget(self) -> Any: ...
,这基本上意味着它肯定存在。因此,这些存根基本上与您的情况下的实际情况有所不同(它们可能在那里略有错误)。
不过也请注意,写入缓存属性是以前做过的事情。最好的实现之一是 @cached_property
in Django
,因此您也可以从那里复制:
https://docs.djangoproject.com/en/2.2/_modules/django/utils/functional/#cached_property
我使用 lazy-属性 库 (https://pypi.org/project/lazy-property/) 有一段时间了。它工作得很好,但在我的编辑器中,这些惰性属性不提供任何自动完成。
我的设置是 Atom,使用 ide-python 包 (https://github.com/lgeiger/ide-python), which is driven by the python-language-server (https://github.com/palantir/python-language-server), which uses Jedi (https://github.com/davidhalter/jedi) 进行自动完成。
基本上,这个问题应该可以在任何基于 Jedi 的自动完成上下文中重现。
我一直想知道是否有一种方法可以重写 lazy-属性 中的代码(可能使用类型提示等等),这样 Jedi 就可以理解来自 lazy-属性-装饰方法应该和没有装饰器一样。
这个装饰器的实现其实超级简单,基本上就是:
class lazy_property(property):
def __init__(self, method, fget=None, fset=None, fdel=None, doc=None):
self.method = method
self.cache_name = f"_{self.method.__name__}"
doc = doc or method.__doc__
super().__init__(fget=fget, fset=fset, fdel=fdel, doc=doc)
update_wrapper(self, method)
def __get__(self, instance, owner):
if instance is None:
return self
if hasattr(instance, self.cache_name):
result = getattr(instance, self.cache_name)
else:
if self.fget is not None:
result = self.fget(instance)
else:
result = self.method(instance)
setattr(instance, self.cache_name, result)
return result
有没有人知道 ide我如何重构这个 class 以使 Jedi 明白它应该假定装饰器不会改变 [=31] 的类型=] 值?
非常感谢任何帮助,干杯。
你的问题是 Jedi 无法真正对付 super().__init__(fget=fget, fset=fset, fdel=fdel, doc=doc)
。它并不真正理解你在那里做什么。如果您在该行之后写 self.fget = fget
,Jedi 会理解您的示例。
为了更深入地挖掘,Jedi 尝试了解分支。在您的情况下,它认为 result = self.fget(instance)
的结果是它必须推断的结果,因为 self.fget is not None
推断为 True。它推断为 True
,因为 property
的 self.fget
在类型化存根中定义为 def fget(self) -> Any: ...
,这基本上意味着它肯定存在。因此,这些存根基本上与您的情况下的实际情况有所不同(它们可能在那里略有错误)。
不过也请注意,写入缓存属性是以前做过的事情。最好的实现之一是 @cached_property
in Django
,因此您也可以从那里复制:
https://docs.djangoproject.com/en/2.2/_modules/django/utils/functional/#cached_property