Python 设计 - 初始化、设置和获取类属性

3

我有一个类,在其中一个方法需要首先验证属性是否存在,否则调用函数计算它。然后,确保该属性不是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 块可能有点过度,因为这个类实际上并不与其他类进行交互,它只接受数据并计算一堆东西。

1
我认为你想要的是这样的:https://dev59.com/_HA75IYBdhLWcg3w8-HZ。让一个类打印自己并不是很符合Python的风格;相反,应该实现`__repr__`和/或`__str__`。 - jonrsharpe
由于每个类对象都应该有属性attr,因此最好使用第一类设计。这样可以澄清这个类的属性是什么。您也可以将attr设置为类属性,每当您使用self.attr访问时,它将吞噬attr的副本,并且您也可以为特定对象设置/获取。 - Roshan
2个回答

0
首先,您可以使用 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__方法才能实现这一点,例如:
class myclass2(): def init(self): pass
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: 来检查我需要的所有内容,这样可以在一行代码中完成,并且不依赖于我是否记得设置属性,但是在阅读了像 这篇文章 这样的帖子之后,我认为 hasattr 不是一个通常安全的选择。 - Pietro Marchesi
在我看来,这真的取决于你的系统。如果你依赖很多第三方类,那么可能不是一个好主意,但如果没有的话,我不明白为什么你不能考虑它。 - pragman
@PietroMarchesi 提醒一下,你需要使用 getattr(self, name, None) is not None:;默认情况下,getattr 会对缺失的属性抛出 AttributeError 异常,你应该通过身份测试来检测是否为 None - jonrsharpe
@jonrsharpe,谢谢你!我不知道它的存在。 - pragman

-1

基于 jonrsharpe 链接的答案,我提供了第三种设计选择。这里的想法是,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的回答应用于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)

输出与上面相同。


1
这不是基于完全相同的东西,而更多地是类似。如果您认为此问题是重复的,请标记它,而不是复制答案。 - jonrsharpe
@jonrsharpe 我不够自信将其归类为重复(如果是的话,为什么你要链接到其他答案而不是自己标记它?),但我会暂时保留此答案,因为我认为它可能仍然有助于 OP(我已将其专门定制为符合他的要求)。如果他接受了另一个答案或者我的回答达到-3,我就会删除它。 - Tagc
1
在相关的问题中,似乎更喜欢稍后发表的答案(https://dev59.com/_HA75IYBdhLWcg3w8-HZ#6849299),但对我来说它更加神秘。 - Pietro Marchesi
@PietroMarchesi 感谢您指出这一点,这是一个非常聪明的答案。我已经更新了我的帖子,展示了如何将该解决方案应用于您的问题,并且它可以按预期工作。 - Tagc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接