使用 super() 时的继承问题 (Python)
Inheritance issue when using super() (Python)
下面的两个 classes 显示了我正在处理的 API 的继承。因此,我希望基础 class、ServiceTemplateTest 具有所有服务 和 的一组通用属性,使其表现得像 OrderedDict 对象。所以基础 class 继承自 OrderedDict。然后我在 __init__
中执行 super()
以解决 MRO 问题。现在,当我实际使用这个基 class 时,当我尝试从基 class class __init()
时遇到问题。根据我的调试器,它说我需要调用:self._ServiceTemplateTest__init()
,但它不应该只是 __init()
因为我调用了 super()?允许我继承而不必执行此调用的正确方法是什么:self._ServiceTemplateTest__init()
?
我是否需要在我有多个 super() 调用的非基础 class 上创建一个 __init__()
?如果是这样,什么 super class 应该排在第一位?
感谢您提供的任何建议!
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
问题不是继承或 super()
,而是您试图从 class 外部调用 "private" 方法。名称以两个下划线开头的任何方法——在本例中为您的 __init()
——对于它们在其中定义的 class 是私有的。
Python 并没有真正的 "private" 你可能熟悉的其他 OO 语言,而是做了一些叫做 name mangling 的事情来让它变得不方便,而不是不可能的。从本质上讲,如果您将方法命名为 __init()
,Python 会将其转换为名为 _NameOfClass__init()
的方法,并且会在具有类似命名的调用(或访问属性)中执行相同的操作。诀窍是,"NameOfClass" 部分始终是您正在访问方法 from 的 class 的名称——subclass,SchematicService
,在你的情况下。由于名称不匹配,Python 找不到方法。
当然,在 Python 中,没有什么是真正私密的。如果需要,您可以通过自己修改其名称来访问私有方法。但是,传统观点通常根本不使用双下划线私有方法或属性。按照惯例,如果你想在基 class 上有一个方法,而不是从基 class 外部调用(例如,因为它不是 public 的一部分) API 你想支持),用一个前导下划线命名它。 Pythonistas 知道这意味着,"the signature, purpose, or even presence of this method or attribute might go away at a later date, and I should not rely on it"。
My $0.02:如果您希望该方法可以从任何地方调用——在子classes 和其他不相关的代码中——使它成为一个常规的public 方法(没有前导下划线在名字里);如果您只希望子classes 可以访问它,请使用单个前导下划线。
dcrosta 已经回答了(主要是我正要回答的问题 post)关于从 subclass 调用 __init
的问题。我只是想在您的代码示例中补充一点,整个 SchematicService.__init__()
毫无用处,因为它只会重做 ServiceTemplateTest.__init__()
已经完成的工作。
此外,具有相同名称的 class 属性和实例属性无助于提高可读性/可维护性。如果这些是尚未设置的实例属性的默认值,最好将它们设置为 __init__()
.
中的实例属性
下面的两个 classes 显示了我正在处理的 API 的继承。因此,我希望基础 class、ServiceTemplateTest 具有所有服务 和 的一组通用属性,使其表现得像 OrderedDict 对象。所以基础 class 继承自 OrderedDict。然后我在 __init__
中执行 super()
以解决 MRO 问题。现在,当我实际使用这个基 class 时,当我尝试从基 class class __init()
时遇到问题。根据我的调试器,它说我需要调用:self._ServiceTemplateTest__init()
,但它不应该只是 __init()
因为我调用了 super()?允许我继承而不必执行此调用的正确方法是什么:self._ServiceTemplateTest__init()
?
我是否需要在我有多个 super() 调用的非基础 class 上创建一个 __init__()
?如果是这样,什么 super class 应该排在第一位?
感谢您提供的任何建议!
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
问题不是继承或 super()
,而是您试图从 class 外部调用 "private" 方法。名称以两个下划线开头的任何方法——在本例中为您的 __init()
——对于它们在其中定义的 class 是私有的。
Python 并没有真正的 "private" 你可能熟悉的其他 OO 语言,而是做了一些叫做 name mangling 的事情来让它变得不方便,而不是不可能的。从本质上讲,如果您将方法命名为 __init()
,Python 会将其转换为名为 _NameOfClass__init()
的方法,并且会在具有类似命名的调用(或访问属性)中执行相同的操作。诀窍是,"NameOfClass" 部分始终是您正在访问方法 from 的 class 的名称——subclass,SchematicService
,在你的情况下。由于名称不匹配,Python 找不到方法。
当然,在 Python 中,没有什么是真正私密的。如果需要,您可以通过自己修改其名称来访问私有方法。但是,传统观点通常根本不使用双下划线私有方法或属性。按照惯例,如果你想在基 class 上有一个方法,而不是从基 class 外部调用(例如,因为它不是 public 的一部分) API 你想支持),用一个前导下划线命名它。 Pythonistas 知道这意味着,"the signature, purpose, or even presence of this method or attribute might go away at a later date, and I should not rely on it"。
My $0.02:如果您希望该方法可以从任何地方调用——在子classes 和其他不相关的代码中——使它成为一个常规的public 方法(没有前导下划线在名字里);如果您只希望子classes 可以访问它,请使用单个前导下划线。
dcrosta 已经回答了(主要是我正要回答的问题 post)关于从 subclass 调用 __init
的问题。我只是想在您的代码示例中补充一点,整个 SchematicService.__init__()
毫无用处,因为它只会重做 ServiceTemplateTest.__init__()
已经完成的工作。
此外,具有相同名称的 class 属性和实例属性无助于提高可读性/可维护性。如果这些是尚未设置的实例属性的默认值,最好将它们设置为 __init__()
.