Amaç #ifdef WINDOWS / #endif sahip olduğu kodunuzda aynı etkiyi yaratmaksa .. işte bunu yapmanın bir yolu var (mac btw kullanıyorum).
Basit Kasa, Zincirleme Yok
>>> 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)
Yani bu uygulama ile aynı sözdizimine sahip olacaksınız.
>>> @macos
... def zulu():
... print("world")
...
>>> @windows
... def zulu():
... print("hello")
...
>>> zulu()
world
>>>
Yukarıdaki kodun yaptığı şey, platform eşleşirse, zulu'ya zulu'yu atamaktır. Platform eşleşmezse, daha önce tanımlanmışsa zulu değerini döndürür. Tanımlanmamışsa, istisna yaratan bir yer tutucu işlevi döndürür.
Dekoratörler,
@mydecorator
def foo():
pass
şuna benzer:
foo = mydecorator(foo)
Parametreli bir dekoratör kullanan bir uygulama:
>>> 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")
Parametreli dekoratörler buna benzerdir foo = mydecorator(param)(foo)
.
Cevabı biraz güncelledim. Yorumlara yanıt olarak, orijinal kapsamını sınıf yöntemlerine uygulamayı içerecek ve diğer modüllerde tanımlanan işlevleri kapsayacak şekilde genişlettim. Bu son güncellemede, bir işlevin önceden tanımlanıp tanımlanmadığını belirleme konusundaki karmaşıklığı büyük ölçüde azaltabildim.
[Buradaki küçük bir güncelleme ... Bunu başaramadım - eğlenceli bir alıştırma oldu] Bunu biraz daha test ediyorum ve genel olarak callables üzerinde çalıştığını gördüm - sadece sıradan işlevler değil; çağrılabilir olsun veya olmasın sınıf bildirimlerini de dekore edebilirsiniz. Ve fonksiyonların iç işlevlerini destekler, bu nedenle böyle şeyler mümkündür (muhtemelen iyi bir stil olmasa da - bu sadece test kodudur):
>>> @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.")
Yukarıda, dekoratörlerin temel mekanizması, arayanın kapsamına nasıl erişileceği ve ortak algoritmayı içeren bir dahili fonksiyona sahip olarak benzer davranışa sahip birden fazla dekoratörün nasıl basitleştirileceği gösterilmektedir.
Zincirleme Desteği
Bir fonksiyonun birden fazla platforma uygulanıp uygulanmadığını gösteren bu dekoratörlerin zincirlenmesini desteklemek için dekoratör şu şekilde uygulanabilir:
>>> 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)
Bu şekilde zincirlemeyi desteklersiniz:
>>> @macos
... @linux
... def foo():
... print("works!")
...
>>> foo()
works!
my_callback = windows(<actual function definition>)
- adı bu yüzdenmy_callback
olacak bakılmaksızın dekoratör yapabileceklerine istinaden, üzerine yazılır olun. İşlevin Linux sürümünün bu değişkenle sonuçlanabilmesinin tek yolu,windows()
döndürüldüğünde ortaya çıkabilir - ancak işlevin Linux sürümü hakkında hiçbir bilgisi yoktur. Bunu başarmanın daha tipik bir yolunun, ayrı dosyalarda ve koşulluimport
olarak bunlardan sadece birinde OS'ye özgü işlev tanımlarına sahip olmak olduğunu düşünüyorum .