web-dev-qa-db-ja.com

Pythonデコレータ

インスタンス状態の情報を必要とするデコレータ関数を使用するpythonクラスを記述しようとしています。これは意図したとおりに機能していますが、明示的にデコレータを静的メソッドにすると、次のエラー:

Traceback (most recent call last):
  File "tford.py", line 1, in <module>
    class TFord(object):
  File "tford.py", line 14, in TFord
    @ensure_black
TypeError: 'staticmethod' object is not callable

どうして?

これがコードです:

class TFord(object):
    def __init__(self, color):
        self.color = color

    @staticmethod
    def ensure_black(func):
        def _aux(self, *args, **kwargs):
            if self.color == 'black':
                return func(*args, **kwargs)
            else:
                return None
        return _aux

    @ensure_black
    def get():
        return 'Here is your shiny new T-Ford'

if __name__ == '__main__':
    ford_red = TFord('red')
    ford_black = TFord('black')

    print ford_red.get()
    print ford_black.get()

そして、もし私が行を削除した場合@staticmethod、すべてが機能しますが、理由がわかりません。最初の引数としてselfが必要ではないでしょうか?

43
wkz

これは、staticmethodの使用法ではありません。 staticmethodオブジェクトは descriptors であり、ラップされたオブジェクトを返すため、classname.staticmethodnameとしてアクセスした場合にのみ機能します。例

class A(object):
    @staticmethod
    def f():
        pass
print A.f
print A.__dict__["f"]

プリント

<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>

Aのスコープ内では、常に呼び出し可能な後者のオブジェクトを取得します。

デコレータをモジュールスコープに移動することを強くお勧めします。これはクラス内に属していないようです。クラス内に保持したい場合は、staticmethodにせず、単にクラス本体の最後にdelを付けます。外部から使用するためのものではありません。この場合のクラス。

43
Sven Marnach

Pythonクラスは、クラス宣言の内容を評価した後、実行時に作成されます。クラスは、宣言されたすべての変数と関数を特別なディクショナリに割り当て、そのディクショナリを使用してtype.__new__を呼び出すことで評価されます( カスタマイズのクラス作成 を参照) 。

そう、

class A(B):
    c = 1

以下と同等です。

A = type.__new__("A", (B,), {"c": 1})

@staticmethodを使用してメソッドに注釈を付けると、type.__new__を使用してクラスが作成された後に発生する特別な魔法があります。クラス宣言スコープ内では、@ staticmethod関数は静的メソッドオブジェクトの単なるインスタンスであり、呼び出すことはできません。デコレータはおそらく、同じモジュールのクラス定義の上で宣言する必要がありますOR別の「デコレート」モジュール内(使用しているデコレータの数によって異なります)。一般に、デコレータはクラス。1つの注目すべき例外は、プロパティクラスです( properties を参照)。クラス宣言内にデコレータがあることは、色のクラスのように:

class Color(object):

    def ___init__(self, color):
        self.color = color

     def ensure_same_color(f):
         ...

black = Color("black")

class TFord(object):
    def __init__(self, color):
        self.color = color

    @black.ensure_same_color
    def get():
        return 'Here is your shiny new T-Ford'
8
jfocht

今日、静的メソッドデコレータをクラス内でのみ使用することを望みました。

問題は、デコレータとして使用されようとしている静的メソッドは、実際には静的メソッドオブジェクトであり、呼び出し不可能であることです。

Solution:staticmethod object has method __get__これは引数を取り、実際のメソッドを返します: python documentation Python 3.5以上:

class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"

def __init__(self, f):
    self.f = f

def __get__(self, obj, objtype=None):
    return self.f

私が付いてきた最小の解決策は:

class A():
def __init__(self):
    self.n =  2

@staticmethod
def _addtoglobaltime(func):
    from functools import wraps
    @wraps(func)
    def wrapper(*args, **kwargs):
        self = args[0]
        response = func(*args, **kwargs)
        return self.n, response

    return wrapper

@_addtoglobaltime.__get__('this can be anything')
def get(self):
    return self.n**2

if __name__ == '__main__':
    a = A()
    print(a.get())

印刷します(2、4)

0