Skip to content

Instantly share code, notes, and snippets.

@momijiame
Last active January 4, 2022 14:02
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save momijiame/bebf8d4c16fc0916fd80530ebe961525 to your computer and use it in GitHub Desktop.
Save momijiame/bebf8d4c16fc0916fd80530ebe961525 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""関数の中でグローバル変数を使えなくするデコレータ"""
from typing import List
from typing import Dict
from typing import Optional
from typing import Callable
from typing import Any
from types import FunctionType
import inspect
from functools import partial
def globals_with_module_and_callable(globals_: Optional[Dict[str, Any]] = None,
excepts: Optional[List[str]] = None) -> Dict[str, Any]:
"""モジュールと呼び出し可能オブジェクトだけが含まれるグローバルシンボルテーブルを返す関数
:param globals_: 別のモジュールからインポートして使う場合、globals() の結果を入れる
:param excepts: 例外的に含めたいグローバル変数の名前を含むリストオブジェクト
:return: モジュールと呼び出し可能オブジェクトだけを含む辞書オブジェクト
"""
def need(name, attr):
if name in excepts:
return True
if inspect.ismodule(attr):
return True
if callable(attr):
return True
return False
if globals_ is None:
globals_ = globals()
if excepts is None:
excepts = []
filtered_globals = {
name: attr
for name, attr in globals_.items()
if need(name, attr)
}
return filtered_globals
def bind_globals(globals_: Dict[str, Any]) -> Callable:
"""呼び出し可能オブジェクトを特定のグローバルシンボルテーブルで実行するようにラップする関数を返すデコレータ
:param globals_: 制約したいグローバルシンボルテーブル
:returns: ラップするのに使う関数オブジェクト
"""
def _bind_globals(func: FunctionType) -> FunctionType:
bound_func = FunctionType(code=func.__code__,
globals=globals_,
name=func.__name__,
argdefs=func.__defaults__,
closure=func.__closure__,
)
return bound_func
return _bind_globals
def no_global_variable_decorator(globals_: Optional[Dict[str, Any]] = None):
"""グローバル変数を使えなくするデコレータを返す高階関数"""
partialled = partial(globals_with_module_and_callable, globals_=globals_)
def _no_global_variable(excepts: Optional[List[str]] = None):
partialled_globals_ = partialled(excepts=excepts)
bound_func = bind_globals(globals_=partialled_globals_)
return bound_func
return _no_global_variable
# 上記の関数群を別のモジュールに定義したいときは、ここから分ける
# インポートしたモジュールで globals() の内容を渡してデコレータを作る
# from enhanced_noglobal import no_global_variable_decorator
no_global_variable = no_global_variable_decorator(globals_=globals())
# グローバル変数
a = 1
b = 2
# 作ったデコレータで関数を修飾すれば、その中ではグローバル変数が使えなくなる
@no_global_variable(excepts=['b'])
def f(c, d=4):
# print(a) # コメントアウトを外すと NameError になる
print(b) # 引数で許容しているのでグローバル変数でも例外的に使える
print(c)
print(d) # デフォルト値付きでも大丈夫
def main():
f(3)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment