3

カスタムタイプの不変性を強制する方法を探し、満足のいく答えが見つからなかったので、メタクラスの形式のソリューションで自分のショットを思いつきました。

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

ただし、一般的なアプローチはかなりうまく機能しているように見えますが、実装の詳細はまだいくつかあります(コードのTODOコメントも参照してください)。

  1. 特定のメソッドが型階層のどこかに実装されているかどうかを確認するにはどうすればよいですか?
  2. どのタイプがメソッド宣言の原点であるか(つまり、どのタイプの一部としてメソッドが定義されているか)を確認するにはどうすればよいですか?
4

2 に答える 2

5

特別なメソッドは、インスタンスではなく、常にで検索されます。したがって、タイプにも適用する必要があります。例えば:hasattr

>>> class A(object): pass
... 
>>> class B(A): __eq__ = lambda *_: 1
... 
>>> class C(B): pass
... 
>>> c = C()
>>> hasattr(type(c), '__eq__')
True

チェックは、それ自体で定義されたhasattr(c, '__eq__')インスタンスごとの属性を誤って「キャッチ」する可能性があるため、誤解を招く可能性があります。これは、特別なメソッドとしては機能しません(特定のケースでは、祖先クラスが定義しているため、常にからの結果が表示されることに注意してください) 、および継承は属性を「追加」することしかできず、属性を「減算」することはできません;-)。__eq__c__eq__Truehasattrobject

どの祖先クラスが最初に属性を定義したか(したがって、ルックアップがタイプのみである場合にどの正確な定義が使用されるか)を確認するには、次のようにします。

import inspect

def whichancestor(c, attname):
  for ancestor in inspect.getmro(type(c)):
    if attname in ancestor.__dict__:
      return ancestor
  return None

の属性にinspect直接アクセスするよりも広く機能するため、このようなタスクに使用するのが最適です。__mro__type(c)

于 2010-03-28T15:22:04.243 に答える
0

このメタクラスは、「浅い」不変性を強制します。たとえば、それは妨げません

immutable_obj.attr.attrs_attr = new_value
immutable_obj.attr[2] = new_value

attrs_attrがオブジェクトによって所有されているかどうかによっては、真の不変性に違反していると見なされる場合があります。たとえば、不変タイプでは発生しない次の結果になる可能性があります。

>>> a = ImmutableClass(value)
>>> b = ImmutableClass(value)
>>> c = a
>>> a == b
True
>>> b == c
True
>>> a.attr.attrs_attr = new_value
>>> b == c
False

おそらく、 getattrをオーバーライドして、返す属性に対してある種の不変のラッパーを返すことで、この欠陥を修正することができます。複雑かもしれません。setattrの直接呼び出しをブロックすることもできますが、コードに属性を設定する属性のメソッドについてはどうでしょうか。私はアイデアを考えることができますが、それはかなりメタになります。

また、これはあなたのクラスの賢い使い方だと思いました:

class Tuple(list):
    __metaclass__ = Immutable

しかし、私が望んでいたように、それはタプルを作成しませんでした。

>>> t = Tuple([1,2,3])
>>> t.append(4)
>>> t
[1, 2, 3, 4]
>>> u = t
>>> t += (5,)
>>> t
[1, 2, 3, 4, 5]
>>> u
[1, 2, 3, 4, 5]

リストのメソッドはほとんどまたは完全にCレベルで実装されていると思います。したがって、メタクラスにはそれらの状態の変化をインターセプトする機会がないと思います。

于 2013-06-15T02:44:07.173 に答える