阻止其他类的方法调用我的构造函数

jdm*_*jdm 5 python python-2.7

如何使python"构造函数""私有",以便只能通过调用静态方法创建其类的对象?我知道Python中没有类似C++/Java的私有方法,但我正在寻找另一种方法来阻止其他人调用我的构造函数(或其他方法).

我有类似的东西:

class Response(object):
    @staticmethod
    def from_xml(source):
        ret = Response()
        # parse xml into ret            
        return ret

    @staticmethod
    def from_json(source):
        # parse json
        pass
Run Code Online (Sandbox Code Playgroud)

并希望以下行为:

r = Response() # should fail
r = Response.from_json(source) # should be allowed
Run Code Online (Sandbox Code Playgroud)

使用静态方法的原因是我总是忘记构造函数采用的参数 - 比如JSON或已经解析过的对象.即便如此,我有时会忘记静态方法并直接调用构造函数(更不用说使用我的代码的其他人).记录这份合同对我的遗忘无益.我宁愿用断言强制执行它.

与一些评论者相反,我认为这不是单一的 - "显性比隐性更好","应该只有一种方法".

当我做错时怎么能得到温柔的提醒?我更喜欢一个解决方案,我不需要更改静态方法,只需一个装饰器或构造函数的单行插件就可以了.一个la:

class Response(object):
    def __init__(self):
        assert not called_from_outside()
Run Code Online (Sandbox Code Playgroud)

bru*_*ers 14

我认为这就是你要找的东西 - 但就我而言,这是一种无声的.

class Foo(object):
    def __init__(self):
        raise NotImplementedError()

    def __new__(cls):
        bare_instance = object.__new__(cls)
        # you may want to have some common initialisation code here
        return bare_instance


    @classmethod
    def from_whatever(cls, arg):
        instance = cls.__new__(cls)
        instance.arg = arg
        return instance
Run Code Online (Sandbox Code Playgroud)

给出您的示例(from_jsonfrom_xml),我假设您正在从json或xml源中检索属性值.在这种情况下,pythonic解决方案将有一个普通的初始化程序,并从您的备用构造函数调用它,即:

class Foo(object):
    def __init__(self, arg):
        self.arg = arg

    @classmethod
    def from_json(cls, source):
        arg = get_arg_value_from_json_source(source)
        return cls(arg)

    @classmethod
    def from_xml(cls, source):
        arg = get_arg_value_from_xml_source(source)
        return cls(arg)
Run Code Online (Sandbox Code Playgroud)

哦,是的,关于第一个例子:它将阻止你的类以通常的方式实例化(调用类),但客户端代码仍然可以调用Foo.__new__(Foo),所以这真的是浪费时间.如果你不能以最普通的方式实例化你的课程,那么它将使单元测试变得更难......而且我们中的很多人会因此而讨厌你.

  • 然后记录它.如果您的用户遇到与您相同的问题(我能感受到您的痛苦),那么我们都会感到抱歉,但这就是生活.但实际上,"普通的初始化器+替代构造器"*是*pythonic方式,而作为Python程序员,这是*我*期望的. (3认同)

use*_*ica 12

我建议将工厂方法转换为模块级工厂函数,然后将类本身隐藏在模块用户之外.

def one_constructor(source):
    return _Response(...)

def another_constructor(source):
    return _Response(...)

class _Response(object):
    ...
Run Code Online (Sandbox Code Playgroud)

你可以看到这种方法在像模块中使用re,其中匹配对象,只能通过类似功能构建matchsearch,和文档实际上并没有命名的匹配对象类型.(至少,3.4文档没有.2.7文档错误引用re.MatchObject,但不存在.)匹配对象类型也拒绝直接构造:

>>> type(re.match('',''))()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create '_sre.SRE_Match' instances
Run Code Online (Sandbox Code Playgroud)

但不幸的是,它的方式依赖于C API,因此它不适用于普通的Python代码.


Mar*_*eld 1

这可能可以通过元类来实现,但在 Python 中是非常不鼓励的。Python 不是 Java。Python 中不存在公共与私有的一流概念;这个想法是,该语言的使用者是“同意的成年人”,可以使用他们喜欢的方法。一般来说,旨在“私有”的函数(不属于 API 的一部分)由一个前导下划线表示;然而,这主要只是约定,没有什么可以阻止用户使用这些功能。

在您的情况下,Pythonic 要做的事情是将构造函数默认为可用的 from_foo 方法之一,甚至创建一个“智能构造函数”,它可以在大多数情况下找到适当的解析器。或者,将可选关键字 arg 添加到__init__确定要使用哪个解析器的方法。