Соглашения о создании констант в 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__])