Вызывает исключение при обновлении атрибута 'constant' в python - PullRequest
2 голосов
/ 31 августа 2009

Поскольку у python нет понятия констант, можно ли вызвать исключение, если обновлен атрибут 'constant'? Как?

class MyClass():
    CLASS_CONSTANT = 'This is a constant'
    var = 'This is a not a constant, can be updated'

#this should raise an exception    
MyClass.CLASS_CONSTANT = 'No, this cannot be updated, will raise an exception'

#this should not raise an exception    
MyClass.var = 'updating this is fine'

#this also should raise an exception    
MyClass().CLASS_CONSTANT = 'No, this cannot be updated, will raise an exception'

#this should not raise an exception    
MyClass().var = 'updating this is fine'

Любая попытка изменить CLASS_CONSTANT как атрибут класса или как атрибут экземпляра должна вызывать исключение.

Изменение var в качестве атрибута класса или атрибута экземпляра не должно вызывать исключение.

Ответы [ 5 ]

3 голосов
/ 31 августа 2009

Настройка __setattr__ в каждом классе (например, как показано в моем старом рецепте, на который указывает ответ @ ainab и другие ответы), работает только для прекращения назначения атрибутов INSTANCE, а не атрибутов CLASS. Таким образом, ни один из существующих ответов на самом деле не удовлетворит ваше требование, как указано.

Если то, что вы просили, на самом деле именно то, что вы хотите, вы можете прибегнуть к некоторому сочетанию пользовательских метаклассов и дескрипторов, таких как:

class const(object):
  def __init__(self, val): self.val = val
  def __get__(self, *_): return self.val
  def __set__(self, *_): raise TypeError("Can't reset const!")

class mcl(type):
  def __init__(cls, *a, **k):
    mkl = cls.__class__
    class spec(mkl): pass
    for n, v in vars(cls).items():
      if isinstance(v, const):
        setattr(spec, n, v)
    spec.__name__ = mkl.__name__
    cls.__class__ = spec

class with_const:
  __metaclass__ = mcl

class foo(with_const):
  CLASS_CONSTANT = const('this is a constant')

print foo().CLASS_CONSTANT
print foo.CLASS_CONSTANT
foo.CLASS_CONSTANT = 'Oops!'
print foo.CLASS_CONSTANT

Это довольно продвинутый материал, поэтому вы можете предпочесть более простой подход __setattr__, предложенный в других ответах, несмотря на то, что он НЕ соответствует вашим требованиям, как указано (т. Е. Вы можете разумно решить ослабить свои требования, чтобы добиться простоты; - ). Но методы здесь могут все еще быть интересными: пользовательский тип дескриптора const - это другой способ (ИМХО, гораздо лучше, чем переопределение __setattr__ в каждом классе, для которого нужны некоторые константы И создание всех атрибутов констант, а не выбор и выбор ... ) заблокировать присвоение атрибуту экземпляра; остальная часть кода посвящена пользовательскому метаклассу, создающему уникальные подклассы для каждого класса, чтобы максимально использовать указанный дескриптор и получить именно ту функциональность, которую вы специально запрашивали.

2 голосов
/ 31 августа 2009

Вы можете использовать метакласс для достижения этой цели:

class ImmutableConstants(type):
    def __init__(cls, name, bases, dct):
        type.__init__(cls, name, bases, dct)

        old_setattr = cls.__setattr__
        def __setattr__(self, key, value):
            cls.assert_attribute_mutable(key)
            old_setattr(self, key, value)
        cls.__setattr__ = __setattr__

    def __setattr__(self, key, value):
        self.assert_attribute_mutable(key)
        type.__setattr__(self, key, value)

    def assert_attribute_mutable(self, name):
        if name.isupper():
            raise AttributeError('Attribute %s is constant' % name)

class Foo(object):
    __metaclass__ = ImmutableConstants
    CONST = 5
    class_var = 'foobar'

Foo.class_var = 'new value'
Foo.CONST = 42 # raises

Но вы уверены, что это реальная проблема? Вы действительно случайно устанавливаете константы повсюду? Вы можете найти большинство из них довольно легко с grep -r '\.[A-Z][A-Z0-9_]*\s*=' src/.

2 голосов
/ 31 августа 2009

Вы можете сделать что-то вроде этого: (от http://www.siafoo.net/snippet/108)

class Constants:
  # A constant variable
  foo = 1337

  def __setattr__(self, attr, value):
    if hasattr(self, attr):
      raise ValueError, 'Attribute %s already has a value and so cannot be written to' % attr
    self.__dict__[attr] = value

Тогда используйте это так:

>>> const = Constants()
>>> const.test1 = 42
>>> const.test1
42
>>> const.test1 = 43
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __setattr__
ValueError: Attribute test1 already has a value and so cannot be written to
>>> const.test1
42
1 голос
/ 31 августа 2009

Начните читать это:

http://docs.python.org/reference/datamodel.html#customizing-attribute-access

Вы в основном пишете свою собственную версию __setattr__, которая выдает исключения для некоторых атрибутов, но не для других.

0 голосов
/ 31 августа 2009

Если вы действительно хотите иметь константу, которую нельзя изменить, посмотрите на это: http://code.activestate.com/recipes/65207/

...