Accessing static fields from the decorated class
Asked Answered
S

2

6

Full code example:

def decorator(class_):
    class Wrapper:
        def __init__(self, *args, **kwargs):
            self.instance = class_(*args, **kwargs)

        @classmethod
        def __getattr__(cls, attr):
            return getattr(class_, attr)
    return Wrapper


@decorator
class ClassTest:

    static_var = "some value"


class TestSomething:

    def test_decorator(self):
        print(ClassTest.static_var)
        assert True

When trying to execute test, getting error:

test/test_Framework.py F
test/test_Framework.py:37 (TestSomething.test_decorator)
self = <test_Framework.TestSomething object at 0x10ce3ceb8>

    def test_decorator(self):
>       print(ClassTest.static_var)
E       AttributeError: type object 'Wrapper' has no attribute 'static_var'

Is it possible to access static fields from the decorated class?

Syncytium answered 19/12, 2017 at 16:56 Comment(6)
Can you show the code where you are getting error? It is not immediately obvious from the code snippets above where the problem is.Condiment
It would be helpful if you provided all the code which causes the error in a single snippet. It took me several minutes to understand what you are doing.Sloth
Please edit your question instead of posting a comment so that you can format the code properly.Sloth
Posted full code.Syncytium
The TestSomething class is not necessary for this question, so I removed it.Sloth
__getattr__ should not be used in class context. The issue is obvious. The Wrapper is not derived from ClassTestLuxate
M
4

While the answer from @martineau probably better addresses the specific issue you are trying to solve, the more general approach might be to use create a metaclass in order to redefine the instance method __getattr__ on a type instance (and classes are instances of type).

def decorator(class_):
    class WrapperMeta(type):
        def __getattr__(self, attr):
            return getattr(class_, attr)

    class Wrapper(metaclass=WrapperMeta):
        def __init__(self, *args, **kwargs):
            self.instance = class_(*args, **kwargs)

    return Wrapper

This allows the attribute look-up on the class itself to be passed through WrapperMeta.__getattr__.

Maneating answered 19/12, 2017 at 18:10 Comment(2)
@Oleh Kuzovkov: IMO this answer is better than mine—so you should accept it. Jared: Meta interesting!Nitrobacteria
The main purpose of this question was to bypass fields calls to the decorated class. Inheritance solved the problem for sure, however this solution is really more suitable for this case. Thank you guys!Syncytium
N
1

You can get it to work by making the decorator create a class derived from the one being decorated.

Here's what I mean:

def decorator(class_):
    class Wrapper(class_):
        def __init__(self, *args, **kwargs):
            self.instance = super().__init__(*args, **kwargs)

    return Wrapper

@decorator
class ClassTest:
    static_var = "some value"

print(ClassTest.static_var)  # -> some value
Nitrobacteria answered 19/12, 2017 at 17:16 Comment(7)
Can you explain why the original code doesn't work? As a python newbie, I am unfamiliar with the __getattr__() method. Why does it not work like the OP expects it to?Sloth
A class doesn't have this method __getattr__(), that's all. You could use getattr(class_, name) instead, that works I think.Pulsate
@Code-Apprentice: Because the __getattr__() defined in the OP's code is a Wrapper classmethod. You can learn more about __getattr__() by reading its online documentation.Nitrobacteria
@Nitrobacteria - In order to avoid inheriting from class_ - Can this be done with metaclass to override the Wrapper getattr (some MetaWrapper)?Bananas
After decoration, the name ClassTest refers to the class Wrapper, correct? So shouldn't Classtest.static_var call __getattr__() in Wrapper?Sloth
@UriHoenig: A metaclass might be able to avoid inheriting from class_ by copying all its static attributes into its class instances. Try it and see.Nitrobacteria
@Code-Apprentice: The __getattr__() in the OP's Wrapper class isn't called when ClassTest.static_var is referenced. __getattr__() is for customizing the attribute access of class instances, so it only applies to them, not to class attributes (so it's unclear what making it a @classmethod would even mean).Nitrobacteria

© 2022 - 2024 — McMap. All rights reserved.