Python3添加了__prepare__
,以便您可以替换用于从类声明中收集项目的字典类型(请参阅here。)使用__prepare__
我可以将类设置为允许相同成员函数的多个定义。
class MultiType(type):
@classmethod
def __prepare__(metacls, name, bases, **kwds):
return collections.defaultdict(list)
class Foo(metaclass=MultiType):
def bar(self, x):
return x - 1
def bar(self, x):
return x
在模块级别,我可以使用装饰器玩一些技巧:
def capture(f):
module = importlib.import_module(f.__module__)
if not hasattr(module, 'my_cache'):
setattr(module, 'my_cache', [])
module.my_cache.append(f)
@functools.wraps(f)
def _wrapper(*args, **kwargs):
return (func(*args, **kwargs) for func in module.my_cache)
return _wrapper
@capture
def foo(x):
return x - 1
@capture
def foo(x):
return 42
但是,如果我在闭包中执行此操作,我可能会在模块级别添加范围不应该的东西。
def foo(x):
@some_awesome_deco
def bar(y):
return y
@some_awesome_deco
def bar(y):
return 24
return bar(x+1)
有没有办法识别和捕获在闭包范围内声明的函数,这样我可以处理这些与模块作用域中声明的函数不同,而不必引用闭合函数(即@some_awesome_deco(foo)
?)
答案 0 :(得分:4)
如果您需要支持的只是CPython,那么您的装饰者可以查看sys._getframe(1)
帧对象,该对象表示执行装饰器的代码的执行帧。如果frame.f_locals
字典与frame.f_globals
字典的对象相同,则表示您处于模块级别。如果没有,则您处于嵌套范围内。
然而,您必须生成某种范围键;你可以在f_locals
中存储一些内容(这实际上不会影响实际的本地人)。请记住,当函数退出时,本地(以及框架)将被清除。我会返回一个特殊的可调用的,一个可变的,所以你可以在后续的装饰调用中引用它。例如,您可以使用frame.f_locals[decorated_function.__name__]
检索该对象。
请参阅inspect
module documenation,了解您可以在帧对象上找到哪些属性的概述。
演示:
>>> import sys
>>> def nested_scope_detector(func):
... frame = sys._getframe(1)
... nested_scope = frame.f_globals is not frame.f_locals
... redefinition = func.__name__ in frame.f_locals
... if nested_scope: print('{!r} is located in a nested scope.'.format(func))
... if redefinition: print('Redefining {!r}, previously bound to {!r}'.format(
... func.__name__, frame.f_locals[func.__name__]))
... return func
...
>>> @nested_scope_detector
... def foo(): pass
...
>>> @nested_scope_detector
... def foo(): pass
...
Redefining 'foo', previously bound to <function foo at 0x10e931d08>
>>> def bar():
... @nested_scope_detector
... def foo(): pass
... @nested_scope_detector
... def foo(): pass
...
>>> bar()
<function bar.<locals>.foo at 0x10eb4ef28> is located in a nested scope.
<function bar.<locals>.foo at 0x10eb4eea0> is located in a nested scope.
Redefining 'foo', previously bound to <function bar.<locals>.foo at 0x10eb4ef28>
因此,您可以在返回的包装函数上使用函数属性来存储函数:
def capture(f):
locals = sys._getframe(1).f_locals
preexisting = locals.get(f.__name__)
if preexisting is not None and hasattr(preexisting, 'functions'):
preexisting.functions.append(f)
return preexisting
@functools.wraps(f)
def _wrapper(*args, **kwargs):
return (func(*args, **kwargs) for func in _wrapper.functions)
_wrapper.functions = [f]
return _wrapper
它可以在任何范围内工作:
>>> @capture
... def foo(): return 'bar'
...
>>> @capture
... def foo(): return 'baz'
...
>>> foo()
<generator object <genexpr> at 0x10eb45ee8>
>>> list(foo())
['bar', 'baz']
>>> def bar():
... @capture
... def foo(): return 'bar'
... @capture
... def foo(): return 'baz'
... return foo
...
>>> list(bar()())
['bar', 'baz']