如何提供两种不同的实例化方式

问题描述 投票:0回答:4

假设我有一个类

AmbiguousClass
,它有两个属性,
a
b
(假设它们都是
int
,但它可以更通用)。它们通过一些可逆方程相关,这样我就可以从 b 计算 a 并计算出 a 的倒数。

我想让用户可以通过提供

 AmbiguousClass
a
来实例化
b
,具体取决于对他们来说更容易的方式。请注意,实例化变量的两种方法可以具有相同的签名,因此这不是典型的多态性示例。不提供或两者都提供应该会导致错误/警告。

如何在

__init__
函数中执行此操作?

我最好的猜测是这样的:

class AmbiguousClass():
    def __init__(self, a=None, b=None):
        #if no parameter is provided, we raise an error (can not instantiate)
        if a is None and b is None:
            raise SomeCustomError()

        #if both a and b are provided, this seems redundant, a warning is raised
        elif a is not None and b is not None:
            warnings.warn(f"are you sure that you need to specify both a and b?")
            self.a = a 
            self.b = b

        # if a is provided, we calculate b from it
        elif a is not None:
            self.a = a
            self.b = calculateB(self.a)

        # if b is provided:
        elif b is not None:
            self.b =b
            self.a = calculateA(self.b)

然后,用户必须通过指定他提供的关键字来实例化该类:

var1, var2 = AmbiguousClass(a=3), AmbiguousClass(b=6)

但是,这感觉有点笨拙,特别是如果用户决定提供参数而不提供关键字(默认情况下为

a
,但从用户的角度来看这并不明确,可能会导致意外行为)

python overloading instantiation
4个回答
4
投票

我将强制使用仅采用单个参数的自定义构造函数,并清楚地指示提供了哪个参数,从而避免歧义。

class AmbiguousClass:
    def __init__(self, a, b, _is_from_cls=False):
        if not _is_from_cls:
            raise TypeError(
                "Cannot instantiate AmbiguousClass directly."
                " Use classmethods 'from_a' or 'from_b' instead."
            )
        self.a = a
        self.b = b

    @classmethod
    def from_a(cls, a):
        b = calculateB(a)
        return cls(a=a, b=b, _is_from_cls=True)

    @classmethod
    def from_b(cls, b):
        a = calculateA(b)
        return cls(a=a, b=b, _is_from_cls=True)


var1 = AmbiguousClass.from_a(a=3)
var2 = AmbiguousClass.from_b(b=6)
var3 = AmbiguousClass(3, 6)  # will raise a TypeError
var4 = AmbiguousClass(3, 6, True)  # will not raise a TypeError but the user explicitly overwrites the default parameter.

编辑:

添加了直接调用

AmbiguousClass(2,8)
时的错误。


1
投票

您可以在构造函数中强制使用关键字。

这可能是一个偏好问题,但我喜欢尽可能多地进行“惰性”处理 - 即,在类构建期间进行最少的处理

类似这样的:

import warnings

class SomeCustomException(Exception):
    ...

class AmbiguousClass:
    def __init__(self, *, a=None, b=None):
        if a is None and b is None:
            raise SomeCustomException('At least one parameter must be specified')
        if a is not None and b is not None:
            warnings.warn('Are you sure that you need to specify both a and b?')
        self._a = a
        self._b = b
    def calculateA(self):
        return 999
    def calculateB(self):
        return 666
    @property
    def a(self):
        if self._a is None:
            self._a = self.calculateA()
        return self._a
    @property
    def b(self):
        if self._b is None:
            self._b = self.calculateB()
        return self._b


print(AmbiguousClass(a=3).b)

1
投票

更好的做法是从一开始就不允许出现任何歧义。也就是说,强制使用关键字参数,并禁止调用者同时提供两个参数——这根本没有充分的理由。

可以使用

*
语法强制执行关键字参数,而不允许提供两个参数可以通过使用布尔比较来完成:

class UnambiguousClass:
    def __init__(self, *, a=None, b=None):
        assert (a is None) != (b is None), "Exactly one of `a` and `b` must be provided."
        if a is None:
             self.a = calculateA(b)
             self.b = b
        else:
             self.a = a
             self.b = calculateB(a)

-1
投票

使用两种不同方式进行模糊类实例化的 Pythonic 方法

在设计像

AmbiguousClass
这样的类时,可以使用两个不同的属性(
a
b
)来完成实例化,同时确保不同时提供这两个属性,您确实可以实现更Pythonic和更清晰的实现。为此,我们可以利用类方法作为替代构造函数,允许用户更直观地提供
a
b
,而无需仅依赖关键字参数。

这是使用类方法的更 Pythonic 方法:

class AmbiguousClass():
    def __init__(self, a=None, b=None):
        # If no parameter is provided, raise an error (cannot instantiate)
        if a is None and b is None:
            raise ValueError("Either 'a' or 'b' must be provided.")

        # If both a and b are provided, raise a warning
        elif a is not None and b is not None:
            warnings.warn("Are you sure that you need to specify both 'a' and 'b'?")

        # If a is provided, calculate b from it
        elif a is not None:
            self.a = a
            self.b = self.calculateB(self.a)

        # If b is provided:
        elif b is not None:
            self.b = b
            self.a = self.calculateA(self.b)

    @classmethod
    def from_a(cls, a):
        # Instantiate using 'a'
        return cls(a=a)

    @classmethod
    def from_b(cls, b):
        # Instantiate using 'b'
        return cls(b=b)

    @staticmethod
    def calculateB(a):
        # Your implementation to calculate 'b' from 'a'
        pass

    @staticmethod
    def calculateA(b):
        # Your implementation to calculate 'a' from 'b'
        pass

通过此实现,用户现在可以使用替代构造函数

AmbiguousClass
from_a()
实例化
from_b()
,这清楚地表明了它们提供的属性。当您提供参数而不指定关键字时,此方法有助于避免意外行为。

现在可以如何实例化该类:

# Using the from_a() constructor
var1 = AmbiguousClass.from_a(3)

# Using the from_b() constructor
var2 = AmbiguousClass.from_b(6)

这种方法提高了可读性并减少了无意错误的可能性,提供了一种更 Pythonic 和用户友好的方式来使用

AmbiguousClass
a
实例化
b

© www.soinside.com 2019 - 2024. All rights reserved.