【问题标题】:How to create a non-instantiable class?如何创建一个不可实例化的类?
【发布时间】:2013-04-23 13:44:57
【问题描述】:

对于我目前正在工作的项目之一,我正在考虑创建一个无法由客户端实例化并且只能通过特定接口提供实例的类,即客户端将无法创建更多实例它是由一些黑客攻击的,例如:

>>> try:
...     raise WindowsError
... except:
...     foo = sys.exc_info()
... 
>>> foo
(<type 'exceptions.WindowsError'>, WindowsError(), <traceback object at 0x0000000005503A48>)
>>> type(foo[2])()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create 'traceback' instances

一旦他有了。

我成功地创建了一个无法实例化的类。即

>>> class Foo():
...     def __init__(self):
...         raise TypeError("cannot create 'Foo' instances")
... 
>>> bar = Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: cannot create 'Foo' instances
>>> bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined

但是我如何使用每个相同的定义来创建类的实例呢?

当然我可以这样做:

>>> class Foo():
...     def __init__(self, instantiate = False):
...         if not instantiate:
...               raise TypeError("cannot create 'Foo' instances")

但我觉得它不够优雅,也没有完全阻止客户端进一步实例化它。不,我不会为它构建一个 C++ 模块。

关于如何实现这样的事情有什么建议吗? import abc?

回答 Martijn 的 question 的简要理由并为了完整起见:

实际上,您可以将所讨论的特定相关类的实例视为树中的节点,并且父级和子级都保持连接,相互依赖和认识,并具有唯一的唯一性贯穿任何 python 实例的根(通过使用package 来保证)。特定节点中的任何状态更改都会导致其他人相应地更新自己和他们所连接的数据库。除此之外,我很想知道如何实现这样的事情(traceback 类在取笑我)。

【问题讨论】:

  • 在某些语言中,您创建了一个私有构造函数。我的 Python 技能为零,仅供参考。
  • 为什么需要这个?为什么必须阻止创建实例?
  • @granadaCoder:纯 Python 中没有私有的东西,只有 C 定义的扩展类型才能隐藏东西。
  • 哈!现在我知道关于python的一件事了!谢谢。
  • 不用说,这是个糟糕的主意。

标签: python instantiation objectinstantiation


【解决方案1】:

你正在做的是一个坏主意,你不应该这样做。 我敢肯定还有其他更好的解决方案。 如果您确实决定按照自己的方式行事(您不应该这样做),那么您可以在不使用 __init__() 的情况下创建对象:


python 中的对象是使用__new__() 方法创建的。 __init__() 方法只编辑由__new__() 创建的对象。比如__init__()通常会为对象初始化一些属性。

当声明类似x = Foo() 时会发生这样的事情:

  1. x = object.__new__(Foo) 首先被调用并创建对象。
  2. Foo.__init__(x) 被调用第二个,它只是将一些属性等初始化为已经存在的对象。

这意味着您不需要调用Foo()(因此,也可以调用__init__())。相反,您可以直接拨打__new__()

class Foo(object):
    def __init__(self):
        raise TypeError("Cannot create 'Foo' instances.")

>>> x = object.__new__(Foo)
>>> x
<__main__.Foo object at 0x02B074F0>

我们的x 现在是Foo 的一个实例,没有任何属性,它可以使用Foo 类中定义的任何方法。

如果需要,您可以创建自己的 __init__ 替换函数来初始化属性:

def init_foo(foo, name):
    foo.name = name

>>> init_foo(x, "Mike")
>>> x.name
'Mike'

这当然也可以是Foo的实例方法:

class Foo(object):
    def __init__(self):
        raise TypeError("Cannot create 'Foo' instances.")

    def init(self, name):
        self.name = name

>>> x = object.__new__(Foo)
>>> x.init("Mike")
>>> x.name
'Mike'

更进一步,您甚至可以使用 classmethod 只需一次调用即可创建对象:

class Foo(object):
    def __init__(self):
        raise TypeError("Cannot create 'Foo' instances.")

    @classmethod
    def new(cls, name):
        obj = object.__new__(cls)
        obj.name = name
        return obj

>>> x = Foo.new("Mike")
>>> x.name
'Mike'

【讨论】:

  • 您是否使用 python 3.x 因为在 2.6/2.7 上,如果我不继承 Foo 中的 object,我会得到 TypeError: object.__new__(X): X is not a type object (classobj)。仅供参考traceback 在使用您的方法时拒绝实例化。不过欣赏你的方法。
  • @hus787 是的,我在python 3.3,这导致了您的错误。我编辑了答案,所以Foo 现在继承了object
  • @hus787 另外,我很确定traceback 是一个用C 编码的类,不能在python 中重新创建。您要求的类不能被实例化,不能被子类化,您仍然可以创建它的实例。这是你能得到的最好的。
  • 既然python有一袋好东西,我想可能有办法实现这样的事情,所以我问了这个问题。在得出结论之前,我将对此进行一些独立的实验。它应该是客户端不可实例化的。顺便说一句,我已经更新了问题。
  • 您能否解释一下为什么 @BleedingFingers 的做法是个坏主意?我相信你是对的,但除非你用事实来支持你的陈述,否则它似乎更像是一种观点,而且由于我们不明白它为什么不好,我们不太可能听从你的警告。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-22
  • 1970-01-01
  • 1970-01-01
  • 2010-12-28
  • 2023-02-16
  • 1970-01-01
相关资源
最近更新 更多