24

Apologies if I've got the terminology wrong here—I can't think what this particular idiom would be called.

I've been trying to create a Python 3 class that statically declares instances of itself inside itself—sort of like an enum would work. Here's a simplified version of the code I wrote:

class Test:
    A = Test("A")
    B = Test("B")

    def __init__(self, value):
        self.value = value

    def __str__(self):
       return "Test: " + self.value

print(str(Test.A))
print(str(Test.B))

Writing this, I got an exception on line 2 (A = Test("A")). I assume line 3 would also error if it had made it that far. Using __class__ instead of Test gives the same error.

  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in Test
NameError: name 'Test' is not defined

Is there any way to refer to the current class in a static context in Python? I could declare these particular variables outside the class or in a separate class, but for clarity's sake, I'd rather not if I can help it.

To better demonstrate what I'm trying to do, here's the same example in Java:

public class Test {
    private static final Test A = new Test("A");
    private static final Test B = new Test("B");

    private final String value;

    public Test(String value) {
        this.value = value;
    }

    public String toString() {
        return "Test: " + value;
    }

    public static void main(String[] args) {
        System.out.println(A);
        System.out.println(B);
    }
}

This works as you would expect: it prints:

Test: A
Test: B

How can I do the same thing in Python?

Samir Talwar
  • 14,220
  • 3
  • 41
  • 65

4 Answers4

22

After you defined the class, just add these two lines:

Test.A = Test("A")
Test.B = Test("B")

A class in Python is an object like any other and you can add new variables at any time. You just can't do it inside the class since it's not defined at that time (it will be added to the symbol table only after the whole code for the class has been parsed correctly).

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
4

In Python, a class block isn't just a declaration; it's executed at run time to build the class. Each def inside the class builds a function and binds the name into the class's namespace. For that reason, you can't simply do A = MyClass() directly inside the class block, because MyClass() isn't fully defined until the class block closes. Here's how to do it:

class Test:
    def __init__(self, value):
        self.value = value

    def __str__(self):
       return "Test: " + self.value

Test.A = Test("A")
Test.B = Test("B")
Daniel Stutzbach
  • 74,198
  • 17
  • 88
  • 77
4

While the Aaron's answer is the preferred way you can also use metaclasses:

>>> class TestMeta(type):
...  def __init__(cls, name, bases, dct):
...   cls.A = cls()
...   cls.B = cls()
... 
>>> class Test:
...   __metaclass__ = TestMeta
... 
>>> Test
<class __main__.Test at 0x7f70f8799770>
>>> Test.A
<__main__.Test object at 0x7f70f86f2e10>
>>> Test.B
<__main__.Test object at 0x7f70f86f2e90>
>>> 
mg.
  • 7,822
  • 1
  • 26
  • 30
  • 1
    Pretty cool. Metaclasses are something I haven't played about with before. I'll have to try them out at some point. – Samir Talwar Mar 30 '10 at 16:09
0

One paradigm that's known in Java is to use Enums. In python it's possible to do it as well check here.

Basically you define a metadata class like this:

class TestMetadata:
  def __init__(self, *args):
    # do your init logic
    ...

then you define your constants class

class Tests(Enum):
  A = TestMeta(...)
  B = TestMeta(...)
  
  @property
  def prop1(self):
    return self.value.prop1
mohRamadan
  • 571
  • 6
  • 15