在Python中,计算类中的变量数或阻止添加新的类变量

d l*_*d l 3 python variables class count

在python中,有没有办法防止在定义对象后添加新的类变量?

例如:

class foo:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

bar = foo()
try:
    bar.d = 4
except Exception, e:
    print "I want this to always print"
Run Code Online (Sandbox Code Playgroud)

或者,有没有办法计算对象中的变量数?

class foo:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3
    def count(self):
        ...

bar = foo()
if bar.count() == 3:
    print "I want this to always print"
Run Code Online (Sandbox Code Playgroud)

我想到这样做的唯一方法是使用字典或列表:

class foo:
    def __int__(self):
        self.dict = {'foo':1, 'bar':2} 
        self.len  = 2
    def chk():
        return self.len == len(self.list)
Run Code Online (Sandbox Code Playgroud)

然而,这样做对于python来说感觉相当麻烦.(obj.dict [ '富']).如果可能的话,我更喜欢obj.foo.

我希望这样,以便在我意味着更改现有变量时,我从不会意外地声明变量.

f = foo()
f.somename = 3
...
f.simename = 4 #this is a typo

if f.somename == 3:
    solve_everything()
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Lau*_*low 5

我建议使用__setattr__以避免奇怪的__slots__.

在搞乱时总是要小心__setattr__,因为它负责设置所有实例属性,包括你设置的那些属性__init__.因此,它必须有一些方法知道何时允许设置属性,以及何时拒绝它.在这个解决方案中,我指定了一个特殊属性来控制是否允许新属性:

class A(object):
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3
        self.freeze = True

    def __setattr__(self, attr, value):
        if getattr(self, "freeze", False) and not hasattr(self, attr):
            raise AttributeError("You shall not set attributes!")
        super(A, self).__setattr__(attr, value)
Run Code Online (Sandbox Code Playgroud)

测试:

a = A()
try:
    a.d = 89
except AttributeError:
    print "It works!"
else:
    print "It doesn't work."
a.c = 42
print a.a
print a.c
a.freeze = False
a.d = 28
a.freeze = True
print a.d
Run Code Online (Sandbox Code Playgroud)

结果:

It works!
1
42
28

另请参阅gnibblers的答案,它将这个概念整齐地包装在类装饰器中,因此它不会使类定义混乱,并且可以在不重复代码的情况下在多个类中重用.


编辑:

一年后回到这个答案,我意识到一个上下文管理器可能会更好地解决这个问题.这是gnibbler的类装饰器的修改版本:

from contextlib import contextmanager

@contextmanager
def declare_attributes(self):
    self._allow_declarations = True
    try:
        yield
    finally:
        self._allow_declarations = False

def restrict_attributes(cls):
    cls.declare_attributes = declare_attributes
    def _setattr(self, attr, value):
        disallow_declarations = not getattr(self, "_allow_declarations", False)
        if disallow_declarations and attr != "_allow_declarations":
            if not hasattr(self, attr):
                raise AttributeError("You shall not set attributes!")
        super(cls, self).__setattr__(attr, value)
    cls.__setattr__ = _setattr

    return cls
Run Code Online (Sandbox Code Playgroud)

以下是如何使用它:

@restrict_attributes
class A(object):
    def __init__(self):
        with self.declare_attributes():
            self.a = 1
            self.b = 2
            self.c = 3
Run Code Online (Sandbox Code Playgroud)

因此,只要您想设置新属性,只需使用with上面的语句即可.它也可以从实例外部完成:

a = A()
try:
    a.d = 89
except AttributeError:
    print "It works!"
else:
    print "It doesn't work."
a.c = 42
print a.a
print a.c
with a.declare_attributes():
    a.d = 28
print a.d
Run Code Online (Sandbox Code Playgroud)