Eliu S
Eliu S

Reputation: 122

How to add or change a Class (not an instance)?

I would like to set an attribute to an class object directly, without creating an instance, e.g. having an alternative name that can be accessed like the __ name __ attribute:

class Foo:
    pass

> Foo.__name__
Foo

But this doesn't work:

some_file.py:

class Foo:
    alternativ_name = __name__ + "_ending"

print(Foo.alternativ_name)

This prints:

__main___ending

If I try it in the interactive Console, it returns something else again:

>>> class Foo:
...   alt_name = __name__ + "_ending"
...
>>> Foo.alt_name
'builtins_ending'

What I would like to achive is:

class Foo:
    alt_name = __name__ + "_ending"
Foo.alt_name

should return:

'Foo_ending'

How do I do this?

Upvotes: 1

Views: 85

Answers (2)

Sjoerd
Sjoerd

Reputation: 75588

The variables __name__ and Foo.__name__ actually point to two different things. Using __name__ within the Foo class still uses the global variable, and not Foo.__name__.

Within the class, it is not possible to explicitly reference the same class:

class Foo:
    alt_name = Foo.__name__ + "_ending"
    # raises NameError: name 'Foo' is not defined

If you want the property on objects, you can do it during runtime, e.g. in the __init__. If you really want the property on the class itself, you can do that using metaclasses:

class Foo:
    class __metaclass__(type):
        @property
        def alt_name(cls):
            return cls.__name__ + "_ending"

Upvotes: 1

Dunes
Dunes

Reputation: 40703

Foo.__name__ has not yet been created at the point you are trying to access it. Therefore, when you access __name__ it gets the module's __name__. There are several ways you can solve this. One is by using a metaclass, but this is pretty overkill for just adding an attribute to a class. The second is to use a decorator on the class, and the third is to make alt_name a non-data descriptor or maybe a property.

Using a decorator:

def add_alt_name(template):
    def decorator(klass):
        klass.alt_name = template.format(klass.__name__)
        return klass
    return decorator

@add_alt_name(template="{}_ending")
class Foo:
    pass

print(Foo.alt_name)

Using a non-data descriptor:

class AlternativeName:
    def __init__(self, template, name="alt_name"):
        self.template = template
        self.name = "_" + name

    def __get__(self, instance, klass):
        try:
            return getattr(klass, self.name)
        except AttributeError:
            pass
        alt_name = self.template.format(klass.__name__)
        setattr(klass, self.name, alt_name)
        return alt_name

class Foo:
    alt_name = AlternativeName(template="{}_ending")

print(Foo.alt_name)

Much simpler just to use a decorator.

Upvotes: 0

Related Questions