0

一部のメソッドの実行をブロックするラッパーを作成しようとしています。古典的な解決策は、次のパターンを使用することです。

class RestrictingWrapper(object):
    def __init__(self, w, block):
        self._w = w
        self._block = block
    def __getattr__(self, n):
        if n in self._block:
            raise AttributeError, n
        return getattr(self._w, n)

このソリューションの問題は、すべての呼び出しで発生するオーバーヘッドであるため、MetaClass を使用して同じタスクを実行しようとしています。これが私の解決策です:

class RestrictingMetaWrapper(type):
    def __new__(cls, name, bases, dic):
        wrapped = dic['_w']
        block = dic.get('_block', [])

        new_class_dict = {}
        new_class_dict.update(wrapped.__dict__)
        for attr_to_block in block:
            del new_class_dict[attr_to_block]
        new_class_dict.update(dic)

        return type.__new__(cls, name, bases, new_class_dict)

シンプルなクラスで完璧に動作します:

class A(object):
    def __init__(self, i):
        self.i = i
    def blocked(self):
        return 'BAD: executed'
    def no_blocked(self):
        return 'OK: executed'
class B(object):
    __metaclass__ = RestrictingMetaWrapper
    _w = A
    _block = ['blocked']

b= B('something')
b.no_blocked  # 'OK: executed'
b.blocked     # OK: AttributeError: 'B' object has no attribute 'blocked'

ndarray問題は、numpyのような「より複雑な」クラスで発生します。

class NArray(object):
    __metaclass__ = RestrictingMetaWrapper
    _w = np.ndarray
    _block = ['max']

na = NArray()        # OK
na.max()             # OK: AttributeError: 'NArray' object has no attribute 'max'
na = NArray([3,3])   # TypeError: object.__new__() takes no parameters
na.min()             # TypeError: descriptor 'min' for 'numpy.ndarray' objects doesn't apply to 'NArray' object

他のクラス (例: pandas.Series) は、指定されたメソッドをブロックしないなどの奇妙なエラーが発生するため、メタクラスが適切に定義されていないと思います。

エラーの場所を見つけることができますか? この問題を解決するための他のアイデアはありますか?

更新: nneonneo のソリューションはうまく機能しますが、ラップされたクラスは、クラス定義内のいくつかの黒魔術でブロッカーを壊す可能性があるようです。

nneonneo のソリューションを使用する:

import pandas

@restrict_methods('max')
class Row(pandas.Series):
    pass

r = Row([1,2,3])
r.max()        # BAD: 3      AttributeError expected  
4

1 に答える 1

1

で述べているようにTypeError、 (および関連する関数) は;minのインスタンスでのみ機能します。np.ndarrayしたがって、新しいサブクラスは、ラップしようとしているクラスから継承する必要があります。

次に、基本クラスを拡張するため、メソッドを適切な記述子に置き換える必要があります。

class RestrictedMethod(object):
    def __get__(self, obj, objtype):
        raise AttributeError("Access denied.")

class RestrictingMetaWrapper(type):
    def __new__(cls, name, bases, dic):
        block = dic.get('_block', [])

        for attr in block:
            dic[attr] = RestrictedMethod()

        return type.__new__(cls, name, bases, dic) # note we inject the base class here

class NArray(np.ndarray):
    __metaclass__ = RestrictingMetaWrapper
    _block = ['max']

注: 進取の気性に富んだアプリケーションは、基本クラスのメソッド (例: ) を介して「制限された」機能にアクセスできますnp.ndarray.max(na)

編集: ラッパーを簡素化し、透過的にサブクラス化できるようにしました。

これはすべて、クラス デコレータを使用してより簡単な方法で実行できることに注意してください。

class RestrictedMethod(object):
    def __get__(self, obj, objtype):
        raise AttributeError("Access denied.")

def restrict_methods(*args):
    def wrap(cls):
        for attr in args:
            setattr(cls, attr, RestrictedMethod())
        return cls
    return wrap

@restrict_methods('max', 'abs')
class NArray(np.ndarray):
    pass
于 2012-09-25T04:29:28.557 に答える