如果目标是使代码中的效果与#ifdef WINDOWS / #endif具有的效果相同。.这是一种实现方法(我在Mac btw上)。
简单案例,无需链接
>>> def _ifdef_decorator_impl(plat, func, frame):
... if platform.system() == plat:
... return func
... elif func.__name__ in frame.f_locals:
... return frame.f_locals[func.__name__]
... else:
... def _not_implemented(*args, **kwargs):
... raise NotImplementedError(
... f"Function {func.__name__} is not defined "
... f"for platform {platform.system()}.")
... return _not_implemented
...
...
>>> def windows(func):
... return _ifdef_decorator_impl('Windows', func, sys._getframe().f_back)
...
>>> def macos(func):
... return _ifdef_decorator_impl('Darwin', func, sys._getframe().f_back)
因此,通过此实现,您将获得与问题相同的语法。
>>> @macos
... def zulu():
... print("world")
...
>>> @windows
... def zulu():
... print("hello")
...
>>> zulu()
world
>>>
如果平台匹配,则上面的代码实际上是在将zulu分配给zulu。如果平台不匹配,则如果先前已定义,它将返回zulu。如果未定义,它将返回一个引发异常的占位符函数。
如果您牢记,装饰器在概念上很容易弄清楚
@mydecorator
def foo():
pass
类似于:
foo = mydecorator(foo)
这是使用参数化装饰器的实现:
>>> def ifdef(plat):
... frame = sys._getframe().f_back
... def _ifdef(func):
... return _ifdef_decorator_impl(plat, func, frame)
... return _ifdef
...
>>> @ifdef('Darwin')
... def ice9():
... print("nonsense")
参数化修饰符类似于foo = mydecorator(param)(foo)
。
我已经相当多地更新了答案。为了回应评论,我扩展了其原始范围,将应用程序包括到类方法中,并涵盖了其他模块中定义的功能。在最后的更新中,我已经能够大大降低确定功能是否已经定义的复杂性。
[这里有一点更新...我简直无法接受-这是一个有趣的练习]我一直在对此进行更多测试,发现它通常可用于可调用对象-不仅仅是普通函数;您还可以装饰类声明(无论是否可调用)。并且它支持函数的内部功能,因此这样的事情是可能的(尽管可能不是很好的样式-这只是测试代码):
>>> @macos
... class CallableClass:
...
... @macos
... def __call__(self):
... print("CallableClass.__call__() invoked.")
...
... @macos
... def func_with_inner(self):
... print("Defining inner function.")
...
... @macos
... def inner():
... print("Inner function defined for Darwin called.")
...
... @windows
... def inner():
... print("Inner function for Windows called.")
...
... inner()
...
... @macos
... class InnerClass:
...
... @macos
... def inner_class_function(self):
... print("Called inner_class_function() Mac.")
...
... @windows
... def inner_class_function(self):
... print("Called inner_class_function() for windows.")
上面的内容演示了装饰器的基本机制,如何访问调用者的作用域以及如何通过定义包含通用算法的内部函数来简化具有类似行为的多个装饰器。
链接支持
为了支持链接这些装饰器,以指示某个功能是否适用于多个平台,可以这样实现装饰器:
>>> class IfDefDecoratorPlaceholder:
... def __init__(self, func):
... self.__name__ = func.__name__
... self._func = func
...
... def __call__(self, *args, **kwargs):
... raise NotImplementedError(
... f"Function {self._func.__name__} is not defined for "
... f"platform {platform.system()}.")
...
>>> def _ifdef_decorator_impl(plat, func, frame):
... if platform.system() == plat:
... if type(func) == IfDefDecoratorPlaceholder:
... func = func._func
... frame.f_locals[func.__name__] = func
... return func
... elif func.__name__ in frame.f_locals:
... return frame.f_locals[func.__name__]
... elif type(func) == IfDefDecoratorPlaceholder:
... return func
... else:
... return IfDefDecoratorPlaceholder(func)
...
>>> def linux(func):
... return _ifdef_decorator_impl('Linux', func, sys._getframe().f_back)
这样,您就可以支持链接:
>>> @macos
... @linux
... def foo():
... print("works!")
...
>>> foo()
works!
my_callback = windows(<actual function definition>)
-因此,无论修饰符可能做什么,该名称my_callback
都将被覆盖。windows()
返回该变量的Linux版本的唯一方法是返回该变量-但是该函数无法得知Linux版本。我认为实现此目的的更典型方法是将操作系统特定的功能定义放在单独的文件中,并且有条件地import
仅其中之一。