我想知道是否有可能基于全局设置(例如操作系统)控制Python函数定义.例子:

@linux
def my_callback(*args, **kwargs):
    print("Doing something @ Linux")
    return

@windows
def my_callback(*args, **kwargs):
    print("Doing something @ Windows")
    return

然后,如果有人在使用Linux,将使用my_callback的第一个定义,第二个定义将被默默忽略.

Its not about determining the OS, its about function definition / decorators.

推荐答案

如果目标是在代码中产生与#ifdef WINDOWS/#endif相同的效果..这里有一个方法(顺便说一句,我在mac电脑上).

Simple Case, No Chaining

>>> 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)

下面是一个使用参数化decorator 的实现:

>>> 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")

参数化decorator 类似于foo = mydecorator(param)(foo).

我已经更新了很多答案.作为对 comments 的回应,我扩展了它最初的范围,包括类方法的应用程序,并涵盖了在其他模块中定义的函数.在上一次更新中,我已经能够大大降低确定函数是否已经定义的复杂性.

[这里有一点更新……我就是记不下来——这是一个有趣的练习]我已经对它做了更多的测试,发现它通常适用于可调用函数,而不仅仅是普通函数;您还可以修饰类声明,无论是否可调用.它支持函数的内部函数,所以类似这样的事情是可能的(虽然可能不是好的风格——这只是测试代码):

>>> @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.")

上面演示了decorators的基本机制,如何访问调用者的作用域,以及如何通过定义一个包含公共算法的内部函数来简化具有类似行为的多个decorator.

Chaining Support

为了支持链接这些decorator ,指示一个函数是否应用于多个平台,decorator 可以如下实现:

>>> 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!

以下 comments 并不适用于当前状态下的此解决方案.它们是在寻找解决方案的第一次迭代中产生的,不再适用.例如,声明"注意,只有当macos和windows与zulu在同一个模块中定义时,这才有效."(向上投票4次)适用于最早的版本,但已在当前版本中解决;下面的大多数陈述都是如此.It's curious that the comments that validated the current solution have been removed.

Python-3.x相关问答推荐

使用PANAS根据另两个列表中的值对一个列表中的字符串值进行分组

Pyvis和Networkx:如何根据源或目标使 node colored颜色 不同

PySpark每毫秒使用先前的值填充数据

msg-seviri l1.5本机文件

估计列表中连续对的数量

从另一个云函数调用带有仅允许内部流量标志的云函数时出现问题

重复数组直到一定长度 groupby pandas

找到在指定列的另一个分组中存在重复的行.

「Python Pandas」多级索引列和行匹配,如果列和行名称相似,则排除这些单元格中的值添加

Pandas matplotlib:条形图占总数的百分比

包含值超出范围的 ID 的新 DataFrame 列?

BeautifulSoup 和 pd.read_html - 如何将链接保存到最终数据框中的单独列中?

获取比较多列的最大值并返回特定值

Tkinter 窗口显示(无响应)但代码正在运行

Tkinter AttributeError:对象没有属性'tk'

为什么 string.maketrans 在 Python 3.1 中不起作用?

如何使用已打开并使用登录凭据登录的浏览器

如何强制 Sphinx 使用 Python 3.x 解释器

如何为 anaconda python3 安装 gi 模块?

异常被忽略是什么类型的消息?