(저는 Python의 유형 주석 및 mypy에 익숙하지 않으므로 XY 문제가 발생하지 않도록 내 문제를 자세히 설명하고 있습니다)
임의이지만 고정 된 유형의 값을 교환하는 두 개의 추상 클래스가 있습니다.
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Generic, TypeVar
T = TypeVar('T') # result type
class Command(ABC, Generic[T]):
@abstractmethod
def execute(self, runner: Runner[T]) -> T:
raise NotImplementedError()
class Runner(ABC, Generic[T]):
def run(self, command: Command[T]) -> T:
return command.execute(self)
이 인터페이스의 구현에서 Command
하위 클래스는 내 Runner
하위 클래스 의 속성에 액세스해야합니다 (명령이 다른 기능을 가진 실행기에 적응할 수 있다고 상상해보십시오).
class MyCommand(Command[bool]):
def execute(self, runner: Runner[bool]) -> bool:
# Pseudo code to illustrate dependency on runner's attributes
return runner.magic_level > 10
class MyRunner(Runner[bool]):
magic_level: int = 20
이것은 예상대로 작동하지만 mypy를 만족시키지 않습니다.
mypy_sandbox.py:24: error: "Runner[bool]" has no attribute "magic_level" [attr-defined]
분명히 mypy는 정확합니다. magic_level
속성은에 정의되어 MyRunner
있지만에 정의되어 있지 않습니다 Runner
(에 대한 인수 유형 execute
). 따라서 인터페이스가 너무 일반적입니다. 명령은 러너와 함께 작동 할 필요가없고 일부 러너에서만 작동합니다. 따라서 Command
지원되는 러너 클래스를 캡처하기 위해 두 번째 유형 var에서 제네릭을 만들어 보겠습니다 .
R = TypeVar('R') # runner type
T = TypeVar('T') # result type
class Command(ABC, Generic[T, R]):
@abstractmethod
def execute(self, runner: R) -> T:
raise NotImplementedError()
class Runner(ABC, Generic[T]):
def run(self, command: Command[T, Runner[T]]) -> T:
return command.execute(self)
class MyCommand(Command[bool, MyRunner]):
def execute(self, runner: MyRunner) -> bool:
# Pseudo code to illustrate dependency on runner's attributes
return runner.magic_level > 10
# MyRunner defined as before
이것은 mypy를 만족하지만 코드를 사용하려고하면 mypy가 다시 불평합니다.
if __name__ == '__main__':
command = MyCommand()
runner = MyRunner()
print(runner.run(command))
mypy_sandbox.py:35: error: Argument 1 to "run" of "Runner" has incompatible type "MyCommand"; expected "Command[bool, Runner[bool]]" [arg-type]
이번에는 오류를 이해하지 못합니다. MyCommand
is a subclass of Command[bool, MyRunner]
, MyRunner
is a subclass of Runner[bool]
, so is MyCommand
in compatible with Command[bool, Runner[bool]]
?
그리고 mypy가 만족 스러웠다면 mypy 불평없이 (에 묶여 있지 않기 때문에 ) "다른 값"을 사용 Command
하는 Runner
서브 클래스를 가진 서브 클래스를 구현할 수있을 것입니다 . 시도 했지만 또 다른 오류가 발생합니다.T
R
T
R = TypeVar('R', bound='Runner[T]')
error: Type variable "mypy_sandbox.T" is unbound [valid-type]
위에서 설명한 확장이 가능하지만 올바르게 유형 검사되도록하려면 어떻게 유형 주석을 달 수 있습니까?
현재 주석은 실제로 모순입니다.
Runner
단지 수 Command
의 형식이야 Command[T, Runner[T]]
.execute
메서드 Command[bool, Runner[bool]]
는 모든 Runner[bool]
.execute
메서드는 MyCommand
" Runner[bool]
가있는magic_level
" 만 허용합니다 .따라서 MyCommand
는 a Command[bool, Runner[bool]]
가 아닙니다 . " Runner[bool]
없이magic_level
" 를 허용하지 않습니다 . 이로 인해 MyPy는 그 이유가 더 일찍 발생하더라도 대체를 거부합니다.
이 문제는 R
의 자체 유형으로 매개 변수화하여 해결할 수 있습니다 Runner
. 이렇게하면 baseclass에 의한 Runner
매개 변수화 Command
를 방지 하고 대신 실제 하위 유형 에 의해 매개 변수화합니다 . Runner[T]
Runner[T]
R = TypeVar('R', bound='Runner[Any]')
T = TypeVar('T') # result type
class Command(ABC, Generic[T, R]):
@abstractmethod
def execute(self, runner: R) -> T:
raise NotImplementedError()
# Runner is not generic in R
class Runner(ABC, Generic[T]):
# Runner.run is generic in its owner
def run(self: R, command: Command[T, R]) -> T:
return command.execute(self)
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다