英文:
Mypy throwing union-attr errors after instance checks before early return
问题
以下是翻译好的内容:
我正试图在Python中通过将“Request”对象传递到处理程序“链”中来实现责任链模式。传递给每个处理程序方法的“Request”对象具有一些可选字段和一些联合类型。
我对这些处理程序的一般模式是在方法顶部创建一些布尔值,以检查当前处理程序方法是否应处理请求,如果不应处理,则通过调用下一个处理程序来提前返回。
以下是我的设置示例:
from pathlib import Path
from typing import Any, Callable
class Request:
data: int | Any | None = None
location: str | Path | None = None
def handle_int(request: Request, next_handler: Callable[[Request], int]) -> int:
is_int = isinstance(request.data, int)
is_str = isinstance(request.location, str)
if not (is_int and is_str):
return next_handler(request)
a = request.location.split("_")
return request.data * 3
我遇到的问题是,每当我访问请求对象的属性时,mypy会捕获“union-attr”错误:
test_mypy.py:17:9: error: Item "Path" of "str | Path | None" has no attribute "split" [union-attr]
test_mypy.py:17:9: error: Item "None" of "str | Path | None" has no attribute "split" [union-attr]
test_mypy.py:19:12: error: Unsupported operand types for * ("None" and "int") [operator]
test_mypy.py:19:12: note: Left operand is of type "int | Any | None"
我可以通过在返回语句之后添加断言来消除这些错误,但这实际上为提前返回之前的谓词检查创建了基本重复的代码,感觉不是一个好的模式。
除了创建更具体的请求类型之外,还有一种方法可以让mypy知道我们在提前返回之后意味着已经对请求中的属性形式进行了检查吗?
英文:
I am trying to implement the chain of responsibility pattern in python by passing a Request
object into a handler "chain". The Request
object passed in to each handler method in sequence has a few optional fields and some union types.
The general pattern I have for these handlers is to create a few boolean values at the top of the method to check if the current handler method should handle the request and if not, return early by calling the next handler in line.
The following is an example of my setup:
from pathlib import Path
from typing import Any, Callable
class Request:
data: int | Any | None = None
location: str | Path | None = None
def handle_int(request: Request, next_handler: Callable[[Request], int]) -> int:
is_int = isinstance(request.data, int)
is_str = isinstance(request.location, int)
if not (is_int and is_str):
return next_handler(request)
a = request.location.split("_")
return request.data * 3
The issue I am having is that mypy catches union-attr
errors whenever I access the attributes on the request object:
test_mypy.py:17:9: error: Item "Path" of "str | Path | None" has no attribute "split" [union-attr]
test_mypy.py:17:9: error: Item "None" of "str | Path | None" has no attribute "split" [union-attr]
test_mypy.py:19:12: error: Unsupported operand types for * ("None" and "int") [operator]
test_mypy.py:19:12: note: Left operand is of type "int | Any | None"
I can get rid of these by adding assertions after the return statement but that is creating basically duplicate code for the predicate checks before the early return and feels like a bad pattern.
Apart from creating more specific request types is there a way to let mypy know that the fact we are after the early return means that checks have been done on the form of the attributes n the request already?
答案1
得分: 1
mypy
混淆了,因为isinstance
的类型缩小不能通过变量传播。必须直接在if
中完成。
使用
if not (isinstance(request.data, int) and isinstance(request.location, str)):
return next_handler(request)
而不是
is_int = isinstance(request.data, int)
is_str = isinstance(request.location, str)
if not (is_int and is_str):
return next_handler(request)
英文:
mypy
is just getting confused because the type narrowing of isinstance
cannot be propagated through variables. It has to be done directly in the if
.
Use
if not (isinstance(request.data, int) and isinstance(request.location, str)):
return next_handler(request)
Instead of
is_int = isinstance(request.data, int)
is_str = isinstance(request.location, str)
if not (is_int and is_str):
return next_handler(request)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论