简体   繁体   English

计算属性(如果不存在)

[英]Calculate attribute if it doesn't exist

I am trying to access an attribute that shouldn't be created in the __init__ method of my class but can be calculated by calling another method. 我试图访问不应该在我的类的__init__方法中创建的属性,但可以通过调用另一个方法来计算。 I am trying to make it so that if I try to access the attribute and it does not exist it will be automatically calculated. 我试图这样做,如果我尝试访问该属性,它不存在,它将自动计算。 However, I don't want it to be recalculated if the attribute does exist, even if the value would be different. 但是,如果属性确实存在,即使值不同,我也不希望重新计算它。 For example: 例如:

class SampleObject(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def calculate_total(self):
        self.total = self.a + self.b

sample = SampleObject(1, 2)
print sample.total   # should print 3
sample.a = 2
print sample.total   # should print 3
sample.calculate_total()
print sample.total   # should print 4

My best solution so far is to make a get_total() method that does what I need. 到目前为止,我最好的解决方案是创建一个get_total()方法来完成我需要的工作。

class SampleObject2(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def calculate_total(self):
        self.total = self.a + self.b

    def get_total(self):
        if hasattr(self, 'total'):
            return self.total
        else:
            self.calculate_total()
            return self.total

sample2 = SampleObject2(1, 2)
print sample2.get_total() # prints 3
sample2.a = 2
print sample2.get_total() # prints 3
sample2.calculate_total()
print sample2.get_total() # prints 4

This is working fine, but I have read that using getters in python is discouraged and I was hoping to avoid calling this function every time I wanted to access the attribute. 这工作正常,但我已经知道在python中使用getter是不受欢迎的,我希望每次我想访问该属性时都避免调用此函数。 Is this my best solution, or is there a cleaner, more pythonic way of doing this? 这是我最好的解决方案,还是有更清洁,更pythonic的方式来做到这一点?

This is an example that I made up. 这是我编写的一个例子。 In my actual problem, calculate_total() is a time consuming process that won't necessarily need to be called. 在我的实际问题中,calculate_total()是一个耗时的过程,不一定需要调用。 So I don't want to execute it in the init method. 所以我不想在init方法中执行它。

You want to use the @property decorator. 你想使用@property装饰器。 Create a method, that will be accessed like a normal attribute, that does lazy computation: 创建一个方法,它将像普通属性一样访问,执行延迟计算:

class SampleObject:

    def __init__(self):
        # ...
        self._total = None

    @property
    def total(self):
        """Compute or return the _total attribute."""
        if self._total is None:
            self.compute_total()

        return self._total

Pyramid (a Web framework) comes with a reify decorator that is similar to property (shown by Austin Hastings) but it works a little differently: the function is only executed once, and after that, the value returned by the function is always used. Pyramid(一个Web框架)带有一个类似于propertyreify装饰器(由Austin Hastings显示)但它的工作方式略有不同:该函数只执行一次,之后,函数返回的值总是被使用。 It essentially does what Austin's code does, but without having to use a separate attribute: it's a generalization of that pattern. 它本质上是做奥斯汀的代码所做的,但不必使用单独的属性:它是该模式的概括。

You probably don't want to use a whole Web framework just for this one decorator, so here is an equivalent one I wrote: 您可能不希望仅为这一个装饰器使用整个Web框架,因此这是我写的等效文件:

import functools

class Descriptor(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, inst, type=None):
        val = self.func(inst)
        setattr(inst, self.func.__name__, val)
        return val

def reify(func):
    return functools.wraps(func)(Descriptor(func))

Usage: 用法:

class ReifyDemo:
    @reify
    def total(self):
        """Compute or return the total attribute."""
        print("calculated total")
        return 2 + 2    # some complicated calculation here

r = ReifyDemo()
print(r.total)     # prints 'calculated total 4' because the function was called
print(r.total)     # prints just '4` because the function did not need to be called

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM