如何使用Decorator绕过python函数定义? - python

我想知道是否有可能基于全局设置(例如OS)控制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的第一个定义,而第二个定义将被忽略。

它与确定操作系统无关,而与功能定义/修饰符有关。

参考方案

如果目标是使代码中的效果与#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!

下面的注释并不真正适用于此解决方案的当前状态。它们是在第一次迭代中找到解决方案时制作的,因此不再适用。例如,语句“请注意,仅当macOS和Windows与zulu在同一模块中定义时,此方法才有效”。 (已投票4次)最早的版本,但在当前版本中已解决;下面的大多数语句都是这种情况。很好奇的是,验证了当前解决方案的注释已被删除。

Python:检查是否存在维基百科文章 - python

我试图弄清楚如何检查Wikipedia文章是否存在。例如,https://en.wikipedia.org/wiki/Food 存在,但是https://en.wikipedia.org/wiki/Fod 不会,页面只是说:“维基百科没有此名称的文章。”谢谢! 参考方案 >>> import urllib >>> prin…

Python Pandas导出数据 - python

我正在使用python pandas处理一些数据。我已使用以下代码将数据导出到excel文件。writer = pd.ExcelWriter('Data.xlsx'); wrong_data.to_excel(writer,"Names which are wrong", index = False); writer.…

Python:无符号32位按位算术 - python

试图回答另一篇有关其解决方案涉及IP地址和网络掩码的文章时,我陷入了普通的按位算法。在Python中,是否存在一种标准的方式来进行按位AND,OR,XOR,NOT运算,假设输入是“32位”(可能是负数)整数或long,并且结果必须是[[ 0,2 ** 32]?换句话说,我需要一个与无符号长整数之间的C按位运算有效的Python对应物。编辑:具体问题是这样的:…

Python:如何从字节中提取特定位? - python

我有一条消息,显示为14 09 00 79 3d 00 23 27。我可以通过调用message[4]从此消息中提取每个字节,例如,这将给我3d。如何从该字节中提取单个8位?例如,如何将24-27位作为单个消息?只需28位? 参考方案 要回答问题的第二部分,您可以使用按位运算来获取特定的位值# getting your message as int i = …

Python uuid4,如何限制唯一字符的长度 - python

在Python中,我正在使用uuid4()方法创建唯一的字符集。但是我找不到将其限制为10或8个字符的方法。有什么办法吗?uuid4()ffc69c1b-9d87-4c19-8dac-c09ca857e3fc谢谢。 参考方案 尝试:x = uuid4() str(x)[:8] 输出:"ffc69c1b" Is there a way to…