Python関数の属性-使用と乱用[終了]


195

多くの人がこの機能を認識していませんが、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の使用法です。しかし、カスタム属性はどうですか?それらを使用する正当な理由はありますか?


3
PEP 232をチェックしてください。
user140352 2009

2
これは驚くべきことですか?一般に、Pythonオブジェクトはアドホック属性をサポートしています。もちろん、そうでないものもあり、特に組み込み型のものはそうです。私にとって、これをサポートしないものは例外であり、規則ではないようです。
allyourcode

3
Djangoでの1つのアプリケーション:管理者変更リストのカスタマイズ
Grijesh Chauhan 2013

2
@GrijeshChauhanこれらのドキュメントを見た後、私はこの質問に来ました!
Alexander Suraphel、2015

5
これが閉じているのは残念ですが、関数が発生させる可能性のあるカスタム例外をアタッチして、呼び出しコードでキャッチしたときに簡単にアクセスできるようにしたいと思います。説明的な例を提供しますが、それは答えで行うのが最善です。
ウィルハーディ

回答:


153

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

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

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

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

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


2
これにはマイナス面があると思いますか?たとえば、2つのライブラリが同じアドホック属性を書き込もうとした場合はどうなりますか?
allyourcode

18
私はまさにこれを行うことを考えていました。それから私は立ち止まった。「これは悪い考えですか?」私は疑問に思いました。それから、私はSOにさまよった。ぶらぶらと回った後、私はこの質問/答えを見つけました。これが良いアイデアかどうかまだわかりません。
allyourcode

7
これは間違いなくすべての回答の関数属性の最も合法的な使用法です(2012年11月現在)。他のほとんどすべての回答では、グローバル変数の代わりに関数属性を使用しています。ただし、グローバル状態を取り除くことはできません。これは、グローバル変数の問題です。値が設定されると、変更されないため、これは異なります。それは一定です。これの良い結果は、グローバル変数に固有の同期の問題に遭遇しないことです。はい、独自の同期を提供できますが、それがポイントです。自動的に安全になるわけではありません。
allyourcode

実際、属性が問題の関数の動作を変更しない限り、それは良いことだと私は言います。比較.__doc__
Dima Tisnek

このアプローチは、Python 2. *にはない装飾機能に出力の説明を添付するためにも使用できます。
Juh_ 2013年

125

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

これは間違いなく、「虐待」の範疇に入るでしょう。


2
面白い。Pythonで静的変数を実装する他の方法はありますか?
Eli Bendersky 2008

4
-1、これはPythonのジェネレーターで実装されます。

124
これは、CとPythonの類似点を示しており、この特定の関数を作成するための最良の方法を推奨していないため、この回答に反対票を投じるかなり悪い理由です。
Robert Rossney、2009年

3
@RobertRossneyしかし、ジェネレーターを使用するのであれば、これは関数属性の不適切な使用法です。もしそうなら、これは虐待です。質問はそれらについても尋ねるので、虐待を
賛成

1
間違いなく、PEP 232、@ user140352、およびhopのコメントとこのSOの回答による
ホブ

52

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

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

36
+1この機能の乱用の良い例です。これは、質問が求めていたことの1つです。
マイケルダン

1
これはミパディの答えとどう違うのですか?intの代わりに属性値が関数であることを除いて、同じことのようです。
allyourcode

def test()、本当に必要?
Keerthana Prabhakaran

15

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

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

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


7
re匂い:これはグローバルログファイルを取り除きません。それは、別のグローバルであるログ関数にそれを追い詰めるだけです。
allyourcode

2
@allyourcode:ただし、同じモジュール内のさまざまな関数に対して多数のグローバルログファイルを用意する必要がある場合は、名前の衝突を回避するのに役立ちます。
firegurafiku 2016

11

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

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


3
クラスが便利なのに、なぜ関数をプッシュするのですか?そして、クラスが関数をエミュレートできることを忘れないでください。
muhuk 2008

1
またtime.sleep(1)、より良いos.system('sleep 1')
ボリスゴレリク

3
@bgbg True、ただしこの例は睡眠に関するものではありません。
allyourcode

これは間違いなく乱用です。ここでの関数の使用は完全に無料です。muhukはまさに正しいです。クラスの方が優れたソリューションです。
allyourcode

1
また、「これがクラスよりも優れている点は何ですか?」多くのPythonプログラマーには明らかではないこの欠点を打ち消すために。
cjs 2018

6

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

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

デコレーターはどのように役立ちますか?factory1.datatype=list古いスタイルのデコレータのように宣言のすぐ下に設定しないのはなぜですか?
Ceasar Bautista 2015

2
2つの主な違い:スタイル、複数の属性を設定する方が簡単です。あなたは間違いなく属性として設定できますが、私の意見では複数の属性で冗長になり、さらにデコレーターを拡張してさらに処理する機会を得ます(関数を使用するすべての場所ではなく、1つの場所でデフォルトが定義されている、または属性が設定された後に追加の関数を呼び出します)。これらすべての結果を達成する他の方法があります。これはよりクリーンであると思いますが、心を変えて幸せです;)
DiogoNeves

クイックアップデート:Python 3 items()では、の代わりに使用する必要がありますiteritems()
スコット平均

4

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


私はこのアイデアが好きです!計算された値をキャッシュするためのより一般的なトリックは、呼び出し側が提供するつもりのない属性のデフォルト値としてdictを使用することです-Pythonは関数を定義するときに一度だけ評価するので、そこにデータを格納して保持することができます周り。関数属性を使用することはそれほど明白ではないかもしれませんが、私にとってはそれほどハックが少ないように感じます。
Soren Bjornstad

1

これが可能である唯一の理由は、doc-stringまたは他のそのようなものを配置する論理的な場所があったためだと私は常に想定していました。私がそれをプロダクションコードに使用した場合、それを読んだほとんどのユーザーを混乱させることがわかります。


1
これは混乱を招く可能性が最も高いというあなたの主旨に同意しますが、docstringsに関しては、そうです。しかし、なぜ関数にAD-HOC属性があるのですか?docstringを保持するための属性の固定セットがある場合があります。
allyourcode

@allyourcode言語に設計された特定のアドホックケースではなく一般的なケースを使用すると、物事がより簡単になり、古いバージョンのPythonとの互換性が向上します。(たとえば、docstringを設定または操作するコードは、属性が存在しない場合を処理する限り、docstringを実行しないバージョンのPythonでも機能します。)
cjs
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.