4

I've made a class which can be compared and sorted inside common data structures.

The thing is that I wanted to make two class constants for the maximum and minimum values that class can take. So I could call this value just importing MyClass and writing

obj = MyClass.MY_MAX_CONSTANT

The thing is that calling the constructor or init method to initialize these constants is not allowed.

In Java this would be declared as static and it would work, but I don't know how can I do a class / static constant in Python using the constructor / init method. Haven't found much googling but some general recipes for constants and suggestions for making properties.

I don't need a mechanism to avoid changing the constant value since I'm definitely not changing it.

My first try was:

class MyClass(object):
    MY_MAX_CONSTANT = MyClass(10,10)
    MY_MIN_CONSTANT = MyClass(0,0)

    def __init__(self, param1, param2): # Not the exact signature, but I think this works as an example 
        # We imagine some initialization work here
        self.x = param1
        self.y = param2


    # SORT FUNCTIONS
    def __cmp__(self, other):
        # Implementation already made here

    def __eq__(self, other):
        # Implementation already made here

    def __ne__(self, other):
        # Implementation already made here

    def __ge__(self, other):
        # Implementation already made here

    # And so on...

A second try, by using some functions for each constant:

class MyClass(object):
    def __init__(self, param1, param2): # Not the exact signature, but I think this works as an example 
        # We imagine some initialization work here
        self.x = param1
        self.y = param2
        MY_MAX_CONSTANT = None
        MY_MIN_CONSTANT = None

    @staticmethod
    def get_max(self):
        if not MyClass.MY_MAX_CONSTANT:
            MyClass.MY_MAX_CONSTANT = MyClass(10,10)
        return MyClass.MY_MAX_CONSTANT

    @staticmethod
    def get_min(self):
        if not MyClass.MY_MIN_CONSTANT:
            MyClass.MY_MIN_CONSTANT = MyClass(0,0)
        return MyClass.MY_MIN_CONSTANT    

    # SORT FUNCTIONS (I'm not writing them twice for spacing)

But I wanted to avoid strange function mechanisms only for making two constants.

I prefer the constant being in the class and not a module because it feels more natural to me, but I'm hearing any advice or suggestion. Can anyone point me a better pythonic solution?

Thanks

madtyn
  • 1,469
  • 27
  • 55
  • 2
    "The thing is that calling the constructor or init method to initialize these constants is not allowed." - who's not allowing it? Your teacher? Python allows it, but you'll have to create the constants once the class actually exists - it doesn't exist yet inside the class statement body. – user2357112 Oct 26 '17 at 17:15
  • Where should I define the constants? The init doesn't look like to me a good place to call itself recursively – madtyn Oct 26 '17 at 17:16

1 Answers1

9

Add your constants after creating your class, you are allowed to add more class attributes:

class MyClass:
    # ...

MyClass.MY_MAX_CONSTANT = MyClass(10, 10)
MyClass.MY_MIN_CONSTANT = MyClass(0, 0)

Only when the class statement has completed running is the class object available, bound to the name MyClass. You can't create instances before this point.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thank you. This works. My only doubt now is how the import sentence works when I import the class. It brings from the right module all the things related to MyClass? How does Python know when to stop importing? How do I know? (Until know I thought importing a class only imported the indented content under the declaration) – madtyn Oct 26 '17 at 18:05
  • 1
    Importing will execute all top-level code in your module the first time. The rest is just binding names to the objects; `sys.modules['modulename']` is the central module object and you get names bound to the various specific names you could import from there, or just a reference to the module object itself. So everything will exist in memory. – Martijn Pieters Oct 26 '17 at 20:09
  • @MartijnPieters How would one type this using mypy? I get `"Type[MyClass]" has no attribute "MY_MAX_CONSTANT"`, and I cannot find any way around this. – MarnixKlooster ReinstateMonica Apr 15 '23 at 13:53
  • 1
    @MarnixKloosterReinstateMonica: you can add `MY_MAX_CONSTANT: ClassVar[Self]` to the class body here. `ClassVar` is `from typing import ClassVar`, and if you are using Python 3.11 or newer then `Self` can be imported from the same module, otherwise install `typing_extensions` and import it from that project. – Martijn Pieters Jun 13 '23 at 18:36
  • @MartijnPieters Thanks, works like a charm! (After I upgraded from my distro-default mypy 0.942 to a more current 1.3.0, so that mypy knows about Self, see https://github.com/python/typing_extensions/issues/96.) – MarnixKlooster ReinstateMonica Jun 14 '23 at 09:28
  • @MarnixKloosterReinstateMonica: yes, v1.x is a marked improvement over the 0.x series releases. And `Self` is so much nicer than having to use `ClassVar["MyClass"]` (a forward reference), especially when subclassing or having to chase down all forward references when trying to rename a class. :-) – Martijn Pieters Jun 14 '23 at 09:35