Nếu mục tiêu là có cùng loại hiệu ứng trong mã của bạn mà #ifdef WINDOWS / #endif có .. đây là một cách để làm điều đó (Tôi đang sử dụng mac btw).
Trường hợp đơn giản, không có chuỗi
>>> 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)
Vì vậy, với việc thực hiện này, bạn nhận được cùng một cú pháp bạn có trong câu hỏi của bạn.
>>> @macos
... def zulu():
... print("world")
...
>>> @windows
... def zulu():
... print("hello")
...
>>> zulu()
world
>>>
Những gì đoạn mã trên đang làm, về cơ bản, là gán zulu cho zulu nếu nền tảng khớp. Nếu nền tảng không khớp, nó sẽ trả về zulu nếu nó được xác định trước đó. Nếu nó không được xác định, nó sẽ trả về một hàm giữ chỗ làm tăng ngoại lệ.
Khái niệm trang trí là khái niệm dễ dàng để tìm ra nếu bạn nhớ rằng
@mydecorator
def foo():
pass
tương tự như:
foo = mydecorator(foo)
Đây là một triển khai sử dụng một trang trí tham số:
>>> 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")
Trang trí tham số là tương tự như foo = mydecorator(param)(foo)
.
Tôi đã cập nhật câu trả lời khá nhiều. Đáp lại các bình luận, tôi đã mở rộng phạm vi ban đầu của nó để bao gồm ứng dụng cho các phương thức lớp và để bao hàm các hàm được định nghĩa trong các mô-đun khác. Trong bản cập nhật cuối cùng này, tôi đã có thể giảm đáng kể độ phức tạp liên quan đến việc xác định xem một chức năng đã được xác định chưa.
[Một bản cập nhật nhỏ ở đây ... Tôi chỉ không thể đặt nó xuống - đó là một bài tập thú vị] Tôi đã thực hiện thêm một số thử nghiệm về điều này, và thấy nó hoạt động chung trên các thiết bị gọi - không chỉ là các chức năng thông thường; bạn cũng có thể trang trí khai báo lớp cho dù có thể gọi được hay không. Và nó hỗ trợ các chức năng bên trong của các chức năng, vì vậy những thứ như thế này là có thể (mặc dù có lẽ không phải là phong cách tốt - đây chỉ là mã thử nghiệm):
>>> @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.")
Ở trên trình bày cơ chế cơ bản của trình trang trí, cách truy cập phạm vi của người gọi và cách đơn giản hóa nhiều trình trang trí có hành vi tương tự bằng cách có một hàm bên trong chứa thuật toán chung được xác định.
Hỗ trợ xích
Để hỗ trợ xâu chuỗi các trình trang trí này cho biết liệu một chức năng có áp dụng cho nhiều hơn một nền tảng hay không, trình trang trí có thể được thực hiện như vậy:
>>> 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)
Bằng cách đó, bạn hỗ trợ xích:
>>> @macos
... @linux
... def foo():
... print("works!")
...
>>> foo()
works!
my_callback = windows(<actual function definition>)
- vì vậy tênmy_callback
sẽ bị ghi đè, bất kể nhà trang trí có thể làm gì. Cách duy nhất phiên bản Linux của chức năng có thể kết thúc trong biến đó là nếuwindows()
trả về nó - nhưng chức năng không có cách nào để biết về phiên bản Linux. Tôi nghĩ rằng cách điển hình hơn để thực hiện điều này là có các định nghĩa chức năng dành riêng cho hệ điều hành trong các tệp riêng biệt vàimport
chỉ có một trong số chúng.