【问题标题】:Inheritance issue when using super() (Python)使用 super() (Python) 时的继承问题
【发布时间】:2016-04-21 11:21:36
【问题描述】:

下面是两个类,显示了我正在处理的 API 的继承。因此,我希望基类 ServiceTemplateTest 为所有服务 提供一组通用属性,使其表现得像 OrderedDict 对象。所以基类继承自 OrderedDict。然后我在__init__ 中执行super() 以解决MRO 问题。现在,当我实际使用这个基类时,当我尝试从基类中对__init() 进行分类时遇到了问题。根据我的调试器,它说我需要调用:self._ServiceTemplateTest__init(),但不应该只是__init(),因为我调用了 super()?什么是让我无需进行此调用即可继承的正确方法:self._ServiceTemplateTest__init()

我是否需要在我有多个 super() 调用的非基类上创建一个__init__()?如果是这样,什么超类应该首先出现?

感谢您提供的任何建议!

from collections import OrderedDict
import urllib2, json, urllib
class ServiceTemplateTest(OrderedDict):
    _con = None
    _url = None
    def __init__(self, url, connection=None, initialize=False, **kwargs):
        super(ServiceTemplateTest, self).__init__()
        self._url = url
        self._con = connection
        if initialize:
            self.__init(connection)
    def __init(self, connection=None):
        if connection is None:
            connection = self._con
        attributes = [attr for attr in dir(self)
                      if not attr.startswith('__') and \
                      not attr.startswith('_')]
        params = {"f":"json"}
        params = urllib.urlencode(params)
        result = json.loads(
            urllib2.urlopen(url="{url}?{params}".format(url=self._url,
                                                        params=params)).read())

        for k,v in result.items():
            if k in attributes:
                setattr(self, "_"+ k, v)
                self[k] = v
            else:
                self[k] = v
        self.__dict__.update(result)
    #----------------------------------------------------------------------
    @property
    def connection(self):
        return self._con
    #----------------------------------------------------------------------
    @connection.setter
    def connection(self, value):
        self._con = value
        self.refresh()
    #----------------------------------------------------------------------
    @property
    def url(self):
        return self._url
    #----------------------------------------------------------------------
    @url.setter
    def url(self, value):
        """"""
        self._url = value
        self.refresh()
    #----------------------------------------------------------------------
    def __str__(self):
        return json.dumps(self)
    #----------------------------------------------------------------------
    def __repr__(self):
        return self.__str__()
    #----------------------------------------------------------------------
    def refresh(self):
        self.__init()

class SchematicService(ServiceTemplateTest):
    """schematic service"""
    _con = None
    _json_dict = None
    _json = None
    _url = None
    _nbSchematicLayers = None
    _nbTemplates = None
    _type = None
    _name = None
    _nbEstimatedDiagrams = None
    def __init__(self, url, connection=None, initialize=False, **kwargs):
        super(SchematicService, self).__init__(url=url, connection=connection,
                                               initialize=initialize, **kwargs)
        self._url = url
        self._con = connection
        if initialize:
            self.__init(connection)
    #----------------------------------------------------------------------
    @property
    def nbSchematicLayers(self):
        if self._nbSchematicLayers is None:
            self.__init()
        return self._nbSchematicLayers
    #----------------------------------------------------------------------
    @property
    def nbTemplates (self):
        if self._nbTemplates  is None:
            self.__init()
        return self._nbTemplates
    #----------------------------------------------------------------------
    @property
    def type(self):
        if self._type  is None:
            self.__init()
        return self._type
    #----------------------------------------------------------------------
    @property
    def name(self):
        if self._name is None:
            self.__init()
        return self._name
    #----------------------------------------------------------------------
    @property
    def nbEstimatedDiagrams(self):
        if self._nbEstimatedDiagrams is None:
            self.__init()
        return self._nbEstimatedDiagrams
    @property
    def somerandompropertytest(self):
        return "hi"
if __name__ == "__main__":
    url = "http://servicesbeta6.esri.com/arcgis/rest/services/S1_Schematics/MapServer"
    s = SchematicService(url=url, initialize=True)
    print s

【问题讨论】:

    标签: python python-2.7 super


    【解决方案1】:

    问题不在于继承或super(),而是您试图从类外部调用“私有”方法。任何名称以两个下划线开头的方法(在这种情况下是您的 __init())都是它们所定义的类的私有方法。

    Python 并不真正具有您可能从其他 OO 语言中熟悉的意义上的“私有”,相反,它做了一些称为 name mangling 的事情,以使其不方便,而不是不可能。本质上,如果您将方法命名为__init(),Python 会将其转换为名为@9​​87654325@ 的方法,并且会在具有类似命名的调用(或访问属性)中执行相同的操作。诀窍是,“NameOfClass”部分始终是您正在访问 from 方法的类的名称——在您的情况下是子类SchematicService。由于名称不匹配,Python 无法找到该方法。

    当然,在 Python 中,实际上没有什么是真正私有的。如果需要,您可以通过自己修改其名称来访问私有方法。然而,传统观点通常是根本不使用双下划线私有方法或属性。按照惯例,如果您想在基类上拥有一个不打算从基类外部调用的方法(例如,因为它不是您想要支持的公共 API 的一部分),请使用单个前导下划线。 Pythonistas 知道这意味着,“这个方法或属性的签名、目的甚至存在可能会在以后消失,我不应该依赖它”。

    我的 0.02 美元:如果您希望该方法可以从任何地方调用——无论是在子类中还是在其他不相关的代码中——让它成为一个常规的公共方法(名称中没有前导下划线);如果您只希望子类可以访问它,请使用单个前导下划线。

    【讨论】:

    • 谢谢,这很有帮助。我总是学到新东西!
    • 通过更改名称像魅力一样工作。欣赏它。
    【解决方案2】:

    dcrosta 已经就从子类调用__init 的问题做出了回答(主要与我即将发布的内容相同)。我只是想在您的代码示例中添加这一点,整个 SchematicService.__init__() 只是没用,因为它只会重做 ServiceTemplateTest.__init__() 已经完成的事情。

    此外,具有相同名称的类属性和实例属性无助于 wrt/可读性/可维护性。如果这些旨在作为尚未设置的实例属性的默认值,则最好将它们设置为 __init__() 中的实例属性。

    【讨论】:

    • 你能用一个例子解释你最后的陈述吗?
    猜你喜欢
    • 2020-07-05
    • 2020-07-24
    • 2020-10-03
    • 2020-05-15
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 1970-01-01
    相关资源
    最近更新 更多