Mar*_*her 3 python metaclass immutability
在寻找一种方法来强制自定义类型的不变性并且没有找到满意的答案后,我想出了我自己的元类形式的解决方案:
class ImmutableTypeException( Exception ): pass
class Immutable( type ):
'''
Enforce some aspects of the immutability contract for new-style classes:
- attributes must not be created, modified or deleted after object construction
- immutable types must implement __eq__ and __hash__
'''
def __new__( meta, classname, bases, classDict ):
instance = type.__new__( meta, classname, bases, classDict )
# Make sure __eq__ and __hash__ have been implemented by the immutable type.
# In the case of __hash__ also make sure the object default implementation has been overridden.
# TODO: the check for eq and hash functions could probably be done more directly and thus more efficiently
# (hasattr does not seem to traverse the type hierarchy)
if not '__eq__' in dir( instance ):
raise ImmutableTypeException( 'Immutable types must implement __eq__.' )
if not '__hash__' in dir( instance ):
raise ImmutableTypeException( 'Immutable types must implement __hash__.' )
if _methodFromObjectType( instance.__hash__ ):
raise ImmutableTypeException( 'Immutable types must override object.__hash__.' )
instance.__setattr__ = _setattr
instance.__delattr__ = _delattr
return instance
def __call__( self, *args, **kwargs ):
obj = type.__call__( self, *args, **kwargs )
obj.__immutable__ = True
return obj
def _setattr( self, attr, value ):
if '__immutable__' in self.__dict__ and self.__immutable__:
raise AttributeError( "'%s' must not be modified because '%s' is immutable" % ( attr, self ) )
object.__setattr__( self, attr, value )
def _delattr( self, attr ):
raise AttributeError( "'%s' must not be deleted because '%s' is immutable" % ( attr, self ) )
def _methodFromObjectType( method ):
'''
Return True if the given method has been defined by object, False otherwise.
'''
try:
# TODO: Are we exploiting an implementation detail here? Find better solution!
return isinstance( method.__objclass__, object )
except:
return False
Run Code Online (Sandbox Code Playgroud)
然而,虽然一般方法似乎工作得相当好,但仍有一些不确定的实现细节(另请参阅代码中的 TODO 注释):
特殊方法总是根据类型而不是实例来查找。所以hasattr也必须适用于类型。例如:
>>> class A(object): pass
...
>>> class B(A): __eq__ = lambda *_: 1
...
>>> class C(B): pass
...
>>> c = C()
>>> hasattr(type(c), '__eq__')
True
Run Code Online (Sandbox Code Playgroud)
检查hasattr(c, '__eq__')会产生误导,因为它可能会错误地“捕获”自身__eq__定义的每个实例属性c,这不会充当特殊方法(请注意,在特定情况下,__eq__您将始终看到True来自的结果hasattr,因为祖先类object定义了它, 并且继承只能“添加”属性,而不能“减去”任何属性;-)。
要检查哪个祖先类首先定义了一个属性(因此当查找仅在类型上时将使用哪个确切定义):
import inspect
def whichancestor(c, attname):
for ancestor in inspect.getmro(type(c)):
if attname in ancestor.__dict__:
return ancestor
return None
Run Code Online (Sandbox Code Playgroud)
最好inspect用于此类任务,因为它比直接访问 上的__mro__属性更广泛type(c)。