2

Python 3.7:

I have a class definition, where a class attribute is assigned a value based on the class name, and the value is used in a decorator within the class.

I'm looking for a pronoun for "the class currently being defined".

Here's an example:

class MyClass:

    # NameError: name '__class__' is not defined
    class_logger = find_logger(__class__.__name__)

    # __init__ is not important
    def __init__(self):
        # This works!
        self.logger = find_logger(__class__.__name__)

    @mydecorator(class_logger)
    # mymethod is not important
    def mymethod(self):
        return 1

What is a suitable pronoun for "the class currently being defined"?

(This is similar to How to get the current Python class name in __init__ regardless of the class of "self"?, but there, the question was "in the definition of init", and at the time was for 2.7. Python 3.x solved that problem.)

Quantum Mechanic
  • 625
  • 1
  • 6
  • 20
  • 1
    Interesting question. I don't think it's necessarily guaranteed that the class object exists in some kind of partially-initialized state while the class block hasn't yet completely executed. So there may not be any equivalent of `__class__`. Getting the name and only the name might be more possible, though... – Kevin Apr 16 '19 at 17:10
  • 1
    The class does not exist while the class block is being executed. There is no class object to refer to – juanpa.arrivillaga Apr 16 '19 at 17:14

2 Answers2

4

It isn't guaranteed that the class object exists and is accessible to the user when the class definition hasn't finished evaluating. Even if it was accessible, it might not be initialized to the extent that you could do anything useful with it.

That said, you may be able to get the name of the class. __qualname__ is an attribute that holds the qualified name of the class / function / method / descriptor / generator instance. You should be able to access it from within the class' definition.

class Fred:
    print("Fred's qualname:", __qualname__)
    class Barney:
        print("Barney's qualname:", __qualname__)

Result:

Fred's qualname: Fred
Barney's qualname: Fred.Barney

If your class isn't defined at the file-level, you might need to do some string manipulation to separate its name from the rest of the path. For example, change the above example to print("Barney's qualname:", __qualname__.rpartition(".")[-1]) to get just "Barney" rather than "Fred.Barney".


Despite my best efforts, I couldn't find documentation that explicitly confirms that __qualname__ has a sensible value when accessed as a regular name and not as an attribute. I'm not fully convinced that this is well-defined behavior, so I don't think I can unreservedly endorse it for production-quality code.

Kevin
  • 74,910
  • 12
  • 133
  • 166
  • A quick check of `dirs()` after the `class MyClass():` (given working code), shows that `__qualname__` is the only useful variable defined there. As `__qualname__` will have the dotted class hierarchy (e.g., "One.Two.Three"), I've used it with split to get the leaf class. Also, `__qualname__` seems well-documented in 3.7.3, https://docs.python.org/3/library/stdtypes.html#definition.__qualname__. I can see back to 3.6.8 that it exists, but not 3.5. – Quantum Mechanic Apr 17 '19 at 10:32
  • 1
    I interpret https://docs.python.org/3/library/stdtypes.html#definition.__qualname__ to refer only to the `__qualname__` accessible via attribute access on the class object, e.g. `Fred.__qualname__`. If that passage implies that the name is _also_ available in the local namespace in the class block, then I'm confused why `__name__` doesn't work the same way. It's available, but its value is `__main__` and not `Fred`, so it seems like it belongs to the module object and not the class. This inconsistency leads me to believe that it's implementation-specific behavior. – Kevin Apr 17 '19 at 14:29
  • I agree with your analysis of the docs. And I'm also disappointed that it's that way. I'd much prefer for Python docs to acknowledge that distinction. It's also frustrating that I can't easily pass the class name into a method decorator defined in the class, but I have to jump through hoops to do it indirectly. – Quantum Mechanic Apr 18 '19 at 10:13
2

To work with class name during its definition you can also use a metaclass with __prepare__ method:

def find_logger(name):
    return name + '_logger'


class PrepareClassLogger(type):
    def __prepare__(name, bases, **kwargs):
        return {'class_logger': find_logger(name)}


class MyClass(metaclass=PrepareClassLogger):
    pass

print(MyClass.class_logger)  # MyClass_logger
sanyassh
  • 8,100
  • 13
  • 36
  • 70