-1

I have defined a base class somewhere, that is extendable by some user.

Now i want to access variables potentially defined by that user in his subclasses so that i can do something with it.

My attempt to solve this problem is by using metaclasses

Here's the example set up:

class Meta(type):
    def __new__(cls, name, bases, attrs):
        clsobj = super().__new__(cls, name, bases, attrs)
        print(f'the name of this class is {clsobj.NAME.upper()}')
        return clsobj


class Base(metaclass=Meta):
    pass

class C1(Base):
    NAME = "C1"

if __name__ == "__main__":
    c1 = C1()

But i'm getting the following error:

AttributeError: type object 'Base' has no attribute 'NAME'

N.B. The Base class shouldn't neither know about its sublcass variables, but should just access them.

QuentinC
  • 12,311
  • 4
  • 24
  • 37
moctarjallo
  • 1,479
  • 1
  • 16
  • 33

2 Answers2

0

One possible solution is (bypassing the error that occurs during construction of Base class):

class Meta(type):
    def __new__(cls, name, bases, attrs):
        clsobj = super().__new__(cls, name, bases, attrs)
        if 'NAME' not in vars(clsobj):  # add these lines
            pass                        # add these lines (bypass mechanism)
        else:                           # add these lines
            print(f'the name of this class is {clsobj.NAME.upper()}')          
        return clsobj                   


class Base(metaclass=Meta):
    pass

class C1(Base):
    NAME = "C1"

if __name__ == "__main__":
    c1 = C1()

In case of multiple variables of interest, the if ... pass ... else .. becomes:

clsattrs = vars(clsobj)
if 'VAR1' not in clsattrs or 'VAR2' not in clsattrs or 'VAR3' not in clsattrs ...:
    pass
else:
    function_on(VAR1)
    function_on(VAR2)
    function_on(VAR3)
    ....
`

That's more simple and ccncise i guess..

But i'm still waiting for a possibly better solution !

moctarjallo
  • 1,479
  • 1
  • 16
  • 33
0

Building up on OP's self-answer, but more generalized (so no need to guess the name of attributes):

class Meta(type):
    def __new__(cls, name, bases, attrs):
        clsobj = super().__new__(cls, name, bases, attrs)
        for attr, value in vars(clsobj).items():
            if not attr.startswith('__'):  # we probably want to ignore 'magic' attributes
                print(value)
        return clsobj
DeepSpace
  • 78,697
  • 11
  • 109
  • 154
  • Actually i want to perform some computation on those attributes, not just print them (this was only for a simplification purpose). So i really need to know the name of the variable so i can do something with it. For example here i'd need to append the NAME variable to some other string and get a result. – moctarjallo Aug 27 '20 at 19:32
  • @moctarjallo What is stopping you from doing something with the name of the variable, just like I'm doing when checking if it starts with `__`? – DeepSpace Aug 27 '20 at 19:34
  • i see. But you are `looping` over all variables and then probably do `if attr == 'NAME'` right ? Cool. But you'd need to know what attribute you're looking for ! So I think my solution might be better(without a loop) since i'm just directly checking `if 'NAME' in attrs`. – moctarjallo Aug 27 '20 at 19:40
  • @moctarjallo The question says "variables potentially defined by that user". Nothing specific about `NAME`. All my answer is doing is offering a more generalized approach (as it says) by not hard-coding potential, unknown beforehand values. However, if you are interested in some specific ones, my approach still offers more flexibility as you will not need 5 more `if-else` blocks, just add `and attr in ('x', 'y', 'z')` to the existing `if` check – DeepSpace Aug 27 '20 at 19:48
  • i understand. But what if i'm looking for more than one variable? I'd need then to have multiple `if` statements in the `for` loop to retrieve the variables i'm looking for from `attrs`. While in my solution you would only need one line of `if` statement but multiple conditions (checking if each of those variables are present in attrs). Nevertheless i understand that the question didn't precisely specify the multiplicity of variables; i only needed the solution for one variable which would be enough for me to know how how to generalize to multiple variables ;) – moctarjallo Aug 27 '20 at 20:12
  • @moctarjallo `" I'd need then to have multiple if statements in the for loop"` no, you won't, and I explained how you can do that in my previous comment. – DeepSpace Aug 27 '20 at 20:14
  • Oh let me see again ;-) – moctarjallo Aug 27 '20 at 20:15
  • No! the `and attr in ('x', 'y', 'z')` wouldn't work either because as i said i need to retrieve those `'x', 'y', 'z'` variables and do something with it. But here you are only checking if they exist. I need more than that. I need to get **ALL** those attributes and do something with **EACH** of them. – moctarjallo Aug 27 '20 at 20:18
  • @moctarjallo that is in fact wrong. Please try to execute an example and see what's happening. – DeepSpace Aug 27 '20 at 20:19
  • I tried. Believe me. Your code works! It is just less elegant i think ;-) i don't wanna loop all the attributes of the class. Just check directly if the ones i'm interested are present. – moctarjallo Aug 27 '20 at 22:14
  • @moctarjallo If you think that 5 `if ... else: pass` blocks are more elegant than an `in` check then there is nothing more I can say on the topic. Good luck :) – DeepSpace Aug 27 '20 at 22:18
  • No! You don't understand. I'm not gonna use more than one `if ... else: pass`. Instead i'm gonna do something like \n `clsattrs = vars(clsobj) if 'VAR1' not in clsattrs or 'VAR2' not in clsattrs or 'VAR3' not in clsattrs ...: pass else: function_on(VAR1) function_on(VAR2) function_on(VAR3) .... ` That's more simple and ccncise i guess.. – moctarjallo Aug 27 '20 at 22:25
  • I have updated my answer to add the code above in a more formatted way. – moctarjallo Aug 27 '20 at 22:30