I am trying to write a clean code and by doing this learning one or two things.
class Building:
def __init__(self, db):
self.__db = db
def decode(self, value):
field = dict()
for key in self.__db:
field[key] = int(value)
return field
class Apartment(Building):
def __init__(self, db):
super().__init__(db)
def decode(self, str_value):
field = super().decode(str_value)
field = self.scaling(field)
return field
def scaling(self, field):
field['size'] /= 1000
return field
class Home(Building):
def __init__(self, db):
super().__init__(db)
def decode(self, str_value):
field = super().decode(str_value)
field = self.scaling(field)
return field
def scaling(self, field):
field['garden'] /= 25
return field
def main():
apartment_db = { 'size' : None}
home_db = { 'garden' : None}
app = Apartment(apartment_db)
home = Home(home_db)
print(app.decode('5'))
print(home.decode('100'))
if __name__ == "__main__":
main()
Output:
{'size': 0.005} {'garden': 4.0}
As it can be seen, each derived class have its own databank and its own scaling (no scaling is in my opinion is scaling as well).
My thinking problem is that the base class do not know the scaling implementation, which is defined in the derived class.
If i could pass the scaling implementation of the derived class to the base class, then my decode method in the derived class will be very simply:
class Apartment(Building):
def decode(self,str_value):
return super().decode(str_value)
Is there a way to do it in python3.7?
Yes it is possible.
If you are fine with adding a new method to the Building
class, a simple way would be add the scaling
method to it as well. Now the derived classes, which don't have their own definition for scaling
, will just use the Building
's trivial implementation:
class Building:
def __init__(self, db):
self.__db = db
def decode(self, value):
field = dict()
for key in self.__db:
field[key] = int(value)
return self.scaling(field)
def scaling(self, value):
return value
If you don't want to add a method to Building
, you still have some options. The look before you leap way to do this, would be to check if self
has the attribute 'scaling'
, and then pass field
through self.scaling
if that is true. This works becuse self
is always the instance which we are calling the method on, so it will have the attributes that are defined for that object's class. Here's a quick implementation of that:
class Building:
def __init__(self, db):
self.__db = db
def decode(self, value):
field = dict()
for key in self.__db:
field[key] = int(value)
if hasattr(self, 'scaling'):
field = self.scaling(field)
return field
Alternatively, the easier to ask forgiveness than permission way, would be to use try-except
, and catch the AttributeError
and do no scaling when the method doesn't exist:
class Building:
def __init__(self, db):
self.__db = db
def decode(self, value):
field = dict()
for key in self.__db:
field[key] = int(value)
try:
field = self.scaling(field)
except AttributeError:
pass
return field
This can be also done without adding any methods and even without using an if
-statement or try-except
, with a kinda funny hack. This passes a lambda, which just returns its argument (=identity function), as the 3rd argument for the gettattr
function. This 3rd argument is the default value that gettattr
returns when it cannot find the given attribute. Then we simply call the return value of the gettattr
with the field
as an argument and return the result of that:
class Building:
def __init__(self, db):
self.__db = db
def decode(self, value):
field = dict()
for key in self.__db:
field[key] = int(value)
return getattr(self, 'scaling', lambda x: x)(field)
All of the above ways will let you remove the __init__
and decode
methods from the derived classes and have them just implement scaling
if necessary. They will also all give the same results when used like here:
class Building:
# Any of the above implementations,
# will give the exact same results.
class Apartment(Building):
def scaling(self, field):
field['size'] /= 1000
return field
class Home(Building):
def scaling(self, field):
field['garden'] /= 25
return field
def main():
apartment_db = { 'size' : None}
home_db = { 'garden' : None}
app = Apartment(apartment_db)
home = Home(home_db)
print(app.decode('5'))
print(home.decode('100'))
if __name__ == "__main__":
main()
Output:
{'size': 0.005} {'garden': 4.0}
Inheritance works from Base class to Derived class, so in short, I don't believe you can call a Derived-class method from the Base-class. If we are talking about instances then maybe there are some tricks.
It is also good practice to think Derived class as a specialization of the Base class, so that common behavior remains in the Base class and you just tweak (aka override) some parameters and methods where it is needed, and for the particular purpose. This makes the entire code more readable and maintainable.
For your particular case, I can think of a different solution, where you could embed the scaling directly in your Base class, and change the scaling factor only in your derived classes, something like:
class Base(object):
def __init__(self):
self.scaling = 1
def decode(self, value):
field = { 'distance' : int(value)/self.scaling }
return field
class Derived(Base):
def __init__(self):
self.scaling = 1000
base = Base()
print(base.decode(5))
derived = Derived()
print(derived.decode(5))
All you need to change, in this case, is the self.scaling
value in any class you derive from Base
. Your real-world scenario might be more complex, but I believe this paradigm would serve you better.
There are two approaches you can take. First, make scaler
a function argument to decode
; it doesn't need to be a method, because you aren't using its self
argument. (Although for each derived class implementation, I'll define each class's scaler as a private static method. Note, though, that you don't need a derived class simply to define a scaler; you can instantiate Building
directly and pass an appropriate function directly to Building.decode
)
class Building:
def __init__(self, db):
self.__db = db
def decode(self, value, scaler=lambda x: x):
field = dict()
for key in self.__db:
field[key] = int(value)
return scaler(field)
class Apartment(Building):
@staticmethod
def _scale_by_1000(d):
d['size'] /= 1000
return d
def decode(self, str_value):
return super().decode(str_value, self._scale_by_1000)
class Home(Building):
@staticmethod
def _scale_by_25(d):
d['garden'] /= 25
return d
def decode(self, str_value):
return super().decode(str_value, self._scale_by_25)
Alternately, define a do-nothing scaling
method in Building
, and let derived class override it as necessary.
class Building:
def __init__(self, db):
self.__db = db
def decode(self, value):
field = dict()
for key in self.__db:
field[key] = int(value)
return self.scaling(field)
def scaling(self, d):
return d
class Apartment(Building):
def scaling(self, field):
field['size'] /= 1000
return field
class Home(Building):
def scaling(self, field):
field['garden'] /= 25
return field
Note that in both cases, I've dispensed with overriding methods like __init__
where the derived class did nothing but use super
to call the parent method with the same arguments. Inheritance ensures that the parent will be called if no override is defined.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.