简体   繁体   中英

python: mixing @cached_property and __getattr__ properly

Mixing functools.cached_property and __getattr__ works well, except... when Exception raises:

from functools import cached_property

class A:
    def __init__(self):
        self.exists = 1

class B:
    def __init__(self, a):
        self.a = a
        self.b = 1

    def __getattr__(self, name):
        return getattr(self.a, name)   

    @cached_property
    def cached_func(self):
        return self.not_exists
    
    def func(self):
        return self.not_exists

a = A()
b = B(a)

b.cached_func  # 'A' object has no attribute 'cached_func'
b.func() # -> 'A' object has no attribute 'not_exists'

The Exception raised when using cached_property is not explicit at all -- if not wrong. Is there a way to work around this?

OK, I found a way by adding a new decorator that will catch AttributeError before its propagation.

I created a specific Exception to better handle it during runtime.

from functools import cached_property, wraps
 

class NestedAttributeError(RuntimeError):
    """Specific exception for describing nested __getattr__ error"""
    
def intercept_AttributeError(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except AttributeError as exc:
            raise NestedAttributeError(exc)

    return wrapped


class A:
    def __init__(self):
        self.exists = 1
        
    def __repr__(self):
        return "AObj"
    

class B:
    def __init__(self, a):
        self.a = a
        self.b = 1
    
    def __repr__(self):
        return "BObj"

    def __getattr__(self, name):
        #return getattr(self.a, name)   
        try:
            attr = getattr(self.a, name)   
        except AttributeError as exc:
            raise NestedAttributeError(exc)
        return attr

    @cached_property
    @intercept_AttributeError
    def cached_func(self):
        a = self.not_exists
        return a + 1
    
    def func(self):
        return self.not_exists

b = B(A())

b.cached_func  # -> NestedAttributeError: 'A' object has no attribute 'not_exists'
b.func()  # -> NestedAttributeError: 'A' object has no attribute 'not_exists'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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