Reputation: 922
Unfortunately, in Python, a class attribute cannot reference its class name. The following raises a NameError
:
class Foo(object):
bar = Foo
In a situation where a class attribute must reference its class name (in my case, it's part of a validation scheme for a library). What is the clearest way to do so?
The following is my current attempt:
class Foo(object):
bar = None
Foo.bar = Foo
This works, but even with comments is likely to cause confusion, since you expect a class attribute to be initialized when declared, not after the class is declared.
Does anyone have a better workaround?
Upvotes: 13
Views: 5876
Reputation: 123433
You could define a class decorator that replaced placeholder strings with the class being defined:
def fixup(cls):
placeholder = '@' + cls.__name__
for k,v in vars(cls).items():
if v == placeholder:
setattr(cls, k, cls)
return cls
@fixup
class Foo(object):
bar = '@Foo'
print('Foo.bar: {!r}'.format(Foo.bar)) # -> Foo.bar: <class '__main__.Foo'>
Another alternative would be to use the __init_subclass__()
special method which was introduced in Python 3.6 to create a base class and then derive your class from it instead of the generic object
:
class Base(object):
def __init_subclass__(cls, /, **kwargs):
super().__init_subclass__(**kwargs)
cls.bar = cls
class Foo(Base):
pass
print('Foo.bar: {!r}'.format(Foo.bar)) # -> Foo.bar: <class '__main__.Foo'>
Upvotes: 2
Reputation: 8178
You can use lambda expression:
class Foo(object):
bar = lambda: Foo
This violates PEP 8, but works
Upvotes: 0
Reputation: 37509
Use a meta class to automatically set it.
def my_meta(name, bases, attrs):
cls = type(name, bases, attrs)
cls.bar = cls
return cls
class Foo(object):
__metaclass__ = my_meta
>>> print Foo.bar
<class '__main__.Foo'>
Upvotes: 5
Reputation: 27311
You can use a class decorator
def moi(fieldname):
def _selfref(cls):
setattr(cls, fieldname, cls.__name__)
return cls
return _selfref
usage:
@moi('bar')
class Foo(object):
pass
then:
>>> print Foo.bar
Foo
Upvotes: 2
Reputation: 1335
Why not use a property if you just want to ''alias'' __class__
?
@property
def bar(self):
return self.__class__
That said it's bound to create issues if you play with some inheritance.
Upvotes: 0
Reputation: 83788
You can use metaclasses. Define a __new__
which iterates over set attributes and then sets the back pointer to the class itself.
Upvotes: 1