31

I am writing a program in python which contains many constant variables. I would like to create a file that will hold all these variables like the .h file in C that contains many #define. I tried to use configparser however I didn't find it easy and fun to use.

Do you know a better way?

SkyWalker
  • 28,384
  • 14
  • 74
  • 132
TomE8
  • 546
  • 1
  • 4
  • 14
  • As far as configuration variables go, you can set environment variables and read them from within your code. You could also use JSON or YAML files. – Tony Tuttle May 25 '18 at 17:17

6 Answers6

35

Constants (in a sense) in Python 3.8+

Python 3.8 introduces the typing.Final type qualifier, which is used to indicate that a variable or attribute should not be reassigned, redefined, or overridden.

PEP 591 -- Adding a final qualifier to typing

from typing import Final

# Annotate module variables
# (with or without an explicit type, using the syntax Final[<type>])
# (type is auto-determined in absence of an explicit type)
PI: Final[float] = 3.141592654
ANSWER_TO_EVERYTHING: Final = 42


# Annotate instance variables in class bodies
# (explicit type is needed if no value is assigned)
class Point:
    x: Final[int]
    y: Final = 0

    def __init__(self, x: int):
        self.x = x


# Annotate instance variables directly
# (only allowed in __init__ methods)
class Person:
    def __init__(self, birth_year: int):
        self.birth_year: Final = birth_year

Linters and type checkers will show you warnings if you reassign or redefine Final variables. Note that there is no runtime check, so you can still run the code below.

ANSWER_TO_EVERYTHING: Final = 42
ANSWER_TO_EVERYTHING = 420  # shows warning
print(ANSWER_TO_EVERYTHING)  # prints 420

There is also the typing.final decorator, which is used to restrict inheriting classes and overriding methods.

32

Python does not allow constant declarations like C or C++.

Normally in Python, constants are capitalized (PEP 8 standards) which helps the programmer know it's a constant.

Ex. MY_CONSTANT = "Whatever"

Another valid way of doing it which I don't use but heard of, is using a method:

def MY_CONSTANT():
    return "Whatever"

Now in theory, calling MY_CONSTANT() acts just like a constant.

EDIT

Like the comments says, someone can go and change the value by calling

MY_CONSTANT = lambda: 'Something else'

but don't forget the same person can call MY_CONSTANT = "Something else" in the first example and change the initial value. In both cases it is unlikely but possible.

pfabri
  • 885
  • 1
  • 9
  • 25
scharette
  • 9,437
  • 8
  • 33
  • 67
  • 6
    `MY_CONSTANT = lambda: 'Something else'` – Not sure why you'd bother with a "constant function", it's not any more constant. – deceze May 25 '18 at 17:17
  • 1
    @scharette The point is that whether you use `def` or `lambda`, you can still easily change the value of `MY_CONSTANT`; it's just a name that refers to some object, whether that object be an `int` or a `function`. Plus, the overhead of calling a function (both the lexical overhead of having to add `()` and the run-time overhead of setting up the stack frame) outweigh any perceived benefit. – chepner May 25 '18 at 17:31
  • 1
    `MY_CONSTANT = lambda: "new value"!` is just as easy to write as `MY_CONSTANT = "new value!"`; making it a function doesn't do anything useful. – chepner May 25 '18 at 17:33
  • You're right, this is not how I understood what he meant. I'm not trying to say this is a real constant, he's asking for C like constants, which doesn't exist in built-in python. My point is if someone is calling `MY_CONSTANT = lambda: 'Something else'` he really wants to change the value. – scharette May 25 '18 at 17:36
  • Samething goes for someone changing the constant itself in C. I mean a constant is a constant until the day someone goes around and change the inital value... – scharette May 25 '18 at 17:37
21

There are no constants in Python, the way they exist in C or Java. You can imitate them by functions:

def FOO():
  return "foo"

You can wrap the function call in a property, and thus make it look like a variable:

class Const:
  @property
  def FOO(self):
    return "foo"

CONST = Const()  # You need an instance

if something == CONST.FOO:
  ...

With a bit of meta stuff, one can get unsettable attributes with a terse syntax:

def const(cls):
    # Replace a class's attributes with properties,
    # and itself with an instance of its doppelganger.
    is_special = lambda name: (name.startswith("__") and name.endswith("__"))
    class_contents = {n: getattr(cls, n) for n in vars(cls) if not is_special(n)}
    def unbind(value):  # Get the value out of the lexical closure.
        return lambda self: value
    propertified_contents = {name: property(unbind(value))
                             for (name, value) in class_contents.items()}
    receptor = type(cls.__name__, (object,), propertified_contents)
    return receptor()  # Replace with an instance, so properties work.


@const
class Paths(object):
    home = "/home"
    null = "/dev/null"

Now you can access Paths.home as a normal value, but can't assign to it. You can define several classes decorated with @const, as you might use several .h files.

9000
  • 39,899
  • 9
  • 66
  • 104
9

You can use something like this:

Files structure:

myapp/
    __init__.py
    settings.py
    main.py

settings.py

CONST_A = 'A'
CONST_B = 'B'

__init__.py

from . import settings as global_settings


class Settings:

    def __init__(self):
        for setting in dir(global_settings):
            if setting.isupper():
                setattr(self, setting, getattr(global_settings, setting))

    def __setattr__(self, attr, value):
        if not getattr(self, attr, None):
            super().__setattr__(attr, value)
        else:
            raise TypeError("'constant' does not support item assignment")


settings = Settings()

main.py

import settings

print(settings.CONST_A)  # prints A

settings.CONST_A = 'C'  # raises TypeError error

print(settings.CONST_A)  # prints A

settings.CONST_C = 'C'  # also able to add new constants
print(settings.CONST_C)  # prints C

Overwritten __setattr__ in Settings class makes all the attributes read-only. The only requirement is to have all the constants in your settings.py written in capital letters. But be aware, that it's not gonna work if you import variables directly:

from settings import CONST_A

print(settings.CONST_A)  # prints A

settings.CONST_A = 'C'  # sets C

print(settings.CONST_A)  # prints C
Artem Nepo
  • 413
  • 1
  • 3
  • 9
  • I taken your code as it, __init__ Class giving me error 'indent expected'. After correcting, it's working. Cheers :-) – dinu0101 Sep 13 '19 at 08:11
  • Interesting. `import settings` in `main.py` imports the variable `settings` not `settings.py`. – xuhdev Nov 05 '20 at 01:10
5

Just define a constants.py file and write all your constants. There is no other magic trick in Python. Use caps as a general convention.

SkyWalker
  • 28,384
  • 14
  • 74
  • 132
saran3h
  • 12,353
  • 4
  • 42
  • 54
1

Python isn't preprocessed. You can just create a file constant.py

#!/usr/bin/env python
# encoding: utf-8
"""
constant.py
"""

MY_CONSTANT = 50

Import constant.py file when ever you want constant values like below example.

#!/usr/bin/env python
# encoding: utf-8
"""
example.py
"""
import constant
print constant.MY_CONSTANT * 2

This way you can use constants across project.

You also have the option, if the constants are tied to a particular class and used privately within that class of making them specific to that class:

class Foo(object):
   GOOD = 0
   BAD = 1

   def __init__(self...

If you want to define and use entire module, making them on top of the module

PIE = 3.47

class Foo(object):
   def __init__(self...
Ankireddy
  • 179
  • 1
  • 6