具有属性构建的不可变 class
Immutable class with attribute build-up
我有一个 class DifferentialExtension
:
class DifferentialExtension(object):
__slots__ = ('f', 'x', 'D', 'T')
def __init__(self, f=None, x=None):
/*
# some code that builds up list 'self.D'
*/
self.D = tuple(self.D)
return None
我要使 class "Immutable" 即使用 DifferentialExtension
创建的对象不允许更改属性 "D"(在 __init__
之后已完成),这些属性都不会分配给新对象。 D
不一定是list
,最后返回的时候可以是tuple
。
In [1]: DE = DifferentialExtension(log(x), x)
In [2]: DE.D
Out[2]: ((Poly(1, x, domain='ZZ'), Poly(1/x, t0, domain='ZZ(x)'))
In [3]: DE.D = (1, 5, 5) # raises Error.
是这样的吗?
class DifferentialExtension(object):
_frozen = set()
__slots__ = ('f', 'x', 'D', 'T')
def __init__(self, f=None, x=None):
self.D = 'something'
self.D = tuple(self.D)
self._frozen.add(id(self))
def __setattr__(self, attr, value):
if id(self) in self._frozen:
raise TypeError('object is frozen')
object.__setattr__(self, attr, value)
测试:
In [29]: a = DifferentialExtension('eh', 'oh')
In [30]: a.D
Out[30]: ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g')
In [31]: a.D = 'something else'
...
TypeError: object is frozen
编辑。
正如另一个答案中提到的,命名元组是执行此操作的自然方法,但由于您在构造过程中进行了一些计算,因此请使用 classmethod 作为备用构造函数:
class DifferentialExtension(namedtuple('DifferentialExtension', 'f, x, D, T')):
@classmethod
def build_me(cls, f=None, x=None):
# a bunch of code that works on D and sets D and T (if you need T)
T = 'something T'
D = 'something' ##D = tuple(D) works
return cls(f, x, D, T)
正在测试命名元组:
In [41]: DE = DifferentialExtension.build_me(f='some f value', x='some x value')
In [42]: DE.D
Out[42]: 'something'
In [43]: DE.D = 'some other thing'
...
AttributeError: can't set attribute
通常在 Python 中,您必须假设使用您的 class 的人不会试图做一些恶意的事情。因此,如果他 真的 想要更改 D
的值,他可能有充分的理由。因此,您可能不希望无法更改 D
,因为那不是 Pythonic。但是,您可能希望帮助用户避免意外更改 D
。最好使用只读 属性 来完成,如:
class DifferentialExtension(object):
def __init__(self, f=None, x=None):
self._D = 'immutable value'
return None
@property
def D(self):
return self._D
运行 这给出:
>>> d = DifferentialExtension()
>>> d.D = 'mutate'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
如果用户 真的 想要更改值,他可以通过直接访问 self._D
但是你必须假设他知道他在做什么,因为他弄乱了你内部的一个带下划线的变量。
使用 namedtuple
作为不可变对象的基础。如果您愿意,您可以扩展它们以满足您的需要。
from collections import namedtuple
class DifferentialExtension(namedtuple('DifferentialExtension', 'f x')):
def another_method(self):
print self.x
x = DifferentialExtension(1, 2)
x.f = 2.2
# AttributeError: can't set attribute
我有一个 class DifferentialExtension
:
class DifferentialExtension(object):
__slots__ = ('f', 'x', 'D', 'T')
def __init__(self, f=None, x=None):
/*
# some code that builds up list 'self.D'
*/
self.D = tuple(self.D)
return None
我要使 class "Immutable" 即使用 DifferentialExtension
创建的对象不允许更改属性 "D"(在 __init__
之后已完成),这些属性都不会分配给新对象。 D
不一定是list
,最后返回的时候可以是tuple
。
In [1]: DE = DifferentialExtension(log(x), x)
In [2]: DE.D
Out[2]: ((Poly(1, x, domain='ZZ'), Poly(1/x, t0, domain='ZZ(x)'))
In [3]: DE.D = (1, 5, 5) # raises Error.
是这样的吗?
class DifferentialExtension(object):
_frozen = set()
__slots__ = ('f', 'x', 'D', 'T')
def __init__(self, f=None, x=None):
self.D = 'something'
self.D = tuple(self.D)
self._frozen.add(id(self))
def __setattr__(self, attr, value):
if id(self) in self._frozen:
raise TypeError('object is frozen')
object.__setattr__(self, attr, value)
测试:
In [29]: a = DifferentialExtension('eh', 'oh')
In [30]: a.D
Out[30]: ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g')
In [31]: a.D = 'something else'
...
TypeError: object is frozen
编辑。 正如另一个答案中提到的,命名元组是执行此操作的自然方法,但由于您在构造过程中进行了一些计算,因此请使用 classmethod 作为备用构造函数:
class DifferentialExtension(namedtuple('DifferentialExtension', 'f, x, D, T')):
@classmethod
def build_me(cls, f=None, x=None):
# a bunch of code that works on D and sets D and T (if you need T)
T = 'something T'
D = 'something' ##D = tuple(D) works
return cls(f, x, D, T)
正在测试命名元组:
In [41]: DE = DifferentialExtension.build_me(f='some f value', x='some x value')
In [42]: DE.D
Out[42]: 'something'
In [43]: DE.D = 'some other thing'
...
AttributeError: can't set attribute
通常在 Python 中,您必须假设使用您的 class 的人不会试图做一些恶意的事情。因此,如果他 真的 想要更改 D
的值,他可能有充分的理由。因此,您可能不希望无法更改 D
,因为那不是 Pythonic。但是,您可能希望帮助用户避免意外更改 D
。最好使用只读 属性 来完成,如:
class DifferentialExtension(object):
def __init__(self, f=None, x=None):
self._D = 'immutable value'
return None
@property
def D(self):
return self._D
运行 这给出:
>>> d = DifferentialExtension()
>>> d.D = 'mutate'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
如果用户 真的 想要更改值,他可以通过直接访问 self._D
但是你必须假设他知道他在做什么,因为他弄乱了你内部的一个带下划线的变量。
使用 namedtuple
作为不可变对象的基础。如果您愿意,您可以扩展它们以满足您的需要。
from collections import namedtuple
class DifferentialExtension(namedtuple('DifferentialExtension', 'f x')):
def another_method(self):
print self.x
x = DifferentialExtension(1, 2)
x.f = 2.2
# AttributeError: can't set attribute