【问题标题】:Python design - initializing, setting, and getting class attributesPython 设计 - 初始化、设置和获取类属性
【发布时间】:2017-01-07 13:42:04
【问题描述】:

我有一个类,其中方法首先需要验证属性是否存在,否则调用函数来计算它。然后,确保属性不是None,对它执行一些操作。我可以看到两种略有不同的设计选择:

class myclass():
    def __init__(self):
        self.attr = None

    def compute_attribute(self):
        self.attr = 1

    def print_attribute(self):
        if self.attr is None:
            self.compute_attribute()
        print self.attr

class myclass2():
    def __init__(self):
        pass

    def compute_attribute(self):
        self.attr = 1
        return self.attr

    def print_attribute(self):
        try:
            attr = self.attr
        except AttributeError:
            attr = self.compute_attribute()
        if attr is not None:
            print attr

在第一个设计中,我需要确保提前将所有类属性设置为None,这样可以变得冗长但也可以明确对象的结构。

第二种选择似乎是使用更广泛的一种。但是,就我的目的(与信息论相关的科学计算)而言,在任何地方使用 try except 块可能有点矫枉过正,因为这个类并没有真正与其他类交互,它只需要数据并计算一堆东西。

【问题讨论】:

  • 我想你想要这样的东西:stackoverflow.com/questions/3012421/…。让一个类自己打印并不是很pythonic。改用__repr__ 和/或__str__
  • 由于每个类对象都应该有attr属性,所以最好使用一流的设计。这澄清了这个类的属性是什么。您也可以将 attr 设置为类属性,每当您使用 self.attr 访问时,它都会吞下 attr 的副本,您也可以只为特定对象设置/获取。

标签: python class oop scientific-computing class-attributes


【解决方案1】:

首先,你可以使用hasattr来检查一个对象是否有属性,如果属性存在则返回True

hasattr(object, attribute) # will return True if the object has the attribute

其次,您可以在 Python 中自定义属性访问,您可以在这里阅读更多内容:https://docs.python.org/2/reference/datamodel.html#customizing-attribute-access

基本上,你重写 __getattr__ 方法来实现这一点,所以像:

类 myclass2(): def 初始化(自我): 通过

def compute_attr(self):
    self.attr = 1
    return self.attr

def print_attribute(self):
    print self.attr

def __getattr__(self, name):
    if hasattr(self, name) and getattr(self, name)!=None:
        return getattr(self, name):
    else:
        compute_method="compute_"+name; 
        if hasattr(self, compute_method):
            return getattr(self, compute_method)()

确保你只使用getattr 来访问__getattr__ 中的属性,否则你将得到无限递归

【讨论】:

  • if hasattr(self, name) and getattr(self, name)!=None: 是我最初的想法——它会在一行中检查我需要的所有内容,它不取决于我是否记得设置属性,但在阅读了@987654322 之类的帖子之后@我的印象是 hasattr 通常不是一个安全的选择。
  • 恕我直言,这真的取决于你的系统,如果你依赖很多第三方类,那么可能不是一个好主意,但如果不是,那么我不明白你为什么不能考虑一下。
  • @PietroMarchesi 仅供参考,您需要getattr(self, name, None) is not None:;默认情况下,getattr 会为缺少的属性抛出 AttributeError,您应该通过身份测试 None
  • @jonrsharpe 谢谢!我不知道它存在。
【解决方案2】:

基于the answer jonrsharpe linked,我提供了第三种设计选择。这里的想法是MyClass 的客户端或MyClass 本身的代码根本不需要特殊的条件逻辑。取而代之的是,将装饰器应用于执行(假设昂贵的)属性计算的函数,然后存储该结果。

这意味着昂贵的计算是惰性完成的(仅当客户端尝试访问该属性时)并且只执行一次。

def lazyprop(fn):
    attr_name = '_lazy_' + fn.__name__

    @property
    def _lazyprop(self):
        if not hasattr(self, attr_name):
            setattr(self, attr_name, fn(self))
        return getattr(self, attr_name)

    return _lazyprop


class MyClass(object):
    @lazyprop
    def attr(self):
        print('Generating attr')
        return 1

    def __repr__(self):
        return str(self.attr)


if __name__ == '__main__':
    o = MyClass()
    print(o.__dict__, end='\n\n')
    print(o, end='\n\n')
    print(o.__dict__, end='\n\n')
    print(o)

输出

{}

Generating attr
1

{'_lazy_attr': 1}

1

编辑

Cyclone's answer 应用于 OP 的上下文:

class lazy_property(object):
    '''
    meant to be used for lazy evaluation of an object attribute.
    property should represent non-mutable data, as it replaces itself.
    '''

    def __init__(self, fget):
        self.fget = fget
        self.func_name = fget.__name__

    def __get__(self, obj, cls):
        if obj is None:
            return None
        value = self.fget(obj)
        setattr(obj, self.func_name, value)
        return value


class MyClass(object):
    @lazy_property
    def attr(self):
        print('Generating attr')
        return 1

    def __repr__(self):
        return str(self.attr)


if __name__ == '__main__':
    o = MyClass()
    print(o.__dict__, end='\n\n')
    print(o, end='\n\n')
    print(o.__dict__, end='\n\n')
    print(o)

输出与上面相同。

【讨论】:

  • 这不是基于那么多完全一样。如果您认为这个问题是重复的,请将其标记出来,而不是复制答案。
  • @jonrsharpe 我没有足够的信心将其归类为重复项(如果是,为什么你链接到另一个答案而不是自己标记它?),但我会留下这个答案暂时,因为我认为它可能仍然对 OP 有帮助(我已经根据他的要求专门定制了它)。如果他接受另一个答案或者我的答案是 -3,我会删除它。
  • 在链接的问题上,似乎更晚一些 - 但对我来说更神秘 - answer 是首选。
  • @PietroMarchesi 感谢您指出这一点,这是一个非常聪明的答案。我已经更新了我的帖子,以展示该解决方案如何也可以应用于您的问题,并且可以按预期工作。
猜你喜欢
  • 2012-12-15
  • 2020-12-28
  • 2016-07-17
  • 2017-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多