你能发现下面代码中的错误吗?米皮不能。
from typing import Dict, Any
def add_items(d: Dict[str, Any]) -> None:
d['foo'] = 5
d: Dict[str, str] = {}
add_items(d)
for key, value in d.items():
print(f"{repr(key)}: {repr(value.lower())}")
Run Code Online (Sandbox Code Playgroud)
当然,Python 发现了错误,这有助于通知我们'int' object has no attribute 'lower'. 太糟糕了,直到运行时它才能告诉我们这一点。
据我所知, mypy 没有捕获此错误,因为它允许 参数的参数d是add_items协变的。如果我们只阅读字典,那就有意义了。如果我们只是读取,那么我们会希望参数是协变的。如果我们准备读取任何类型,那么我们应该能够读取字符串类型。当然,如果我们只是阅读,那么我们应该将其输入为typing.Mapping.
由于我们正在编写,所以我们实际上希望参数是逆变的。例如,某人传入 a 是完全有意义的Dict[Any, Any],因为它完全能够存储字符串键和整数值。
如果我们要进行读写,就别无选择,只能让参数保持不变。
有没有办法指定我们需要什么样的方差?更好的是, mypy 是否足够复杂,以至于期望它通过静态分析确定方差应该是合理的,并且这应该作为错误归档?或者 Python 中类型检查的当前状态根本无法捕获这种编程错误?
特尔;博士:将实现细节保留在模块命名空间之外的最简洁方法是什么?
已经有许多关于这个主题的类似问题,但相对于现代工具和语言功能,似乎没有一个令人满意的答案。
我正在设计一个 Python 包,我想保持每个模块的公共接口干净,只公开预期的内容,隐藏实现细节(尤其是导入)。
多年来,我看到了许多技术:
在我看来,这太可怕了。一个设计良好的界面应该很容易被发现。公开可见的实现细节会使界面更加混乱。即使作为包的作者,我也不想在它暴露太多时使用它,因为它会使自动完成功能变得不那么有用。
这是一个很好理解的约定,大多数开发工具都足够聪明,至少可以将下划线前缀的名称排序到自动完成列表的底部。如果你有少量的名字来这样处理它工作得很好,但随着名字的数量增加,它变得越来越乏味和丑陋。
以这个相对简单的导入列表为例:
import struct
from abc import abstractmethod, ABC
from enum import Enum
from typing import BinaryIO, Dict, Iterator, List, Optional, Type, Union
Run Code Online (Sandbox Code Playgroud)
应用下划线技术,这个相对较小的导入列表变成了这个怪物:
import struct as _struct
from abc import abstractmethod as _abstractmethod, ABC as _ABC
from enum import Enum as _Enum
from typing import (
BinaryIO as _BinaryIO,
Dict as _Dict,
Iterator as _Iterator,
List as _List,
Optional as _Optional,
Type as _Type,
Union as _Union
)
Run Code Online (Sandbox Code Playgroud)
现在,我知道这个问题可以通过从不进行 …
我无法让编译器解决扩展方法的正确重载问题.我解释的最好方法是使用一些代码.这是一个演示问题的LINQPad脚本.由于我遇到的问题,这将无法编译:
void Main(){
new Container<A>().Foo(a=>false);
}
interface IMarker{}
class A : IMarker{
public int AProp{get;set;}
}
class B : IMarker{
public int BProp{get;set;}
}
class Container<T>{}
static class Extensions{
public static void Foo<T>(this T t, Func<T, bool> func)
where T : IMarker{
string.Format("Foo({0}:IMarker)", typeof(T).Name).Dump();
}
public static void Foo<T>(this Container<T> t, Func<T, bool> func){
string.Format("Foo(Container<{0}>)", typeof(T).Name).Dump();
}
}
Run Code Online (Sandbox Code Playgroud)
我得到的错误是:
以下方法或属性之间的调用不明确:'
Extensions.Foo<Container<A>>(Container<A>, System.Func<Container<A>,bool>)'和'Extensions.Foo<A>(Container<A>, System.Func<A,bool>)'
在我看来,它根本不含糊.第一种方法不接受a Container<T>,只接受a IMarker.似乎通用约束不能帮助解决重载,但在这个版本的代码中,它们似乎确实如下:
void Main(){
new A().Bar();
new A().Foo(a=>a.AProp == …Run Code Online (Sandbox Code Playgroud) 如何选择除我CONVERT在其中一列上调用错误的行之外的所有行?
例如,我这样做:
SELECT rowid
FROM batchinfo
WHERE CONVERT(DATE, reporttime, 101) between '2010-07-01' and '2010-07-31';
Run Code Online (Sandbox Code Playgroud)
我收到一些价值的错误.我有两个问题:
我不确定我写的代码是否有意义.这是一般的想法:
我有一个Parser类,它将采用不同类型的文档.根据类型,我将以不同方式解析文档.
所以假设我有两种类型的文档,A和B.如果我需要扩展程序来处理其他类型,工厂模式似乎是一个非常好的方法,所以我将有一个抽象类.
abstract class Parser
{
...
public void common_method() {
// something common that all parsers will use
// like file IO
}
// derived classes will override this
public abstract void specific_method();
}
class A_Parser : Parser
{
...
}
class B_Parser : Parser
{
...
}
Run Code Online (Sandbox Code Playgroud)
我想知道的问题是我在抽象的Parser中声明了抽象方法和非抽象方法.编译器似乎没有抱怨,它似乎仍然正常工作.
这不标准吗?也许有更好的方法来设计它?