gpt4 book ai didi

python - 装饰器可以访问类的私有(private)成员吗?

转载 作者:太空狗 更新时间:2023-10-30 02:46:50 25 4
gpt4 key购买 nike

我正在编写一个解析 HTML 的类,以便为网页上的配置文件提供接口(interface)。它看起来像这样:

class Profile(BeautifulSoup):
def __init__(self, page_source):
super().__init__(page_source)

def username(self):
return self.title.split(':')[0]

除了更复杂和耗时。因为我知道底层配置文件在 Profile 对象的生命周期内不会发生变化,所以我认为这是缓存结果以避免重新计算已知值的好地方.我用装饰器实现了它,结果如下所示:

def cached_resource(method_to_cache):
def decorator(self, *args, **kwargs):
method_name = method_to_cache.__name__

try:
return self._cache[method_name]
except KeyError:
self._cache[method_name] = method_to_cache(self, *args, **kwargs)
return self._cache[method_name]

return decorator


class Profile(BeautifulSoup):
def __init__(self, page_source):
super().__init__(page_source)
self._cache = {}

@cached_resource
def username(self):
return self.title.split(':')[0]

当我将此代码提供给 pylint 时,它会提示 cached_resource 可以访问客户端类的 protected 变量。

我意识到公共(public)和私有(private)之间的区别在 Python 中并不是什么大问题,但我仍然很好奇——我在这里做了什么坏事吗?让装饰器依赖于与其关联的类的实现细节是一种糟糕的风格吗?

编辑:我不清楚 Duncan 的答案中的闭包是如何工作的,所以这可能有点困惑,但这会是一个更简单的解决方案吗?

def cached_resource(method_to_cache):
def decorator(self, *args, **kwargs):
method_name = method_to_cache.__name__

try:
return self._cache[method_name]
except KeyError:
self._cache[method_name] = method_to_cache(self, *args, **kwargs)
except AttributeError:
self._cache = {}
self._cache[method_name] = method_to_cache(self, *args, **kwargs)
finally:
return self._cache[method_name]

return decorator

最佳答案

这有点代码味道,我想我会同意 pylint 的观点,尽管它非常主观。

你的装饰器看起来像是一个通用的装饰器,但它与类的内部实现细节密切相关。如果您尝试从另一个类使用它,则在 __init__ 中没有初始化 _cache 它将无法工作。我不喜欢的链接是类和装饰器之间共享名为“_cache”的属性的知识。

您可以将 _cache 的初始化从 __init__ 移到装饰器中。我不知道这是否有助于安抚 pylint,它仍然需要类了解并避免使用该属性。这里(我认为)一个更简洁的解决方案是将缓存属性的名称传递给装饰器。这应该彻底打破联系:

def cached_resource(cache_attribute):
def decorator_factory(method_to_cache):
def decorator(self, *args, **kwargs):
method_name = method_to_cache.__name__
cache = getattr(self, cache_attribute)
try:
return cache[method_name]
except KeyError:
result = cache[method_name] = method_to_cache(self, *args, **kwargs)
return result

return decorator
return decorator_factory


class Profile(BeautifulSoup):
def __init__(self, page_source):
super().__init__(page_source)
self._cache = {}

@cached_resource('_cache')
def username(self):
return self.title.split(':')[0]

如果你不喜欢大量重复属性名称的装饰器调用,那么:

class Profile(BeautifulSoup):
def __init__(self, page_source):
super().__init__(page_source)
self._cache = {}

with_cache = cached_resource('_cache')

@with_cache
def username(self):
return self.title.split(':')[0]

编辑:Martineau 认为这可能有点矫枉过正。如果您实际上不需要单独访问类内的 _cache 属性(例如,有一个缓存重置方法),则可能是这样。在那种情况下,您可以完全在装饰器中管理缓存,但如果您打算这样做,则根本不需要实例上的缓存字典,因为您可以将缓存存储在装饰器中,并将键存储在 简介实例:

from weakref import WeakKeyDictionary

def cached_resource(method_to_cache):
cache = WeakKeyDictionary()
def decorator(self, *args, **kwargs):
try:
return cache[self]
except KeyError:
result = cache[self] = method_to_cache(self, *args, **kwargs)
return result
return decorator

class Profile(BeautifulSoup):
def __init__(self, page_source):
super().__init__(page_source)
self._cache = {}

@cached_resource
def username(self):
return self.title.split(':')[0]

关于python - 装饰器可以访问类的私有(private)成员吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18692791/

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