12

[元のバージョンのコードはひどくめちゃくちゃでした。コードを修正した後でも、いくつかの非常に紛らわしいタイプミスが投稿に残っていました。私も最終的にそれらすべてを修正したと思います。深くお詫び申し上げます。】

以下の 2 つの呼び出しはalias、変数に関連付けられたオブジェクトmy_own_idが 2 つの呼び出し間で変更されるため、異なる出力を生成します。

>>> def my_own_id():
...     me = my_own_id
...     return id(me)
... 
>>> alias = my_own_id
>>> alias()
4301701560
>>> my_own_id = None
>>> alias()
4296513024

その出力がその後の変数の再定義に関して不変のままになるようmeに、の定義で何を割り当てることができますか? (IOW、内部変数が常に同じ関数オブジェクトを参照するように?)my_own_idmy_own_idme

(現在のフレームを取得できます(を使用inspect.currentframe())が、現在の関数への参照ではなく、現在のコード オブジェクトへの参照のみが含まれています。)

PSこの質問の動機は、Pythonをよりよく知ることだけです。

4

7 に答える 7

3

my_own_id参照はグローバル名前空間ディクショナリで検索されるように思われる'my_own_id'ため、常に関数定義で使用される名前になります。その名前は異なる値に割り当てることができるため、取得される値も変更される可能性があります。デフォルトの引数を作成meすると、関数定義で関数自体に割り当てて、実際の関数への参照を保持できます。

元の関数自体を最初の引数として暗黙的に渡すこのデコレータを使用できます。

>>> from functools import wraps
>>> def save_id(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(func, *args, **kwargs)
        return wrapper


>>> @save_id
def my_own_id(me): # me is passed implicitly by save_id
    return id(me)

>>> alias = my_own_id
>>> alias()
40775152
>>> my_own_id = 'foo'
>>> alias()
40775152
于 2012-08-10T20:37:50.490 に答える
2

実際、関数名のみに依存している場合、その名前がグローバル変数空間 (関数が定義されたモジュール内) で上書きされている場合、関数の名前を使用した参照は失敗します。

より簡単で保守しやすい方法はnonlocal、関数自体への参照を含む変数を提供するデコレータを作成することです。

from functools import wraps

def know_thyself(func):
   @wraps(func):
   def new_func(*args, **kwargs):
        my_own_id = func
        return func(*args, **kwargs)
   return new_func

また、次のように使用できます。

>>> @know_thyself
... def my_own_id():
...     me = my_own_id
...     return id(me)
... 

フレームのイントロスペクションを使用し、同じオブジェクト コードを再利用して新しい関数を再構築するという、このクリーンな方法とはほど遠い別のアプローチが考えられます。Python の自己参照ラムダ式に関するこの投稿でこれを使用しました: http://metapython.blogspot.com.br/2010/11/recursive-lambda-functions.html

于 2012-08-10T19:58:21.013 に答える
1
import inspect
def _this_fn():
    try:
        frame = inspect.currentframe().f_back
        code = frame.f_code
        return frame.f_globals[code.co_name]
    finally:
        del code
        del frame

def myfunc(*parms):
    print _this_fn()

>>> myfunc(1)
<function myfunc at 0x036265F0>
>>> myfunc
<function myfunc at 0x036265F0>
于 2013-02-18T09:55:28.130 に答える
1

デコレータのアプローチはおそらく最良のものです。ここにいくつかの楽しみがあります:

  1. 関数の引数の 1 つをハイジャックして、静的変数を提供します。

    def fn(fnid=None):
        print "My id:", fnid
    fn.func_defaults = (id(fn),)
    
  2. ここで現在の関数を取得するにはいくつかの方法があります:現在の関数を変数に取得する Python コード? ; これらのほとんどは、さまざまな場所で currentframe.f_code を検索する必要があります。これらは、元の機能を変更することなく機能します。

于 2012-08-10T20:15:58.227 に答える
1

関数を呼び出すことを気にしない場合 (目的の関数をグローバル スコープに取得するため)、関数をラップしてその定義を保護できます。

>>> def be_known():
...     global my_own_id
...     def _my_own_id():
...         return id(_my_own_id)
...     my_own_id = _my_own_id
... 
>>> be_known()
>>> my_own_id()
140685505972568
>>> alias, my_own_id = my_own_id, None
>>> alias()
140685505972568

保護された関数は、グローバル名ではなく、非ローカル名でそれ自体を呼び出す必要があることに注意してください。

于 2012-08-10T20:11:20.160 に答える
0

Lukeにはアイデアがありましたが、それを開発したようには見えませんでした。変更可能なデフォルト パラメータを使用して、関数オブジェクトの値を保持します。デフォルトのパラメーター値は、関数が定義されたときに一度だけ評価され、その後は以前の値を保持します。

>>> def my_own_id(me=[None]):
    if not me[0]:
        me[0] = my_own_id
    return id(me[0])

>>> alias = my_own_id
>>> alias()
40330928
>>> my_own_id = None
>>> alias()
40330928

これには、パラメーターを指定して関数を呼び出さないように注意する必要があります。

于 2012-08-10T20:59:35.853 に答える
0

範囲によるよ

>>> def foo():
...     x = foo
...     print x
... 
>>> foo()
<function foo at 0x10836e938>
>>> alias = foo
>>> alias()
<function foo at 0x10836e938>
>>> foo = None
>>> alias()
None
>>> foo = []
>>> alias()
[]
>>> del foo
>>> alias()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
NameError: global name 'foo' is not defined
>>> 
于 2012-08-10T20:00:31.917 に答える