Python:初始化类的多种方法

4 投票
1 回答
5244 浏览
提问于 2025-04-17 22:05

我有一个类 A,它可以用两种不同的方式来“初始化”。所以,我根据这篇文章中的第二个答案,为它提供了一个类似工厂的接口。

class A(object):

    @staticmethod
    def from_method_1(<method_1_parameters>):
        a = A()
        # set parameters of 'a' using <method_1_parameters>
        return a

    @staticmethod
    def from_method_2(<method_2_parameters>):
        a = A()
        # set parameters of 'a' using <method_2_parameters>
        return a

这两种方法的差别很大,我不能简单地把它们的参数放到类的 __init__ 方法里。所以,类 A 应该用以下方式来初始化:

a = A.from_method_1(<method_1_parameters>)

或者

a = A.from_method_2(<method_2_parameters>)

不过,还是可以调用 A 的“默认初始化”方法:

a = A() # just an empty 'A' object

有没有什么办法可以阻止这种情况发生?我不能仅仅在 __init__ 里抛出 NotImplementedError,因为这两个“工厂方法”也会用到它。

或者我需要完全换一种方法来解决这个问题。

1 个回答

5

这个问题已经被问了很久,但我觉得它足够有趣,可以重新提出来。

当我第一次看到你的问题时,私有构造函数的概念一下子就浮现在我的脑海里。这个概念在其他面向对象编程语言中很重要,但因为Python并不强制隐私,所以我在Python成为我的主要语言后就没怎么考虑过这个问题。

因此,我产生了好奇,找到了这个“Python中的私有构造函数”的问题。这个问题几乎涵盖了这个主题的所有内容,我觉得第二个回答在这里可能会有帮助。

基本上,它使用名称改编来声明一个伪私有的类属性(在Python中并没有真正的私有变量),并将类对象赋值给它。这样,你就会有一个在Python允许的范围内尽可能私有的变量,用来检查你的初始化是通过类方法还是外部调用完成的。我根据这个机制做了以下示例:

class A(object):
    __obj = object()

    def __init__(self, obj=None):
        assert(obj == A.__obj), \
            'A object must be created using A.from_method_1 or A.from_method_2'

    @classmethod
    def from_method_1(cls):
        a = A(cls.__obj)
        print('Created from method 1!')
        return a

    @classmethod
    def from_method_2(cls):
        a = A(cls.__obj)
        print('Created from method 2!')
        return a

测试:

>>> A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "t.py", line 6, in __init__
    'A object must be created using A.from_method_1 or A.from_method_2'
AssertionError: A object must be created using A.from_method_1 or A.from_method_2
>>> A.from_method_1()
Created from method 1!
<t.A object at 0x7f3f7f2ca450>
>>> A.from_method_2()
Created from method 2!
<t.A object at 0x7f3f7f2ca350>

不过,由于这个解决方案是通过名称改编来实现的,它确实有一个缺陷,如果你知道怎么去查找的话:

>>> A(A._A__obj)
<t.A object at 0x7f3f7f2ca450>

撰写回答