简体   繁体   中英

Conventions on creating constants in Python

I am writing an application which needs to find out the schema of a database, across engines. To that end, I am writing a small database adapter using Python. I decided to first write a base class that outlines the functionality I need, and then implement it using classes that inherit from this base. Along the way, I need to implement some constants which need to be accessible across all these classes. Some of these constants need to be combined using C-style bitwise OR.

My question is,

  1. what is the standard way of sharing such constants?
  2. what is the right way to create constants that can be combined? I am referring to MAP_FIXED | MAP_FILE | MAP_SHARED MAP_FIXED | MAP_FILE | MAP_SHARED MAP_FIXED | MAP_FILE | MAP_SHARED style code that C allows.

For the former, I came across threads where all the constants were put into a module first. For the latter, I briefly thought of using a dict of booleans. Both of these seemed too unwieldly. I imagine that this is a fairly common requirement, and think some good way must indeed exist!

what is the standard way of sharing such constants?

Throughout the standard library, the most common way is to define constants as module-level variables using UPPER_CASE_WITH_UNDERSCORES names.

what is the right way to create constants that can be combined? I am referring to MAP_FIXED | MAP_FILE | MAP_SHARED style code that C allows.

The same rules as in C apply. You have to make sure that each constant value corresponds to a single, unique bit, ie powers of 2 (2, 4, 8, 16, ...).

Most of the time, people use hex numbers for this:

OPTION_A = 0x01
OPTION_B = 0x02
OPTION_C = 0x04
OPTION_D = 0x08
OPTION_E = 0x10
# ...

Some prefer a more human-readable style, computing the constant values dynamically using shift operators:

OPTION_A = 1 << 0
OPTION_B = 1 << 1
OPTION_C = 1 << 2
# ...

In Python, you could also use binary notation to make this even more obvious:

OPTION_A = 0b00000001
OPTION_B = 0b00000010
OPTION_C = 0b00000100
OPTION_D = 0b00001000

But since this notation is lengthy and hard to read, using hex or binary shift notation is probably preferable.

Constants generally go at the module level. From PEP 8 :

Constants

Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL.

If you want constants at class level, define them as class properties.

You often find constants at global level, and they are one of the few variables that exist up there. There are also people who write Constant namespaces using dicts or objects like this

class Const:
    x = 33

Const.x

There are some people who put them in modules and others that attach them as class variables that instances access. Most of the time its personal taste, but just a few global variables can't really hurt that much.

Stdlib is a great source of knowledge example of what you want can be found in doctest code :

OPTIONS = {}

# A function to add (register) an option.
def register_option(name):
    return OPTIONS.setdefault(name, 1 << len(OPTIONS))

# A function to test if an option exist.
def has_option(options, name):
    return bool(options & name)

# All my option defined here.
FOO = register_option('FOO')
BAR = register_option('BAR')
FOOBAR = register_option('FOOBAR')


# Test if an option figure out in `ARG`.
ARG = FOO | BAR
print has_option(ARG, FOO)
# True
print has_option(ARG, BAR)
# True
print has_option(ARG, FOOBAR)
# False

NB: The re module also use bit-wise argument style too, if you want another example.

Naming is usually UPPERCASE_WITH_UNDERSCORE, and they are usually module level but occasionally they live in their own class. One good reason to be in a class is when the values are special -- such as needing to be powers of two:

class PowTwoConstants(object):
    def __init__(self, items):
        self.names = items
        enum = 1
        for name in items:
            setattr(self, name, enum)
            enum <<= 1

constants = PowTwoConstants('ignore_case multiline newline'.split())
print constants.newline # prints 4

If you want to be able to export those constants to module level (or any other namespace) you can add the following to the class:

    def export(self, namespace):
        for name in self.names:
            setattr(namespace, name, getattr(self, name))

and then

import sys
constants.export(sys.modules[__name__])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM