Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/353.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在更新'时引发异常;常数';python中的属性_Python_Exception_Attributes_Constants - Fatal编程技术网

在更新'时引发异常;常数';python中的属性

在更新'时引发异常;常数';python中的属性,python,exception,attributes,constants,Python,Exception,Attributes,Constants,由于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,

由于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_常量更改为CLASS属性或实例属性的尝试都会引发异常


将var更改为类属性或实例属性不应引发异常

如果你真的想要一个不能改变的常数,那么看看这个:

开始读这个:


基本上,您可以编写自己版本的
\uuuuu setattr\uuuuuu
,为某些属性引发异常,但对其他属性则不例外。

您可以执行以下操作: (来自)

然后像这样使用它:

>>> 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
常量=常量() >>>常数test1=42 >>>常量test1 42 >>>常数test1=43 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 文件“”,第4行,在\uuuu setattr中__ ValueError:属性test1已具有值,因此无法写入 >>>常量test1 42
在每个类中自定义
\uuuuu setattr\uuuuuu
(例如,正如@ainab的答案指向的我的老配方和其他答案所示),只会停止对实例属性的赋值,而不会停止对类属性的赋值。因此,现有答案中没有一个能够满足您的要求

如果您所要求的实际上正是您想要的,您可以使用一些混合的自定义元类和描述符,例如:

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

这是非常高级的东西,因此您可能更喜欢其他答案中建议的更简单的
\uuuuu setattr\uuuuuuu
方法,尽管它不能满足您所述的要求(即,您可能会合理地选择削弱您的要求以获得简单性;-)。但是这里的技术可能仍然很有趣:自定义描述符类型
const
是另一种阻止分配给实例属性的方法(比在每个需要一些常量的类中重写
\uuuuuuu setattr\uuuuuuu
好得多);代码的其余部分是关于一个自定义元类创建其自身唯一的每类子元类,以便最大限度地利用所述自定义描述符并实现您特别要求的确切功能。

您可以使用一个元类来实现这一点:

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/

可以很容易地找到其中的大多数属性,仍然可以通过访问来更新属性:
MyClass.CLASS\u常量
。我认为这是OP的问题,“可能”与OP发布的具体示例不同。“可能”不是问题,除非相关的程序员是恶意的反社会者。它被记录为一个常数;在单元测试之外发现诚实的错误是唯一的目的。
>>Constants.foo=45;>>>Constants.foo 45
是的,显然,类上的setattr只保护对实例属性的访问,而不是对类属性的访问。因此,正如EPiddy所建议的,您基本上需要确保使用类实例而不是类对象本身的对象。Foo().CONST=42不会引发?是的,您可以覆盖实例上的常量。如果这是一个问题,元类初始值设定项可以为类安装类似的setattr。我没有试图完成它,因为我不明白为什么它是必要的。通过静态分析(例如grep)很容易发现错误,而且如果没有解释器中适当的沙盒功能,就不可能保证安全。即使您使用C扩展名创建常量,也可以使用ctypes覆盖内存位置,或者如果您使用外部常量服务,也可以覆盖服务位置。我计划选择“Alex Martelli”答案,因为我可以使用_const从中创建子类并使用const()[class foo(with_const):],请查看他的解决方案以了解详细信息。但我认为这是一个风格的问题,其他一些开发人员可能更喜欢你的风格。我希望您能解决答案中的“Foo().CONST not raise”问题,这样,如果有人使用您的解决方案,他们就不会有这个问题。我认为你的答案是有价值的,如果你的解决方案解决了这个问题,我想投你一票。顺便说一句,我是python的初学者,我相信你在之前的评论中提出了正确的观点(这远远超出了我目前对python的理解)。我正在寻找一个python级别的解决方案来解决这个问题,主要是为了避免与我一起工作的其他新手意外地覆盖一个值不应该更改的变量。谢谢你的贡献。您的解决方案和Alex Martelli的解决方案都有助于扩展我对python的了解。添加了实例检查,并通过对元类进行子类化使常量检查可自定义。如果您的目的是捕获无意中的错误,我仍然认为最好只搜索代码以直接分配给常量。如果愿意,可以绕过我的或Alex的检查,这些检查会给代码带来复杂性和开销。而静态检查将复杂性分开,并在提交时显示错误,而不是在运行时。完美!我绝对喜欢你对无聊问题的回答。我真希望有一个邀请功能,这样我就可以问你我的问题了。谢谢。Alex Martelli和Ants Aasma的两种解决方案都有效。我选择Alex Martelli的解决方案,因为它可以在子类中使用,但一些开发人员可能也更喜欢Ants-Aasma的风格。
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