在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"
或者,有没有办法计算对象中的变量数量?
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"
我想到这一点的唯一方法是使用字典或列表:
class foo:
def __int__(self):
self.dict = {'foo':1, 'bar':2}
self.len = 2
def chk():
return self.len == len(self.list)
但是,这样做对python来说相当麻烦。 (obj.dict [ '富'])。如果可能的话,我更喜欢obj.foo。
我希望这样做,以便在我改变现有变量时,我从不会意外地声明变量。
f = foo()
f.somename = 3
...
f.simename = 4 #this is a typo
if f.somename == 3:
solve_everything()
先谢谢。
答案 0 :(得分: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)
测试:
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
结果:
It works! 1 42 28
另请参阅gnibblers answer,它将这个概念整齐地包装在类装饰器中,因此它不会使类定义混乱,并且可以在几个类中重复使用而不会重复代码。
修改强>
一年后回到这个答案,我意识到上下文管理器可能会更好地解决这个问题。这是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
以下是如何使用它:
@restrict_attributes
class A(object):
def __init__(self):
with self.declare_attributes():
self.a = 1
self.b = 2
self.c = 3
因此,只要您想设置新属性,只需使用上面的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
答案 1 :(得分:4)
在python中,有没有办法阻止在定义对象后添加新的类变量?
是。 __slots__
。但做仔细阅读说明。
答案 2 :(得分:3)
def freeze(cls):
_init = cls.__init__
def init(self, *args, **kw):
_init(self, *args, **kw)
self.freeze = True
cls.__init__ = init
def _setattr(self, attr, value):
if getattr(self, "freeze", None) and (attr=="freeze" or not hasattr(self, attr)):
raise AttributeError("You shall not set attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
@freeze
class foo(object):
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"
答案 3 :(得分:2)
使用__slots__
类属性阻止添加新属性:
class foo(object):
__slots__ = ['a', 'b', 'c']
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
bar = foo()
try:
bar.d = 4
except Exception as e:
print(e,"I want this to always print")
计算属性:
print(len([attr for attr in dir(bar) if attr[0] != '_' ]))
答案 4 :(得分:1)
使用它来计算实例的no.of属性:
>>> class foo:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
>>> bar=foo()
>>> bar.__dict__
{'a': 1, 'c': 3, 'b': 2}
>>> len(bar.__dict__) #returns no. of attributes of bar
3
答案 5 :(得分:1)
您的意思是新的类变量或新的实例变量吗?后者看起来像你的意思,而且更容易做到。
Per Ignacio Vazquez-Abrams的回答,__slots__
可能就是你想要的。只需在您的类中执行__slots__ = ('a', 'b', 'c')
,这将阻止创建任何其他属性。请注意,这仅适用于您的类的实例 - 仍然可以设置类级属性,并且子类可以添加它们所需的任何属性。他是对的 - 有一些奇怪之处,所以在开始向各处插槽之前阅读链接的文档。
如果您不使用广告位,则return len(vars(self))
可用作建议的count
广告的正文。
作为插槽的替代方法,您可以定义__setattr__
拒绝任何不属于“已知商品”列表的属性,或者在frozen
属性设置为True后拒绝任何新属性__init__
等的结束。这很难做到,但更灵活。
如果您确实希望实例在初始化后完全是只读的,并且您使用的是最新版本的Python,请考虑定义namedtuple
或其子类。元组子类也有一些局限性;如果你需要走这条路,我可以扩展它,但我会坚持使用插槽,除非你有理由不这样做。
答案 6 :(得分:0)
假设您现在希望您的类具有一组固定的可变属性和不可变属性?我已经攻击gnibbler's answer以在init:
之后使类属性不可变def frozenclass(cls):
""" Modify a class to permit no new attributes after instantiation.
Class attributes are immutable after init.
The passed class must have a superclass (e.g., inherit from 'object').
"""
_init = cls.__init__
def init(self, *args, **kw):
_init(self, *args, **kw)
self.freeze = True
cls.__init__ = init
def _setattr(self, attr, value):
if getattr(self, "freeze", None):
if attr=="freeze" or not hasattr(self, attr):
raise AttributeError("You shall not create attributes!")
if hasattr(type(self), attr):
raise AttributeError("You shall not modify immutable attributes!")
super(cls, self).__setattr__(attr, value)
cls.__setattr__ = _setattr
return cls
一个例子:
@frozenclass
class myClass(object):
""" A demo class."""
# The following are immutable after init:
a = None
b = None
c = None
def __init__(self, a, b, c, d=None, e=None, f=None):
# Set the immutable attributes (just this once, only during init)
self.a = a
self.b = b
self.c = c
# Create and set the mutable attributes (modifyable after init)
self.d = d
self.e = e
self.f = f