1

私はPythonでObjective-Cのカテゴリをクリーンに実装しようとしていますが、私の同様の質問に対するこの答えを見つけました。以下のコードをコピーしました:

カテゴリ.py

class category(object):
    def __init__(self, mainModule, override = True):
        self.mainModule = mainModule
        self.override = override

    def __call__(self, function):
        if self.override or function.__name__ not in dir(self.mainModule):
            setattr(self.mainModule, function.__name__, function)

しかし、私は名前空間を無駄にしたくありません。この`categories'を使用することにより、以下のようなNoneTypeオブジェクトとして変数が残ります。

>>> from categories import category
>>> class Test(object):
...     pass
... 
>>> @category(Test)
... def foobar(self, msg):
...     print msg
... 
>>> test = Test()
>>> test.foobar('hello world')
hello world
>>> type(foobar)
<type 'NoneType'>
>>> 

以下のようにしたい

>>> from categories import category
>>> class Test(object):
...     pass
... 
>>> @category(Test)
... def foobar(self, msg):
...     print msg
... 
>>> test = Test()
>>> test.foobar('hello world')
hello world
>>> type(foobar)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foobar' is not defined
>>> 

以下のように自動的に削除する方法はありますか?

    def __call__(self, function):
        if self.override or function.__name__ not in dir(self.mainModule):
            setattr(self.mainModule, function.__name__, function)
            del(somewhere.function.__name__)

sys._getframe私はそれが私にいくつかの有用な情報を与えることを発見しました。しかし、私は自分でそれを作ることができませんでした。

4

2 に答える 2

3

いいえ、それを自動的に行う方法はありません。その後、名前を手動で削除する必要があります。 categoryこれがデコレータです。つまり、

@category(Test)
def f():
    ...

と同じです

def f():
   ...
f = category(Test)(f)

内側から外側のスコープの名前を削除できたとしても、デコレータの実行categoryにその名前がリバウンドされるため、十分ではありません。

デコレータ構文の乱用で境界線にリンクしたコード。デコレータは、デコレータがデコレートする関数を変更または拡張する方法を提供することを目的としていますが、そのコードはデコレータの副作用(つまり、クラスのメソッドとして関数を割り当てる)に依存し、関数を破棄します。ただし、Noneを返すことによってのみ「破棄」できるため、ご覧のとおり、Noneは関数の名前にバインドされたままになります。

その質問に対する投票数の多い回答のアドバイスに従い、クラスに新しいメソッドを割り当てることをお勧めします。Pythonのカテゴリのような「インフラストラクチャ」は実際には必要ありません。必要なときに、既存のクラスに新しいメソッドを直接追加できるからです。

于 2013-03-06T07:28:52.070 に答える
0

私はBrenBarnが言ったことに完全に同意しますが、関数の削除を後のステップに分割することができます。問題は、デコレータが実行された後、変数が再割り当てされることです。したがって、デコレータ自体の内部で削除を実行することはできません。

ただし、関数を覚えて、後でモジュールから削除することはできます。

class category(object):
    functions = []

    def __init__(self, mainModule, override = True):
        self.mainModule = mainModule
        self.override = override

    def __call__(self, function):
        if self.override or function.__name__ not in dir(self.mainModule):
            setattr(self.mainModule, function.__name__, function)
            self.functions.append((inspect.getmodule(function), function.__name__))
            return self.dummy

    @staticmethod
    def dummy():
        pass

    @classmethod
    def cleanUp(cls):
        for module, name in cls.functions:
            if hasattr(module, name) and getattr(module, name) == cls.dummy:
                delattr(module, name)
        cls.functions = []

このcategoryタイプは、装飾する関数を記憶し、後でクリーンアップするためにそれらが属する名前とモジュールを格納します。デコレータは特別なダミー関数も返すため、クリーンアップによって変数が後で再割り当てされないようにすることができます。

>>> class Test(object): pass
>>> @category(Test)
def foobar(self, msg):
    print(msg)

>>> @category(Test)
def hello_world(self):
    print('Hello world')

>>> test = Test()
>>> test.foobar('xy')
xy
>>> test.hello_world()
Hello world

>>> type(foobar)
<class 'function'>
>>> type(hello_world)
<class 'function'>

>>> category.cleanUp()

>>> type(foobar)
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    type(foobar)
NameError: name 'foobar' is not defined
>>> type(hello_world)
Traceback (most recent call last):
  File "<pyshell#27>", line 1, in <module>
    type(hello_world)
NameError: name 'hello_world' is not defined
于 2013-03-06T08:42:20.100 に答える