まず、おそらくこれをやりたくないでしょう。Martijn Pieters が指摘しているように、トップレベルの関数やクラスなど、多くのものはグローバルです。
呼び出し不可能なグローバルのみに対してこれをフィルタリングできます。関数、クラス、C 拡張モジュールからインポートした組み込み関数またはメソッドなどは呼び出し可能です。import
モジュール (グローバルなものはすべて) を除外することもできます。それでも、たとえば、関数をdef
. そのためにある種のホワイトリストを追加できます (これにより、警告なしで使用できるグローバルな「定数」を作成することもできます)。本当に、あなたが思いついたものはせいぜい非常に大雑把なガイドであり、絶対的な警告として扱いたいものではありません.
また、どのように行っても、明示的なアクセス (global
ステートメントを使用) ではなく、暗黙的なグローバル アクセスを検出しようとするのは非常に困難になるため、それは重要ではないことを願っています。
ソース レベルでグローバル変数のすべての暗黙的な使用を検出する明確な方法はありません。
ただし、インタープリター内からのリフレクションを使用するのは非常に簡単です。
モジュールのドキュメントにはinspect
、さまざまなタイプの標準メンバーを示す素敵なチャートがあります。Python 2.xとPython 3.xでは名前が異なるものがあることに注意してください。
この関数は、両方のバージョンのバインドされたメソッド、バインドされていないメソッド、関数、またはコード オブジェクトによってアクセスされるすべてのグローバル名のリストを取得します。
def get_globals(thing):
thing = getattr(thing, 'im_func', thing)
thing = getattr(thing, '__func__', thing)
thing = getattr(thing, 'func_code', thing)
thing = getattr(thing, '__code__', thing)
return thing.co_names
呼び出し可能でないもののみを処理したい場合は、それをフィルタリングできます。
def get_callable_globals(thing):
thing = getattr(thing, 'im_func', thing)
func_globals = getattr(thing, 'func_globals', {})
thing = getattr(thing, 'func_code', thing)
return [name for name in thing.co_names
if callable(func_globals.get(name))]
これは完璧ではありません (たとえば、関数のグローバルにカスタム組み込みの置換がある場合、適切に検索しません) が、おそらく十分です。
それを使用する簡単な例:
>>> def foo(myparam):
... myglobal
... mylocal = 1
>>> print get_globals(foo)
('myglobal',)
そして、非常に簡単import
にモジュールを作成し、その呼び出し可能オブジェクトを再帰的にウォークしてget_globals()
、それぞれを呼び出すことができます。これは、主要なケース (トップレベル関数、トップレベルおよびネストされたクラスのメソッド) で機能しますが、何に対しても機能しません。動的に定義されます (たとえば、関数内で定義される関数またはクラス)。
CPython だけに関心がある場合は、モジュールを使用してdis
モジュール内のすべてのバイトコード、または .pyc ファイル (またはクラスなど) をスキャンし、各操作をログに記録するという別のオプションがありますLOAD_GLOBAL
。
メソッドに対するこれの主な利点の 1 つinspect
は、まだ作成されていない場合でも、コンパイルされた関数を検出できることです。
欠点は、名前を検索する方法がないことです (名前のいくつかがまだ作成されていない場合、どのように存在する可能性がありますか?)。そのため、callable を簡単に除外することはできません。LOAD_GLOBAL
op を対応する(および関連する) op に接続するなど、凝ったことを試みることはできますがCALL_FUNCTION
、かなり複雑になり始めています。
最後に、動的にフックしたい場合は、globals
アクセスするたびに警告するラッパーにいつでも置き換えることができます。例えば:
class GlobalsWrapper(collections.MutableMapping):
def __init__(self, globaldict):
self.globaldict = globaldict
# ... implement at least __setitem__, __delitem__, __iter__, __len__
# in the obvious way, by delegating to self.globaldict
def __getitem__(self, key):
print >>sys.stderr, 'Warning: accessing global "{}"'.format(key)
return self.globaldict[key]
globals_wrapper = GlobalsWrapper(globals())
繰り返しますが、非呼び出し可能オブジェクトを非常に簡単にフィルタリングできます。
def __getitem__(self, key):
value = self.globaldict[key]
if not callable(value):
print >>sys.stderr, 'Warning: accessing global "{}"'.format(key)
return value
print
明らかに Python 3 では、ステートメントをprint
関数呼び出しに変更する必要があります。
警告の代わりに、非常に簡単に例外を発生させることもできます。または、warnings
モジュールの使用を検討することもできます。
これをさまざまな方法でコードにフックできます。最も明白なものは、新しいモジュールごとGlobalsWrapper
に、通常ビルドされglobals
た . それが C 拡張モジュールとどのように相互作用するかはわかりませんが、私の推測では、それが機能するか、無害に無視されるかのいずれかであり、おそらくどちらでも問題ありません。唯一の問題は、これがトップレベルのスクリプトに影響を与えないことです。execfile
それが重要な場合は、メイン スクリプトにGlobalsWrapper
、またはそのようなものを使用するラッパー スクリプトを作成できます。