Jika tujuannya adalah untuk memiliki efek yang sama dalam kode Anda yang memiliki #ifdef WINDOWS / #endif .. inilah cara untuk melakukannya (Saya menggunakan mac btw).
Kasus Sederhana, Tanpa Rantai
>>> 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)
Jadi dengan implementasi ini Anda mendapatkan sintaks yang sama dengan yang Anda miliki dalam pertanyaan Anda.
>>> @macos
... def zulu():
... print("world")
...
>>> @windows
... def zulu():
... print("hello")
...
>>> zulu()
world
>>>
Apa yang dilakukan kode di atas, pada dasarnya, adalah menugaskan zulu ke zulu jika platform cocok. Jika platform tidak cocok, itu akan mengembalikan zulu jika sudah ditentukan sebelumnya. Jika tidak ditentukan, ia mengembalikan fungsi placeholder yang menimbulkan pengecualian.
Penghias secara konsep mudah untuk mencari tahu jika Anda ingat itu
@mydecorator
def foo():
pass
analog dengan:
foo = mydecorator(foo)
Berikut ini implementasi menggunakan dekorator berparameter:
>>> 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")
Dekorator berparameter analog dengan foo = mydecorator(param)(foo)
.
Saya sudah memperbarui sedikit jawabannya. Menanggapi komentar, saya telah memperluas cakupan aslinya untuk memasukkan aplikasi ke metode kelas dan untuk mencakup fungsi yang didefinisikan dalam modul lain. Dalam pembaruan terakhir ini, saya dapat mengurangi kompleksitas yang terlibat dalam menentukan apakah suatu fungsi telah ditentukan.
[Sedikit pembaruan di sini ... Saya tidak bisa menghentikannya - ini merupakan latihan yang menyenangkan] Saya telah melakukan beberapa pengujian lagi, dan ternyata ini berfungsi secara umum pada kartu panggil - bukan hanya fungsi biasa; Anda juga bisa mendekorasi deklarasi kelas apakah dapat dipanggil atau tidak. Dan itu mendukung fungsi fungsi dalam, jadi hal-hal seperti ini dimungkinkan (walaupun mungkin bukan gaya yang baik - ini hanya kode uji):
>>> @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.")
Di atas menunjukkan mekanisme dasar dekorator, cara mengakses ruang lingkup pemanggil, dan bagaimana menyederhanakan banyak dekorator yang memiliki perilaku serupa dengan memiliki fungsi internal yang berisi algoritma umum yang ditentukan.
Dukungan Chaining
Untuk mendukung rantai dekorator ini menunjukkan apakah suatu fungsi berlaku untuk lebih dari satu platform, dekorator dapat diimplementasikan seperti:
>>> 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)
Dengan begitu Anda mendukung rantai:
>>> @macos
... @linux
... def foo():
... print("works!")
...
>>> foo()
works!
my_callback = windows(<actual function definition>)
- sehingga namanyamy_callback
akan ditimpa, terlepas dari apa yang dekorator lakukan. Satu-satunya cara fungsi versi Linux bisa berakhir di variabel itu adalah jikawindows()
mengembalikannya - tetapi fungsi tersebut tidak memiliki cara untuk mengetahui tentang versi Linux. Saya pikir cara yang lebih khas untuk mencapai ini adalah memiliki definisi fungsi OS-spesifik dalam file terpisah, dan secara kondisionalimport
hanya satu dari mereka.