Python Design – Class vs File Level Variables

designpythonscope

I have the following class:

import Radar

class Domain(Radar):
    URL = 'https://mxtoolbox.com/DNSLookup.aspx'
    ADDRESS_KEYS = ('mailing_address', 'city_name', 'state_name', 'zip_code',
                    'country_name')
    GENERAL_PARAMS = {'format': 'json'}
    BALANCE_PARAMS = {**Radar.GENERAL_PARAMS, 'account': 'balance'}
    SUCCESSFUL_QUERY = 1
    API_KEY_ERROR_REASON = 'Invalid API Key'
    MASKED_ENTITY_INDICATOR = 'To View Unmasked Data'

    def __init__(self, *args, **kwargs):
        self.entity_title = 'Crawling'
        super().__init__(*args, **kwargs)

The class' methods (only them) access each of these constant static members few times.

What are the advantages/disadvantages of putting these variables in the outer scope (as a file level variables – and not class level static variables)?

Even though I know both approaches would work, I wonder where is the best place put the variables – inside the class or above it.

The other option:

import Radar

URL = 'https://mxtoolbox.com/DNSLookup.aspx'
ADDRESS_KEYS = ('mailing_address', 'city_name', 'state_name', 'zip_code',
                'country_name')
GENERAL_PARAMS = {'format': 'json'}
BALANCE_PARAMS = {**Radar.GENERAL_PARAMS, 'account': 'balance'}
SUCCESSFUL_QUERY = 1
API_KEY_ERROR_REASON = 'Invalid API Key'
MASKED_ENTITY_INDICATOR = 'To View Unmasked Data'

class Domain(Radar):
    def __init__(self, *args, **kwargs):
        self.entity_title = 'Crawling'
        super().__init__(*args, **kwargs)

Best Answer

This is a mostly stylistic choice. In the end, it doesn't matter. There are some finer differences depending on how they are used.

  • If the constants are only used within the module, using module-level variables is more convenient. (Compare class variables below).

  • If the constants are used outside of this module, then module-level variables have the advantage that you can import them.

  • Class variables have the advantage that they are bundled in that class. This may provide a more convenient interface because you don't have to pass them around individually. For example, the Enum class does this nicely. But in reverse: why would you be bundling them unless they are used together?

Class variables in Python do have some gotchas.

  • Python doesn't have true constants, so anyone could reassign them. (To create read-only fields in Python, you must use a property, or more generally: the descriptor protocol).
  • These variables can be shadowed by subclasses, or by instance variables.
  • The variables can only be accessed through the class or its instances. E.g. to access the URL variable in a method, you have to say Domain.URL or self.URL. Module-level variables can be accessed directly.

In general, Python doesn't force you to stuff everything into a class. You should use this freedom because most variables or functions don't belong into a class. If in doubt, use free variables/functions instead of fields/methods within a class. You can always change your mind later and refactor.

Related Topic