是否可以在没有 exec() 的情况下自动使模块的所有 public class 方法成为全局函数? turtle.py为例
Is it possible to automatically make all of a module's public class methods global functions without exec()? turtle.py as an example
我看到它声称 exec()
从来都不是必需的,应该始终避免。虽然在 99.9% 的时间里显然有更好的方法来做事,但我在查看 turtle.py 时发现了这个:
## The following mechanism makes all methods of RawTurtle and Turtle available
## as functions. So we can enhance, change, add, delete methods to these
## classes and do not need to change anything here.
__func_body = """\
def {name}{paramslist}:
if {obj} is None:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
{obj} = {init}
try:
return {obj}.{name}{argslist}
except TK.TclError:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
raise
"""
def _make_global_funcs(functions, cls, obj, init, docrevise):
for methodname in functions:
method = getattr(cls, methodname)
pl1, pl2 = getmethparlist(method)
if pl1 == "":
print(">>>>>>", pl1, pl2)
continue
defstr = __func_body.format(obj=obj, init=init, name=methodname,
paramslist=pl1, argslist=pl2)
exec(defstr, globals())
globals()[methodname].__doc__ = docrevise(method.__doc__)
_make_global_funcs(_tg_screen_functions, _Screen,
'Turtle._screen', 'Screen()', _screen_docrevise)
_make_global_funcs(_tg_turtle_functions, Turtle,
'Turtle._pen', 'Turtle()', _turtle_docrevise)
这段代码就是为什么使用 turtle 模块对初学者如此方便的原因。除了 Turtle class 实例根据需要自动为自己分配 Screens 之外,这种“机制”自动使每个 public class 方法成为一个全局函数。这就是为什么这样的代码,从不直接实例化任何海龟 classes,有效:
import turtle
turtle.forward(100)
turtle.mainloop()
这将实例化一个 Screen,实例化一个 Turtle,将 Screen 分配给 Turtle,调用该 Turtle 实例的方法来绘制一条线,并调用该 Screen 的方法以保持 window 打开, 都带有自动生成的全局函数。这是我见过的 exec() 的最佳用例。
有没有办法在没有 exec() 的情况下更像 python 一样做到这一点?
这里是如何在没有 exec()
的情况下复制它......有点。
exec()
仍然需要以显式方式将旧的调用签名分配给新的全局函数,没有方便的方法为函数提供新的调用签名。
然而,作为我的问题的答案,闭包用于将方法变成全局函数,调用签名必须是 *args, **kwargs
。
下面的代码似乎可以代替我发布的代码并回答我的问题标题,如何在没有 exec 的情况下复制制作方法全局函数,尽管我认为由于缺乏准确的签名,它不是更 pythonic :
def __turtle_func_closure(obj, init, name):
"""Wraps class methods as global functions."""
def func(*args, **kwargs):
"""Global function equivalent ofor class methods."""
obj_ = getattr(Turtle, obj, None)
if obj_ is None:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
obj_ = init()
try:
return getattr(obj_, name)(*args, **kwargs)
except TK.TclError:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
raise
return func
def _make_global_funcs(functions, cls, obj, init, docrevise):
for methodname in functions:
method = getattr(cls, methodname)
globals()[methodname] = __turtle_func_closure(obj, init, methodname)
globals()[methodname].__doc__ = docrevise(method.__doc__)
_make_global_funcs(_tg_screen_functions, _Screen,
'Turtle._screen', Screen, _screen_docrevise)
_make_global_funcs(_tg_turtle_functions, Turtle,
'Turtle._pen', Turtle, _turtle_docrevise)
我看到它声称 exec()
从来都不是必需的,应该始终避免。虽然在 99.9% 的时间里显然有更好的方法来做事,但我在查看 turtle.py 时发现了这个:
## The following mechanism makes all methods of RawTurtle and Turtle available
## as functions. So we can enhance, change, add, delete methods to these
## classes and do not need to change anything here.
__func_body = """\
def {name}{paramslist}:
if {obj} is None:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
{obj} = {init}
try:
return {obj}.{name}{argslist}
except TK.TclError:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
raise
"""
def _make_global_funcs(functions, cls, obj, init, docrevise):
for methodname in functions:
method = getattr(cls, methodname)
pl1, pl2 = getmethparlist(method)
if pl1 == "":
print(">>>>>>", pl1, pl2)
continue
defstr = __func_body.format(obj=obj, init=init, name=methodname,
paramslist=pl1, argslist=pl2)
exec(defstr, globals())
globals()[methodname].__doc__ = docrevise(method.__doc__)
_make_global_funcs(_tg_screen_functions, _Screen,
'Turtle._screen', 'Screen()', _screen_docrevise)
_make_global_funcs(_tg_turtle_functions, Turtle,
'Turtle._pen', 'Turtle()', _turtle_docrevise)
这段代码就是为什么使用 turtle 模块对初学者如此方便的原因。除了 Turtle class 实例根据需要自动为自己分配 Screens 之外,这种“机制”自动使每个 public class 方法成为一个全局函数。这就是为什么这样的代码,从不直接实例化任何海龟 classes,有效:
import turtle
turtle.forward(100)
turtle.mainloop()
这将实例化一个 Screen,实例化一个 Turtle,将 Screen 分配给 Turtle,调用该 Turtle 实例的方法来绘制一条线,并调用该 Screen 的方法以保持 window 打开, 都带有自动生成的全局函数。这是我见过的 exec() 的最佳用例。
有没有办法在没有 exec() 的情况下更像 python 一样做到这一点?
这里是如何在没有 exec()
的情况下复制它......有点。
exec()
仍然需要以显式方式将旧的调用签名分配给新的全局函数,没有方便的方法为函数提供新的调用签名。
然而,作为我的问题的答案,闭包用于将方法变成全局函数,调用签名必须是 *args, **kwargs
。
下面的代码似乎可以代替我发布的代码并回答我的问题标题,如何在没有 exec 的情况下复制制作方法全局函数,尽管我认为由于缺乏准确的签名,它不是更 pythonic :
def __turtle_func_closure(obj, init, name):
"""Wraps class methods as global functions."""
def func(*args, **kwargs):
"""Global function equivalent ofor class methods."""
obj_ = getattr(Turtle, obj, None)
if obj_ is None:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
obj_ = init()
try:
return getattr(obj_, name)(*args, **kwargs)
except TK.TclError:
if not TurtleScreen._RUNNING:
TurtleScreen._RUNNING = True
raise Terminator
raise
return func
def _make_global_funcs(functions, cls, obj, init, docrevise):
for methodname in functions:
method = getattr(cls, methodname)
globals()[methodname] = __turtle_func_closure(obj, init, methodname)
globals()[methodname].__doc__ = docrevise(method.__doc__)
_make_global_funcs(_tg_screen_functions, _Screen,
'Turtle._screen', Screen, _screen_docrevise)
_make_global_funcs(_tg_turtle_functions, Turtle,
'Turtle._pen', Turtle, _turtle_docrevise)