Соглашения о создании констант в Python

Я пишу приложение, которое должно найти схему базы данных, через движки. С этой целью я пишу небольшой адаптер базы данных, используя Python. Я решил сначала написать базовый класс, в котором описываются необходимые мне функции, а затем реализовать его с использованием классов, которые наследуются от этой базы. По пути мне нужно реализовать некоторые константы, которые должны быть доступны во всех этих классах. Некоторые из этих констант необходимо объединить с помощью C-стиля побитового OR.

Мой вопрос:

  • Каков стандартный способ совместного использования таких констант?
  • Каков правильный способ создания констант, которые можно комбинировать? Я имею в виду код стиля MAP_FIXED | MAP_FILE | MAP_SHARED, который позволяет C.

Для первого я наткнулся на темы, где все константы были помещены в модуль в первую очередь. Для последнего я вкратце подумал об использовании dict булевых. Оба они казались слишком громоздкими. Я полагаю, что это довольно распространенное требование, и думаю, что хороший способ действительно должен существовать!

Ответ 1

Каков стандартный способ совместного использования таких констант?

В стандартной библиотеке наиболее распространенным способом является определение констант как переменных уровня модуля с использованием имен UPPER_CASE_WITH_UNDERSCORES.

Каков правильный способ создания констант, которые можно комбинировать? Я имею в виду MAP_FIXED | MAP_FILE | MAP_SHARED код стиля, который позволяет C.

Используются те же правила, что и в C. Вы должны убедиться, что каждое постоянное значение соответствует одному уникальному биту, то есть степени 2 (2, 4, 8, 16,...).

В большинстве случаев люди используют шестнадцатеричные числа для этого:

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

Некоторые предпочитают более понятный для человека стиль, вычисляя постоянные значения динамически с помощью операторов сдвига:

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

В Python вы также можете использовать двоичную нотацию, чтобы сделать это еще более очевидным:

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

Но так как это обозначение является длинным и трудночитаемым, использование шестнадцатеричной или двоичной нотации сдвига, вероятно, предпочтительнее.

Ответ 2

Константы обычно идут на уровне модуля. Из PEP 8:

Константы

Константы обычно определяются на уровне модуля и записываются всеми заглавными буквами с подчеркиванием, разделяющим слова. Примеры включают MAX_OVERFLOW и TOTAL.

Если вам нужны константы на уровне класса, определите их как свойства класса.

Ответ 3

Вы часто находите константы на глобальном уровне, и они являются одной из немногих переменных, которые существуют там. Есть также люди, которые пишут пространства имен констант, используя dicts или объекты вроде этого

class Const:
    x = 33

Const.x

Есть некоторые люди, которые помещают их в модули и другие, которые присоединяют их как переменные класса, к которым обращаются экземпляры. В большинстве случаев его личный вкус, но лишь несколько глобальных переменных, не могут действительно сильно повредить.

Ответ 4

Stdlib - отличный источник знаний о том, что вы хотите найти в 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

N.B: Модуль re также использует стиль бит-мудрый аргумент, если вы хотите другой пример.

Ответ 5

Именование обычно UPPERCASE_WITH_UNDERSCORE, и они обычно являются модульным уровнем, но иногда они живут в своем классе. Одна из веских причин находиться в классе - это когда значения являются особыми - например, для того, чтобы быть двумя:

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

Если вы хотите экспортировать эти константы на уровень модуля (или любое другое пространство имен), вы можете добавить в класс следующее:

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

а затем

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