python类和类型检查中的不可变属性

Immutable attribute in python class and type check

我正在寻找一种在Python中构建类的方法:

  • setter在赋值前检查值的类型
  • 无法添加新的类属性

就目前而言,我发现了这两个装饰师:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    def getter_setter_gen(name, type_):
        def getter(self):
            return getattr(self,"__" + name)
        def setter(self, value):
            print"setter", value
            if not isinstance(value, type_):
                raise TypeError("%s attribute must be set to an instance of %s" % (name, type_))
            setattr(self,"__" + name, value)
        return property(getter, setter)


    def auto_attr_check(cls):
        new_dct = {}
        print"auto_attr_check", cls.__dict__.items()
        for key, value in cls.__dict__.items():
            if isinstance(value, type):
                value = getter_setter_gen(key, value)
            new_dct[key] = value
        # Creates a new class, using the modified dictionary as the class dict:
        n = type(cls)(cls.__name__, cls.__bases__, new_dct)
        return n

1
2
3
4
5
6
7
8
9
10
11
12
13
    def froze_it(cls):
        def frozensetattr(self, key, value):
            print key
            key = ''+key
            print(dir(self))
            print key
            if not hasattr(self, key):
                raise TypeError("Class {} is frozen. Cannot set {} = {}"
                  .format(cls.__name__, key, value))
            else:
                object.__setattr__(self, key, value)
        cls.__setattr__ = frozensetattr
        return cls

但是我很难加入这两种方法。你能帮助我吗?你有想法吗?谢谢


您遇到的问题是,您的property正在使用setattr来设置属性的值,但您已经覆盖了__setattr__,这样它就永远不会成功。对于主属性名和作为属性基础的实际属性的带下划线前缀的名称(如果setter代码可以做到这一点),hasattr检查将失败。

我建议两个补充的解决方案。首先,更改hasattr检查以更加小心。如果失败,还应检查属性对象的类dict:

1
2
3
4
5
6
    def frozensetattr(self, key, value):
        if not hasattr(self, key) and type(cls.__dict__.get(key)) is not property:
            raise TypeError("Class {} is frozen. Cannot set {} = {}"
              .format(cls.__name__, key, value))
        else:
            object.__setattr__(self, key, value)

第二个修复是对setter函数的修复,它应该绕过底层属性的__setattr__方法:

1
2
3
4
5
    def setter(self, value):
        print"setter", value
        if not isinstance(value, type_):
           raise TypeError("%s attribute must be set to an instance of %s" % (name, type_))
        object.__setattr__(self,"__" + name, value)

最后一个注意事项:您的property创建的属性名上的双下划线前缀不会调用名称管理,我怀疑这是您想要的。名称管理只在编译时发生。当一个方法包含像__bar这样的名称时,编译器会将该名称转换为_NameOfTheClass__bar(其中NameOfTheClass是定义该方法的类的名称)。

在运行时,代码的行为与被损坏的名称直接写入源代码的行为完全相同。这意味着__setattr__和friends不会以任何特殊的方式处理损坏的名称(或双下划线前缀名称,如果它们是作为常规变量名而不是字符串写入的话,编译器会损坏它们)。所以在动态创建的变量上进行名称管理是不容易的。

如果您只想将名称非正式地标记为private(也就是说,它们不是公共API的一部分),那么您可能应该使用一个前导下划线。