【问题标题】:Python lazy attributes that don't eval on hasattr()不在 hasattr() 上评估的 Python 惰性属性
【发布时间】:2009-08-17 07:15:58
【问题描述】:

是否可以制作一个装饰器,使属性变得惰性,当您尝试使用hasattr() 访问它时不会评估它?我想出了如何让它变得懒惰,但hasattr() 让它过早地评估。例如,

class lazyattribute:
    # Magic.

class A:
    @lazyattribute
    def bar(self):
      print("Computing")
      return 5

>>> a = A()
>>> print(a.bar)
'Computing'
5
>>> print(a.bar)
5
>>> b = A()
>>> hasattr(b, 'bar') 
'Computing'
5
# Wanted output: 5

【问题讨论】:

    标签: python decorator lazy-evaluation


    【解决方案1】:

    这可能很难做到。来自hasattr documentation

    hasattr(对象,名称)

    参数是一个对象和一个字符串。如果字符串是对象属性之一的名称,则结果为 True,否则为 False。 (这是通过调用 getattr(object, name) 并查看它是否引发异常来实现的。

    由于属性可以通过__getattr__ 方法动态生成,因此没有其他方法可以可靠地检查它们的存在。对于您的特殊情况,也许明确地测试字典就足够了:

    any('bar' in d for d in (b.__dict__, b.__class__.__dict__))
    

    【讨论】:

      【解决方案2】:

      到目前为止似乎没有人解决的是,也许最好的办法是不要使用hasattr()。相反,请选择 EAFP(请求宽恕比许可更容易)。

      try:
          x = foo.bar
      except AttributeError:
          # what went in your else-block
          ...
      else:
          # what went in your if hasattr(foo, "bar") block
          ...
      

      这显然不是一个直接替代品,您可能需要稍微移动一些东西,但这可能是“最好的”解决方案(当然是主观的)。

      【讨论】:

      • 我同意,如果我正在重构,我不会在这种情况下依赖 hasattr,但我可能更愿意在任何情况下避免不一定存在的属性。带有has_key / __contains__ 的类似字典的对象会很好且明确。
      【解决方案3】:

      问题在于hasattr 使用getattr,因此当您使用hasattr 时,您的属性总是会被评估。如果您发布lazyattribute 魔术的代码,希望有人可以建议一种替代方法来测试不需要hasattrgetattr 的属性的存在。查看hasattr的帮助:

      >>> help(hasattr)
      Help on built-in function hasattr in module __builtin__:
      
      hasattr(...)
          hasattr(object, name) -> bool
      
          Return whether the object has an attribute with the given name.
          (This is done by calling getattr(object, name) and catching exceptions.)
      

      【讨论】:

        【解决方案4】:

        我很好奇你为什么需要这样的东西。如果hasattr 最终调用了您的“计算函数”,那就这样吧。无论如何,您的财产需要有多懒惰?

        不过,通过检查调用函数的名称,这是一种相当不雅的方法。它可能编码得更好一些,但我认为它不应该被认真使用。

        import inspect
        
        class lazyattribute(object):
            def __init__(self, func):
                self.func = func
        
            def __get__(self, obj, kls=None):
                if obj is None or inspect.stack()[1][4][0].startswith('hasattr'):
                    return None
                value = self.func(obj)
                setattr(obj, self.func.__name__, value)
                return value
        
        class Foo(object):
            @lazyattribute
            def bar(self):
                return 42
        

        【讨论】:

        • 我确实尝试过,但检查堆栈没有帮助。堆栈只有一个 get 调用,因为它是第一个元素,等等。
        • 蒂姆,这对我有用。你能发布你的lazyattribute代码吗,也许是别的问题?
        【解决方案5】:

        这个修复有点老套,但它包含以下内容

        1. 重命名 hasattr(比如 _hasattr)
        2. 重新绑定 hasattr 如下:

        
           def hasattr(obj, name):
             try:
               return obj._hasattr(name) or _hasattr(obj, name)
             except:
               return _hasattr(obj, name)
        
        1. 通过检查一些填充了所有惰性属性 names 的数据结构(即数组)来实现类方法 _hasattr(对于您会说的数组:name in lazyAttrArray)

        2. 最终以某种方式让您的@lazyattribute 装饰器将项目添加到某种结构中(例如我们上面提到的数组),然后当您调用 _hasattr 然后您查看该结构

          • 这是我不太确定您将如何实施的步骤,因为我还没有创建自己的装饰器

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-08
          • 2013-03-11
          • 2017-01-18
          • 2018-11-11
          • 2015-05-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多