gpt4 book ai didi

Python 的新 `functools.cached_property` 错误或限制?

转载 作者:行者123 更新时间:2023-12-03 16:21:14 28 4
gpt4 key购买 nike

从 Python 3.8 开始,functools有一个 cached_property .
我一直在使用类似的 lazyprop装饰器基于 Beazley 的食谱(下面的代码),但是当我用内置函数替换时,我遇到了问题。这是其中之一。

当我在类定义中使用装饰器时,使用 @运营商,它不会提示。

但是如果我将它与 setattr 一起使用,我得到:

TypeError: Cannot use cached_property instance without calling __set_name__ on it.

Beazley 的简单版本虽然很好用。

from functools import cached_property

class lazyprop:
"""Based on code from David Beazley's "Python Cookbook"."""
def __init__(self, func):
self.__doc__ = getattr(func, '__doc__')
self.func = func

def __get__(self, instance, cls):
if instance is None:
return self
else:
value = instance.__dict__[self.func.__name__] = self.func(instance)
return value

class D:
def __init__(self, greeting='Hello'):
self.greeting = greeting

def greet(self):
return self.greeting + " world!"


# Beazley's version works...
D.greet = lazyprop(greet)
assert D().greet == "Hello world!"

# ... but the builtin version will fail
D.greet = cached_property(greet)
# D().greet # this will fail

最佳答案

TL;DR 缓存是实例dict本身,需要属性名作为key。所选择的设计施加了限制,但(IMO)这是一个很好的折衷方案。 lazyprop不是线程安全的,或者至少可以调用 self.func在多线程环境中,这不仅仅是必要的。

首先,它是 documented那个__set_name__仅当赋值发生在类型创建的上下文中时才调用。文档中的注释(根据您的示例改编)显示了您需要执行的操作:调用 __set_name__你自己。

class D:
def __init__(self, greeting='Hello'):
self.greeting = greeting

def greet(self):
return self.greeting + " world!"

D.greet = cached_property(greet)
D.greet.__set_name__(D, 'greet') # sets D.greet.attrname = "greet" for later use

assert D().greet == "hello world!"

为什么需要属性名称? cached_property.__set__未定义,因此给出 d = D() , d.greet将首先查找名为 greet 的实例属性在 d.__dict__ .如果没有找到,则 D.greet.__get__(d, D)将被调用。该函数基本上做了三件事:它调用原始 greet如果需要计算该值,则将其保存到具有相同名称的新实例属性中,然后返回计算出的值。

“等等”,你问,“你是什么意思,‘如果需要’?你不是说 D.greet.__get__ 仅在实例属性不存在时才被调用吗?”是的,但是在多线程环境中,您不知道另一个线程是否也可能正在执行 D.greet.__get__同时。为了防止竞争条件, __get__完成以下步骤(如果您愿意,可以按照 in the code 进行操作):
  • 检查具有相同名称的实例属性,以防它是在当前调用开始后在另一个线程中创建的。
  • 如果没有,请尝试获取锁,以便我们可以自己创建实例属性。
  • 获得锁后,再次查找实例属性,以防在我们等待锁时有人创建了它。
  • 如果实例属性仍然不存在,我们可以安全地自己创建它。
  • 最后,我们是否从实例属性中提取了一个值,或者调用了原始的 greet函数自己,我们可以返回值。

  • 考虑到所有这些,我将其称为限制而不是错误,而是一个很容易解决的限制。这种实现可能比试图不依赖属性本身的名称来维护必要映射的实现更简单。

    关于Python 的新 `functools.cached_property` 错误或限制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62160411/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com