I came across this SESE page: What is a proper use of downcasting? (for C#) -- and it talks about when to downcast, and the possible downsides to it.
In Python sub classes are slightly different
Example:
Suppose I have the following abstract
class:
from abc import ABCMeta
from abc import abstractmethod
class Weapon(metaclass=ABCMeta):
def __init__(self, name, damage):
self.name = name
self.damage = damage
@abstractmethod
def prepare(self):
pass
@abstractmethod
def attack(self):
pass
@abstractmethod
def cleanup(self):
pass
def __str__(self):
return "Name: {} Damage: {}".format(self.name, self.damage)
and Subclass:
from Weapon import Weapon
class ChargeGun(Weapon):
def __init__(self, name, damage):
super().__init__(name, damage)
def prepare(self):
print("Aiming my Gun");
def attack(self):
print("Shooting")
def cleanup(self):
print("Lowering gun, and turning off laser sight")
def charge(self):
print("Charging Gun. Please hold..(elevator music playing)")
In my subclass ChargeGun
, I have the def charge(self):
method, and I can declare in the main a ChargeGun
object and use it, like this:
cg = ChargeGun("Standard ChargeGun",5)
cg.prepare()
cg.charge()
If I were to introduce Sword
as a Weapon
subclass, the charge(self):
would not be implemented, and Sword
isn't even aware of it. In Python, there isn't a need to downcast or use a design pattern.
Python isn't Java/C#.
Questions
How should you be thinking when creating sub classes in languages like Python? Should we be thinking from an abstract point of view, and refactor our code?
Example:
def charge(self):
could be seen as a way to prepare aChargeGun
so we can remove it. If I had a silencer, that's a a component of a Gun, so maybe it might make sense to have anabstract
WeaponComponent
class, a silencer and laser sight could be objects of that class and each would override thedef prepare(self):
method. A Sword could have a gem or stone that gives it extra strike ability, etc.
That's just one way of thinking, I'm not saying it's the best way to refactor it.
- Is it okay to take advantage of Python's ability to use the methods of a subclass directly?
1 Answer 1
Give me an API that lets me write code like this:
def useWeapon(someWeapon) :
someWeapon.charge()
someWeapon.prepare()
someWeapon.attack()
someWeapon.cleanup()
useWeapon( ChargeGun("Standard ChargeGun",5) )
And I wont mind if charge()
happens instantly or doesn't change the state of the weapon in anyway. I will mind if it blows up with an error or if I have to test if the weapon needs charging. Sure, this means that objects have to have knowledge of messages that seem to have nothing to do with them. But understand that the interface exists for the client that uses the weapon. Not the weapon.
If you let knowledge of what kind of weapon this is leak out suddenly the client has to KNOW what it's talking to. This is the death of polymorphism.
The favorite example is telling pets to speak. I like not having to tell a dog to bark or a duck to quack. I can just say pet.speak()
and whatever pet that is figures out what to do. If I need my pet collection to sometimes be quiet I use a cat (otherwise known as the null object pattern). You tell it to speak and it ignores you (cause it's a cat). My cat is carefully designed to quietly accept and ignore every possible message. I can send my cat anywhere and things still work nicely without blowing up. However, my pet rock is another issue. You tell my pet rock to speak and it tears a hole in the fabric of reality by complaining that it doesn't know how to speak. Now sure you can be careful and check for rocks but it's easier to use cats. They're really good at doing nothing.
-
If I understand what you're saying, it means I should be thinking in an abstract point of view, so regardless of which
Weapon
I get I can call the necessary methods without it blowing up. Python isn't Java and so you can't sayWeapon weapon;
You would sayChargeGun()
. How do you adjust your thinking or design with languages like that?user297209– user29720902/26/2018 17:31:08Commented Feb 26, 2018 at 17:31 -
Python does objects just fine. Not sure what you're getting at.candied_orange– candied_orange02/26/2018 17:32:36Commented Feb 26, 2018 at 17:32
-
What I mean is, in Java I can do
Weapon cg = new ChargeGun();
I can then write methods that take the Weapon interface or abstract class as a parameter, and it doesn't know specifically what weapon it is, just that it's going to get aWeapon
but in Python I can't declare it the same way as in Java, I have to saycg = ChargeGun()
user297209– user29720902/26/2018 17:36:03Commented Feb 26, 2018 at 17:36 -
Are you talking about strong typing? Python has the same problem with pet rocks that java does. Python just waits until run time to complain about it.candied_orange– candied_orange02/26/2018 17:37:49Commented Feb 26, 2018 at 17:37
-
Unless you use
instanceof
and check it specifically, which is a code smell.user297209– user29720902/26/2018 17:39:07Commented Feb 26, 2018 at 17:39
if isinstance(weapon, ChargeGun): weapon.charge()
, I feel like that answers your question.ChargeGun
, if not directly? Or are you saying don't have thecharge(self):
method?