0

not sure if it's possible, but I would like to replicate the idea of @annotations from java in a python class. The goal would be to iterate through all attributes in this class and return those that are "marked" with a custom decorator (e.g. @render). So, in this case, I can define a generic function that renders a generic UI/view template to display only the render attributes from any class.

In this imaginary code, the Item class would be:

class Item:
    def __init__(self):
        self.id = 100
        @render
        self.desc = 'info about'
        @render
        self.title = 'product x'
        self.vender_ref = 'af2k102hv813'

and only title and desc would be shown/returned. What would be the way to achieve this feature? probably I'm way too far from pythonic thinking here, so appreciate any ideas.

Savrige
  • 3,352
  • 3
  • 32
  • 38
  • If you say 'attributes' you strictly mean attributes, but not methods, yes? As far as I know, you can only decorate methods, with the exceptions of `properties` of course. – mapf May 11 '20 at 21:28

2 Answers2

1

I don't think it's possible to decorate attributes the way you are doing it here. One solution could be this.

class Item:
    def __init__(self):
        self.id = 100
        self.desc = "info about"
        self.title = "product x"
        self.vender_ref = "af2k102hv813"
        self.__rendered_attributes__ = ["desc", "title"]


def render_item(i):
    for attribute in i.__rendered_attributes__:
        value = getattr(i, attribute)
        print("attr", attribute, "=", value)


i = Item()
render_item(i)
valentin
  • 2,596
  • 6
  • 28
  • 48
1

You could use something similar as described in this answer and name your attributes that you want to decorate in a specific way. This may be a bit extreme though. I think @valentin's approach is better, but I just wanted to throw another idea out there. Maybe it can be improved upon.

import inspect


class MyClass(object):
    at_a = '12'
    at_b = '34'

    def __init__(self):
        self.at_c = 'test'
        self.get_attr()

    def get_attr(self):
        attributes = inspect.getmembers(
            self, lambda a: not (inspect.isroutine(a))
        )
        attributes = [a for a in attributes if a[0].startswith('at_')]
        print(attributes)

    def myfunc(self):
        return self.at_a


test = MyClass()

Here is another way, making use of redefining the __setattr__ method. This one doesn't work with class attributes though:

class MyClass(object):
    a = '@render', '12'
    b = '34'

    def __init__(self):
        super().__setattr__('__rendered_attributes__', {})
        self.id = 100
        self.desc = '@render', "info about"
        self.title = "product x"
        self.vender_ref = "af2k102hv813"

    def myfunc(self):
        return self.a

    def __setattr__(self, key, value):
        if isinstance(value, tuple) and value[0] == '@render':
            self.__rendered_attributes__[key] = value[-1]
            value = value[-1]
        super().__setattr__(key, value)


test = MyClass()
print(test.__rendered_attributes__, test.a)
mapf
  • 1,906
  • 1
  • 14
  • 40