2

In Python 3.5, how can the class name be assigned to a class variable? The obvious alternative is to hardcode the class name.

class Foo(object):
    the_module = __name__
    also_the_module = __module__

    def name_of_module(self):
        return __name__
    def name_of_class(self):
        return __class__.__name__  

print("Foo.the_module=      ", Foo.the_module)
print("Foo.also_the_module= ", Foo.also_the_module)
print("Foo.__name__=        ", Foo.__name__)  # what is desired but outside of class def
print("Foo().name_of_module=", Foo().name_of_module())
print("Foo().name_of_class= ", Foo().name_of_class()) # what is desired but inside a method

I tried getting the class from the first argument of a @classmethod.

class Foo(object):
    @classmethod
    def class_name(cls):
        return cls.__name__

# Both of these produce the correct response but neither
# can be used assign to a class variable
print("Foo.class_name()=    ", Foo.class_name())
print("Foo().class_name()=  ", Foo().class_name()) 

Unfortunately, this function cannot be called when assigning to a class variable. class = class_name produces NameError: name 'class_name' is not defined. my_class = Foo.class_name() produces NameError: name 'Foo' is not defined.

NOTE: Updated question to use the correct Python term "class variable" and not a static variable (reflecting my C++ background)

Mike Ulm
  • 380
  • 1
  • 4
  • 14
  • I have tried you last code snippet (ends with `print("Foo().class_name()= ",Foo().class_name())`) and it works. Can you please add code that does not work and also specify desired results? – imposeren Feb 25 '16 at 21:20
  • Can you rephrase your question without using the words "static variable"? That concept does not make sense in the python world. What is it exactly that you want to accomplish? Do you want to set a class variable? Do you want to set it so that instances have access to it? – Oin Feb 25 '16 at 21:20
  • Updated question to use "class variable" – Mike Ulm Feb 25 '16 at 23:56
  • The overall intention is have a class variable that is collection of the individual objects. I want to be be able to pickle that collection to `.pickle`. I can hardcode this as: `class Foo(object): all = AllObjects("foo")` That works. But I would prefer something like `all = AllObjects(__class_name__)`. However, I have been unable to find what works in the class scope. That is context of my question. – Mike Ulm Feb 26 '16 at 00:06

3 Answers3

1

Note that having the class method

@classmethod
def class_name(cls):
    return cls.__name__

Brings nothing compared to calling x.__class__.__name__ directly.

If you want to assign the class name, just modify the class itself:

class Foo(object):
@classmethod
def class_name(cls):
    return cls.__name__

Foo.__name__ = "bar"
a = Foo()
print(a.class_name())

outputs bar

If you wanted different class name only for one instance, you need to create a new class on the fly (inheriting from the old class) and change the name of this class.

def set_class_name(instance, new_class_name):
orig_class = instance.__class__

class _new_class(orig_class):
    pass

_new_class.__name__ = new_class_name
a.__class__ = _new_class

a = Foo()
set_class_name(a, 'bar')
print(a.class_name())

outputs bar too.

ikamen
  • 3,175
  • 1
  • 25
  • 47
1

In Python 3 (since version 3.3), the most straightforward way to do this is to use __qualname__ (which I came across thanks to this answer):

class Foo(object):
    name = __qualname__

class Bar(Foo):
    name = __qualname__

print((Foo.name, Bar.name))

This produces the desired output:

('Foo', 'Bar')

For earlier versions, or if you want to be more explicit, you could always just mention the name of your class (yes, it's not DRY, but it's KISS):

class Foo(object):
    name = Foo.__name__

class Bar(Foo):
    name = Bar.__name__

print((Foo.name, Bar.name))

However, both suggestions above require you to re-assign the name in derived classes. Obviously, as you implemented one in your question, you are aware that you could just use a class method, but then you'd have to call it each time you wanted the name:

class Foo(object):
    @classmethod
    def name(cls):
        return cls.__name__

class Bar(Foo):
    pass

print((Foo.name(), Bar.name()))

If you don't like the parenthesis that cling to name, what you might want to consider is to create a class property. That way you can keep it read-only and inheritable, just like the class method approach, but still maintaining the appearance of a class variable. Creating a class property is the topic of this question. One answer suggests using a method decorator (classproperty, defined there), albeit with some verbosity (though you can cut the setter method):

# classproperty is defined in the aforementioned answer

class Foo(object):
    @classproperty
    def name(cls):
        return cls.__name__

class Bar(Foo):
    pass

print((Foo.name, Bar.name))

Another answer suggests using a meta-class. This is a new concept for me. While it provides succinct code, the quote by Tim Peters mentioned in this pretty good guide about meta classes deters me from recommending it:

class FooMetaclass(type):
    @property
    def name(cls):
        return cls.__name__

class Foo(metaclass=FooMetaclass):
    # for Python 2, remove the metaclass assignment above, and add:
    # __metaclass__ = FeatureMetaclass
    pass

class Bar(Foo):
    pass

print((Foo.name, Bar.name))

I think those are enough. :)

Yuval
  • 3,207
  • 32
  • 45
0
class Foo(object):

def __init__(self):
    self.class_name = self.__class__.__name__

x = Foo()

x.class_name

EDIT Waitaminute. I don't think I'm getting you. What do you mean with static variable?

Gijs
  • 10,346
  • 5
  • 27
  • 38
  • A static variable is bound to the class, not a particular instance of the class. Therefore, a static variable does not have a `self` from which it can apply `__class__.__name__` – Mike Ulm Feb 25 '16 at 20:58
  • Isn't `__name__` itself a static attribute of the class? – Gijs Feb 25 '16 at 21:40
  • In the scope of a class variable, `__name__` returns the module name, not the class name. – Mike Ulm Feb 26 '16 at 00:07
  • `__name__` without any object always returns the module name, except if you're in the 'main' module, then it resolves to `__main__`. But if you call it on an object, including a class which is an object as well, it will return the `__name__` of the object, whether you are in scope of something (a class variable?) or not. – Gijs Feb 26 '16 at 12:12