简体   繁体   中英

Assign class name to a class variable in Python

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)

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.

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?

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. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM