223

この機能を知っている人はあまりいませんが、Python の関数 (およびメソッド) は属性を持つことができます。見よ:

>>> def foo(x):
...     pass
...     
>>> foo.score = 10
>>> dir(foo)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name', 'score']
>>> foo.score
10
>>> foo.score += 1
>>> foo.score
11

Python でこの機能を使用したり悪用したりする可能性は何ですか? 私が知っている良い使い方の 1 つは、PLYが構文規則をメソッドに関連付けるために docstring を使用することです。しかし、カスタム属性はどうですか? それらを使用する正当な理由はありますか?

4

8 に答える 8

172

私は通常、注釈のストレージとして関数属性を使用します。C# のスタイルで書きたいとします (特定のメソッドが Web サービス インターフェイスの一部であることを示します)。

class Foo(WebService):
    @webmethod
    def bar(self, arg1, arg2):
         ...

それから私は定義することができます

def webmethod(func):
    func.is_webmethod = True
    return func

次に、Web サービス呼び出しが到着すると、メソッドを検索し、基になる関数に is_webmethod 属性があるかどうかを確認し (実際の値は関係ありません)、メソッドが存在しないか、Web 経由で呼び出されることを意図していない場合は、サービスを拒否します。

于 2008-12-03T18:06:51.230 に答える
142

関数の静的変数として使用しました。たとえば、次の C コードがあるとします。

int fn(int i)
{
    static f = 1;
    f += i;
    return f;
}

Python でも同様に関数を実装できます。

def fn(i):
    fn.f += i
    return fn.f
fn.f = 1

これは間違いなく、スペクトルの「乱用」の終わりに分類されます。

于 2008-12-03T19:23:38.660 に答える
60

JavaScriptの方法でオブジェクトを実行できます...意味がありませんが、機能します;)

>>> def FakeObject():
...   def test():
...     print "foo"
...   FakeObject.test = test
...   return FakeObject
>>> x = FakeObject()
>>> x.test()
foo
于 2008-12-04T13:53:06.890 に答える
16

私は控えめに使用していますが、かなり便利です。

def log(msg):
   log.logfile.write(msg)

モジュール全体で使用logし、設定するだけで出力をリダイレクトできるようになりましたlog.logfile。それを達成する方法は他にもたくさんありますが、これは軽量で簡単です。初めてやったときは変なにおいがしましたが、グローバルlogfile変数を持つよりもいいにおいがすると信じるようになりました.

于 2009-10-07T18:20:06.360 に答える
14

関数属性を使用して、コードと関連データをまとめてラップする軽量クロージャーを作成できます。

#!/usr/bin/env python

SW_DELTA = 0
SW_MARK  = 1
SW_BASE  = 2

def stopwatch():
   import time

   def _sw( action = SW_DELTA ):

      if action == SW_DELTA:
         return time.time() - _sw._time

      elif action == SW_MARK:
         _sw._time = time.time()
         return _sw._time

      elif action == SW_BASE:
         return _sw._time

      else:
         raise NotImplementedError

   _sw._time = time.time() # time of creation

   return _sw

# test code
sw=stopwatch()
sw2=stopwatch()
import os
os.system("sleep 1")
print sw() # defaults to "SW_DELTA"
sw( SW_MARK )
os.system("sleep 2")
print sw()
print sw2()

1.00934004784

2.00644397736

3.01593494415

于 2008-12-03T20:27:41.853 に答える
11

関数属性を簡単に設定するために、このヘルパー デコレータを作成しました。

def with_attrs(**func_attrs):
    """Set attributes in the decorated function, at definition time.
    Only accepts keyword arguments.
    E.g.:
        @with_attrs(counter=0, something='boing')
        def count_it():
            count_it.counter += 1
        print count_it.counter
        print count_it.something
        # Out:
        # >>> 0
        # >>> 'boing'
    """
    def attr_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            return fn(*args, **kwargs)

        for attr, value in func_attrs.iteritems():
            setattr(wrapper, attr, value)

        return wrapper

    return attr_decorator

ユース ケースは、ファクトリのコレクションを作成し、関数のメタ レベルで作成できるデータ型をクエリすることです。
例(非常にばかげたもの):

@with_attrs(datatype=list)
def factory1():
    return [1, 2, 3]

@with_attrs(datatype=SomeClass)
def factory2():
    return SomeClass()

factories = [factory1, factory2]

def create(datatype):
    for f in factories:
        if f.datatype == datatype:
            return f()
    return None
于 2015-03-26T18:50:05.743 に答える
6

計算済みの値をキャッシュするために関数の属性を使用することがあります。このアプローチを一般化する汎用デコレータを使用することもできます。このような関数の並行性の問題と副作用に注意してください。

于 2008-12-04T08:43:52.723 に答える
1

これが可能な唯一の理由は、doc-string などを配置するための論理的な場所があったからだと、私はいつも思っていました。私がそれを本番コードに使用した場合、それを読んだほとんどの人が混乱することを私は知っています.

于 2009-01-16T18:25:23.187 に答える