概述
对于特定的cheese示例,我同意使用默认值来表示随机初始化或使用静态工厂方法的许多其他答案。但是,在您想到的相关场景中,使用其他简洁的方法调用构造函数而不影响形参名称或类型信息的质量是有价值的。
自Python 3.8和functools开始。在许多情况下,Singledispatchmethod可以帮助实现这一点(更灵活的multimethod可以应用于更多的场景)。(这篇相关文章描述了如何在没有库的情况下在Python 3.4中实现同样的功能。)我还没有在文档中看到这两种方法的例子,具体显示重载__init__,因为你问,但似乎重载任何成员方法的相同原则适用(如下所示)。
"Single dispatch" (available in the standard library) requires that there be at least one positional parameter and that the type of the first argument be sufficient to distinguish among the possible overloaded options. For the specific Cheese example, this doesn't hold since you wanted random holes when no parameters were given, but multidispatch does support the very same syntax and can be used as long as each method version can be distinguish based on the number and type of all arguments together.
例子
下面是一个如何使用这两种方法的例子(一些细节是为了取悦我的ypy,这是我第一次把这些放在一起的目标):
from functools import singledispatchmethod as overload
# or the following more flexible method after `pip install multimethod`
# from multimethod import multidispatch as overload
class MyClass:
@overload # type: ignore[misc]
def __init__(self, a: int = 0, b: str = 'default'):
self.a = a
self.b = b
@__init__.register
def _from_str(self, b: str, a: int = 0):
self.__init__(a, b) # type: ignore[misc]
def __repr__(self) -> str:
return f"({self.a}, {self.b})"
print([
MyClass(1, "test"),
MyClass("test", 1),
MyClass("test"),
MyClass(1, b="test"),
MyClass("test", a=1),
MyClass("test"),
MyClass(1),
# MyClass(), # `multidispatch` version handles these 3, too.
# MyClass(a=1, b="test"),
# MyClass(b="test", a=1),
])
输出:
[(1, test), (1, test), (0, test), (1, test), (1, test), (0, test), (1, default)]
注:
I wouldn't usually make the alias called overload, but it helped make the diff between using the two methods just a matter of which import you use.
The # type: ignore[misc] comments are not necessary to run, but I put them in there to please mypy which doesn't like decorating __init__ nor calling __init__ directly.
If you are new to the decorator syntax, realize that putting @overload before the definition of __init__ is just sugar for __init__ = overload(the original definition of __init__). In this case, overload is a class so the resulting __init__ is an object that has a __call__ method so that it looks like a function but that also has a .register method which is being called later to add another overloaded version of __init__. This is a bit messy, but it please mypy becuase there are no method names being defined twice. If you don't care about mypy and are planning to use the external library anyway, multimethod also has simpler alternative ways of specifying overloaded versions.
Defining __repr__ is simply there to make the printed output meaningful (you don't need it in general).
Notice that multidispatch is able to handle three additional input combinations that don't have any positional parameters.