목표가 코드에서 #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!
my_callback = windows(<actual function definition>)
이름my_callback
을 덮어 씁니다. 함수의 Linux 버전이 해당 변수로 끝나는 유일한 방법은 변수를windows()
반환하는 것입니다. 그러나 함수는 Linux 버전에 대해 알 방법이 없습니다. 이 작업을 수행하는 가장 일반적인 방법은 OS 특정 기능 정의를 별도의 파일에 저장하고 조건부import
중 하나만 갖는 것입니다.