web-dev-qa-db-ja.com

Python関数属性-使用と悪用

この機能を知っている人は多くありませんが、Pythonの関数(およびメソッド)には attributes を含めることができます。見よ:

>>> 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の使用法であり、構文規則をメソッドに関連付けます。しかし、カスタム属性はどうでしょうか?それらを使用する正当な理由はありますか?

175
Eli Bendersky

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

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

その後、私は定義することができます

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

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

141

関数の静的変数として使用しました。たとえば、次の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

これは間違いなくスペクトルの「乱用」の端に該当します。

117
mipadi

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

>>> def FakeObject():
...   def test():
...     print "foo"
...   FakeObject.test = test
...   return FakeObject
>>> x = FakeObject()
>>> x.test()
foo
50
defnull

私はそれらを控えめに使用しますが、かなり便利です:

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

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

14
Robert Rossney

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

#!/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

10
Kevin Little

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

4
unbeknown

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

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
2
DiogoNeves

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

1
Dale Reidy