Могу ли я предотвратить изменение объекта в Python?

Я хочу контролировать глобальные переменные (или глобально измененные переменные) таким образом, чтобы они были установлены только один раз в коде инициализации программы и блокировали их после этого.

Я использую UPPER_CASE_VARIABLES для глобальных переменных, но я хочу иметь верный способ не изменять переменную в любом случае.

  • Предоставляет ли python эту (или подобную) функцию?
  • Как вы контролируете переменные с глобальным охватом?

Ответ 1

У Activestate есть рецепт под названием " Константы в Python" от маститого Алекса Мартелли для создания модуля const с атрибутами, которые нельзя восстановить после создания. Это звучит как то, что вы ищете, за исключением увеличения, но это можно добавить, проверив, чтобы увидеть, было ли имя атрибута заглавными или нет.

Конечно, это может быть обойдено детерминированными, но то, как Python - и считается большинством людей "хорошей вещью". Однако, чтобы сделать его немного более сложным, я предлагаю вам не беспокоиться о добавлении предположительно очевидного метода __delattr__ поскольку тогда люди могут просто удалить имена, а затем добавить их обратно к другим значениям.

Вот о чем я говорю:

# Put in const.py...
# from http://code.activestate.com/recipes/65207-constants-in-python
class _const:
    class ConstError(TypeError): pass  # Base exception class.
    class ConstCaseError(ConstError): pass

    def __setattr__(self, name, value):
        if name in self.__dict__:
            raise self.ConstError("Can't change const.%s" % name)
        if not name.isupper():
            raise self.ConstCaseError('const name %r is not all uppercase' % name)
        self.__dict__[name] = value

# Replace module entry in sys.modules[__name__] with instance of _const
# (and create additional reference to it to prevent its deletion -- see
#  https://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name)
import sys
_ref, sys.modules[__name__] = sys.modules[__name__], _const()

if __name__ == '__main__':
    import __main__  as const  # Test this module...

    try:
        const.Answer = 42  # Not OK to create mixed-case attribute name.
    except const.ConstCaseError as exc:
        print(exc)
    else:  # Test failed - no ConstCaseError exception generated.
        raise RuntimeError("Mixed-case const names should't be allowed!")

    try:
        const.ANSWER = 42  # Should be OK, all uppercase.
    except Exception as exc:
        raise RuntimeError("Changing existing const attribute should't be allowed!")
    else:  # Test succeeded - no exception generated.
        print('const.ANSWER set to %d raised no exception' % const.ANSWER)

    try:
        const.ANSWER = 17  # Not OK, attempt to change defined constant.
    except const.ConstError as exc:
        print(exc)
    else:  # Test failed - no ConstError exception generated.
        raise RuntimeError("Shouldn't be able to change const attribute!")

Выход:

const name 'Answer' is not all uppercase
const.ANSWER set to 42 raised no exception
Can't change const.ANSWER

Ответ 2

Python - очень открытый язык и не содержит ключевое слово final. Python дает вам больше свободы делать что-то и предполагает, что вы знаете, как все должно работать. Поэтому предполагается, что люди, использующие ваш код, будут знать, что SOME_CONSTANT не следует назначать значение в некоторой случайной точке кода.

Если вы действительно этого хотите, вы можете заключить константу внутри функции getter.

def getConstant()
  return "SOME_VALUE"

Ответ 3

Вы можете обернуть глобальные переменные в объект и переопределить метод.__ setattr__. Затем вы можете запретить установку атрибутов, которые уже установлены. Однако это не касается сложных объектов, которые передаются по ссылке. Вам нужно будет сделать мелкие/глубокие копии этих объектов абсолютно уверенными, что они не могут быть изменены. Если вы используете новые классы стиля, вы можете переопределить объект.__ getattribute __ (self, name), чтобы сделать копии.

class State(object):
    def __init__(self):
        pass

    def __setattr__(self, name, value):
        if name not in self.__dict__:
            self.__dict__[name] = value

** Обычно я так не волнуюсь, если кто-то попытается очень сильно сломать мой код. Я считаю, что превышение __setattr__ является достаточным (особенно если вы делаете исключение), чтобы предупредить того, кто играет с кодом, который цель для государства должна быть прочитана только. Если кто-то все еще чувствует необходимость изменения состояния, то любое поведение undefined, с которым они сталкиваются, не является моей ошибкой.