Могу ли я предотвратить изменение объекта в Python? - PullRequest
12 голосов
/ 14 сентября 2010

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

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

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

Ответы [ 3 ]

14 голосов
/ 15 сентября 2010

Activestate имеет рецепт под названием Константы в Python от почтенного Alex Martelli для создания модуля 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("Defining a valid const attribute should 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
7 голосов
/ 14 сентября 2010

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

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

def getConstant()
  return "SOME_VALUE"
2 голосов
/ 14 сентября 2010

Вы можете обернуть ваши глобальные переменные в объект и переопределить метод объекта .__ 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__ достаточно (особенно, если вы выкидываете исключение), чтобы предупредить любого, кто играет с кодом, что целью для штата является только чтение. Если кто-то все еще чувствует необходимость изменить состояние, то с любым неопределенным поведением, с которым он сталкивается, я не виноват.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...