Reputation: 4102
below is two classes showing inheritance for an API I am working on. So I want the base class, ServiceTemplateTest, to have a common set of properties for all services and to behave like an OrderedDict object. So the base class inherirts from OrderedDict. I then do a super()
in the __init__
in order to clear up the MRO issue. Now when I actually use this base class, I am getting issues when I try to class __init()
from the base class. According to my debugger it's saying I need to call: self._ServiceTemplateTest__init()
, but shouldn't it just be __init()
since I call super()? What is the proper way to allow me to inherit without have to do this call: self._ServiceTemplateTest__init()
?
Do I need to create an __init__()
on the non-base classes where I have multiple super() calls? If so, what super class should come first?
Thank you for any advice you can provide!
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
Upvotes: 0
Views: 81
Reputation: 77902
dcrosta already answered (mainly the same thing as I was about to post) on the problem with calling __init
from a subclass. I just wanted to add that in your code example, the whole SchematicService.__init__()
is just useless, as it will only redo what ServiceTemplateTest.__init__()
already done.
Also, having both class attributes and instance attributes with the same names doesn't help wrt/ readability / maintainability. If these are intended to be defaults for not-yet-set instance attributes, it's better to set them as instance attributes in the __init__()
.
Upvotes: 1
Reputation: 26258
The issue isn't inheritance or super()
, but that you're trying to call a "private" method from outside of the class. Any method whose name begins with two underscores -- your __init()
in this case -- are private to the class they're defined in.
Python doesn't really have "private" in the sense you might be familiar with from other OO languages, instead it does something called name mangling to make it inconvenient, rather than impossible. In essence, if you name a method like __init()
, Python will convert that to a method named _NameOfClass__init()
, and will do the same in calls (or access to attributes) with similar naming. The trick is, the "NameOfClass" part is always the name of the class you're accessing the method from -- the subclass, SchematicService
, in your case. Since the names don't match, Python can't find the method.
Of course, in Python nothing is actually really private. You can access the private method by mangling its name yourself, if you want. The conventional wisdom, though, is generally not to use double-underscore private methods or attributes at all. By convention, if you want to have a method on a base class that is not meant to be called from outside of the base class (eg because it isn't part of the public API you want to support), name it with a single leading underscore. Pythonistas know that this means, "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: If you intend the method to be callable from anywhere -- both in subclasses and other, unrelated code -- make it a regular public method (no leading underscores in the name); if you only want it accessible to subclasses, use a single leading underscore.
Upvotes: 2