Two Python classes identical except for superclass - how to avoid duplication? - python

I have some functionality wrapped up in a Python class (classa). classa inherits from another class supera.
I want exactly the same functionality as classa except that I want to inherit from superb.
I could just copy the class classa to a new class classb and then change the superclass for classb but obviously this very tacky, a maintenance headache and and I'm quite sure there's much better way - can anyone tell me what it is ?
EDIT: Thanks for answers so far. I should have said initially the my classa invokes super in its methods in order to invoke supera methods. It seems that this has some significance when looking at mixins as an option

This can be done with Python's multiple inheritance if none of the methods need to invoke super().
class Dog(object):
name = "Spot"
class Cat(object):
name = "Whiskers"
class SpeakingAnimalMixin(object):
def speak(self):
print "My name is", self.name, "and I can speak!"
class SpeakingDog(SpeakingAnimalMixin, Dog):
pass
class SpeakingCat(SpeakingAnimalMixin, Cat):
pass
SpeakingDog().speak()
My name is Spot and I can speak!
If you do need to invoke super() from a method then you need to create the class dynamically. This works fine, but the generated class's name will be less helpful and IDEs and other static analysis tools may be less useful.
You can create the class using a function, passing the superclass as an argument:
def make_speaking_animal_class(SpeechlessAnimal):
class SpeakingAnimal(SpeechlessAnimal):
def get_name(self):
return "Speaking " + super(SpeakingAnimal, self).get_name()
def speak(self):
print "My name is", self.get_name()
return SpeakingAnimal
class Dog(object):
def get_name(self):
return "Spot"
class Cat(object):
def get_name(self):
return "Whiskers"
SpeakingDog = make_speaking_animal_class(Dog)
SpeakingCat = make_speaking_animal_class(Cat)
SpeakingCat().speak()
My name is Speaking Whiskers
However as mentioned, the class's __name__ attribute may not be what you expect.
print SpeakingDog
print SpeakingDog()
<class '__main__.SpeakingAnimal'>
<__main__.SpeakingAnimal object at 0x1004a3b50>
You can fix this by assigning them unique __name__ attributes yourself:
SpeakingDog.__name__ = 'SpeakingDog'
print SpeakingDog
<class '__main__.SpeakingDog'>
(Credit to Andrew Jaffe for suggesting this in an answer, but he deleted it.)
There's another way to create a class dynamically, but I discourage you from using it unless you need to; it's even less clear. The type function has a second use, apart from its main one of determining the class of an object: it can be used to dynamically create a new class.
When used this way, the type function takes three parameters:
name, the __name__ the new class will have.
bases, a tuple of of base classes that the new class will inherit from.
dict, a dictionary containing the methods and attributes the new class will have.
You could use it like this:
def make_speaking_animal_class(SpeechlessAnimal, name):
def get_name(self):
return "Speaking " + super(SpeakingAnimal, self).get_name()
def speak(self):
print "My name is", self.get_name()
bases = (SpeechlessAnimal,)
# We need to define SpeakingAnimal in a variable so that get_name can refer
# to it for the super() call, otherwise we could just return it directly.
SpeakingAnimal = type(name, bases, {
'get_name': get_name,
'speak': speak
})
return SpeakingAnimal
class Dog(object):
def get_name(self):
return "Spot"
class Cat(object):
def get_name(self):
return "Whiskers"
SpeakingDog = make_speaking_animal_class(Dog, 'SpeakingDog')
SpeakingCat = make_speaking_animal_class(Cat, 'SpeakingCat')
SpeakingDog().speak()
SpeakingCat().speak()
My name is Speaking Spot
My name is Speaking Whiskers

Related

Is there a way to call specific classes based on a variable?

I want a program to call a specific class based on a parameter/variable value. However, I don't want to use any clunky if-statements. My first thought was to use the globals() function, but I couldn't get it to work. Here's an example:
class SomeClass:
def __init__():
print("Hello, world!")
class OtherClass:
def runClass(className):
# Call class based on variable className
The reason I want to do this is because there is a wide variety of classes may need to be called, and so just piling up if-statements in my code won't do it. Any help would be greatly appreciated. Thanks!
Here's how you can call a class via globals
class SomeClass:
def __init__(self):
print("Hello, world!")
def __call__(self):
return "SomeClass called"
class OtherClass:
def runClass(self, className):
globals()[className]()()
o = OtherClass()
result = o.runClass("SomeClass")
print(result)
Notice, I am instantiating and then calling it via the __call__ special method, which is the closest match to your description I could think of.
Use a dict.
name_to_class = dict(some=SomeClass,
other=OtherClass)
def factory(name):
klass = name_to_class(name)
return klass()
some_obj = factory("some")
other_obj = factory("other")
One way to solve this problem is to use a dictionary to map the values of the variable className to the corresponding class.
Try this exemple :
class SomeClass:
def init(self):
print("Hello, world!")
class OtherClass:
def init(self):
print("Goodbye, world!")
classNameToClass = {
"SomeClass": SomeClass,
"OtherClass": OtherClass
}
def runClass(className):
# Call class based on variable className
cls = classNameToClass[className]
return cls()
runClass("SomeClass") # prints "Hello, world!"
runClass("OtherClass") # prints "Goodbye, world!"
Here, the dictionary classNameToClass maps the string names of the classes (e.g. "SomeClass") to the corresponding class objects (e.g. SomeClass). Then, in the runClass function, we look up the class object using the value of the className variable, and call it to create an instance of the class.
I've found an answer. The parameter that governs the called class can just be assigned elsewhere. At first, I thought it would need some complex function, but in reality, I guess the question didn't give enough details. The class itself only uses items from whatever object is given. So, instead of having to dynamically call a class, it's as simple as:
class SomeClass:
def printHelloWorld():
print("Hello, world!")
class OtherClass:
def __init__(self, usingClass):
self.object = usingClass
def doThis():
usingClass.printHelloWorld()
x = OtherClass(SomeClass())
x.doThis()
It's on me for not giving enough information. Thank you all for your help.

Call function of class using a variable function call

I'm trying to call a function from a Class thats name will change depending on what type of enemy is being instantiated. How can I accomplish this?
My attempt was this: AssignClass.[self.Class](self)
but obviously that sintax makes no sense
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
# Using a variable function call to remove
# the need for a ton of 'if' statements
AssignClass.[self.Class](self)
# Basically automate doing this:
if self.Class = "Orc":
AssignClass.Orc(self)
elif self.Class = "Wizard"
AssignClass.Wizard(self)
class AssignClass:
def Orc(Creature):
Creature.Class='Orc'
Creature.Health=100
Creature.Mana=0
def Wizard(Creature):
Creature.Class='Wizard'
Creature.Health=75
Creature.Mana=200
Evil_Wizard = Creature("Evil Wizard", "Wizard")
You can retrieve class methods using getattr() and then just pass your Creature instance, e.g.:
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
getattr(AssignClass, Class)(self)
Btw. this is everything but a recommended style for Python classes, the least of which is that you're shadowing the Creature class in your AssignClass (which shouldn't be a class in the first place). I'd recommend you to read the PEP 8 -- Style Guide for Python Code thoroughly.
Played around a little more and found that I can use eval for this. (Safe as no user input can be added here)
class Creature:
def __init__(self, Name, Class):
self.Name = Name
self.Class = Class
eval('AssignClass.'+Class)(self)

A good practice to implement with python multiple inheritance class?

The Scenario:
class A:
def __init__(self, key, secret):
self.key = key
self.secret = secret
def same_name_method(self):
do_some_staff
def method_a(self):
pass
class B:
def __init__(self, key, secret):
self.key = key
self.secret = secret
def same_name_method(self):
do_another_staff
def method_b(self):
pass
class C(A,B):
def __init__(self, *args, **kwargs):
# I want to init both class A and B's key and secret
## I want to rename class A and B's same method
any_ideas()
...
What I Want:
I want the instance of class C initialize both class A and B, because they are different api key.
And I want rename class A and B's same_name_method, so I will not confused at which same_name_method.
What I Have Done:
For problem one, I have done this:
class C(A,B):
def __init__(self, *args, **kwargs):
A.__init__(self, a_api_key,a_api_secret)
B.__init__(self, b_api_key,b_api_secret)
Comment: I know about super(), but for this situation I do not know how to use it.
For problem two, I add a __new__ for class C
def __new__(cls, *args, **kwargs):
cls.platforms = []
cls.rename_method = []
for platform in cls.__bases__:
# fetch platform module name
module_name = platform.__module__.split('.')[0]
cls.platforms.append(module_name)
# rename attr
for k, v in platform.__dict__.items():
if not k.startswith('__'):
setattr(cls, module_name+'_'+k, v)
cls.rename_method.append(k)
for i in cls.rename_method:
delattr(cls, i) ## this line will raise AttributeError!!
return super().__new__(cls)
Comment: because I rename the new method names and add it to cls attr. I need to delete the old method attr, but do not know how to delattr. Now I just leave them alone, did not delete the old methods.
Question:
Any Suggestions?
So, you want some pretty advanced things, some complicated things, and you don't understand well how classes behave in Python.
So, for your first thing: initializing both classes, and every other method that should run in all classes: the correct solution is to make use of cooperative calls to super() methods.
A call to super() in Python returns you a very special proxy objects that reflects all methods available in the next class, obeying the proper method Resolution Order.
So, if A.__init__ and B.__init__ have to be called, both methods should include a super().__init__ call - and one will call the other's __init__ in the appropriate order, regardless of how they are used as bases in subclasses. As object also have __init__, the last super().__init__ will just call it that is a no-op. If you have more methods in your classes that should be run in all base classes, you'd rather build a proper base class so that the top-most super() call don't try to propagate to a non-existing method.
Otherwise, it is just:
class A:
def __init__(self, akey, asecret, **kwargs):
self.key = akey
self.secret = asecret
super().__init__(**kwargs)
class B:
def __init__(self, bkey, bsecret, **kwargs):
self.key = bkey
self.secret = bsecret
super().__init__(**kwargs)
class C(A,B):
# does not even need an explicit `__init__`.
I think you can get the idea. Of course, the parameter names have to differ - ideally, when writing C you don't have to worry about parameter order - but when calling C you have to worry about suplying all mandatory parameters for C and its bases. If you can't rename the parameters in A or B to be distinct, you could try to use the parameter order for the call, though, with each __init__ consuming two position-parameters - but that will require some extra care in inheritance order.
So - up to this point, it is basic Python multiple-inheritance "howto", and should be pretty straightforward. Now comes your strange stuff.
As for the auto-renaming of methods: first things first -
are you quite sure you need inheritance? Maybe having your granular classes for each external service, and a registry and dispatch class that call the methods on the others by composition would be more sane. (I may come back to this later)
Are you aware that __new__ is called for each instantiation of the class, and all class-attribute mangling you are performing there happens at each new instance of your classes?
So, if the needed method-renaming + shadowing needs to take place at class creation time, you can do that using the special method __init_subclass__ that exists from Python 3.6. It is a special class method that is called once for each derived class of the class it is defined on. So, just create a base class, from which A and B themselves will inherit, and move a properly modified version the thing you are putting in __new__ there. If you are not using Python 3.6, this should be done on the __new__ or __init__ of a metaclass, not on the __new__ of the class itself.
Another approach would be to have a custom __getattribute__ method - this could be crafted to provide namespaces for the base classes. It would owrk ony on instances, not on the classes themselves (but could be made to, again, using a metaclass). __getattribute__ can even hide the same-name-methods.
class Base:
#classmethod
def _get_base_modules(cls):
result = {}
for base in cls.__bases__:
module_name = cls.__module__.split(".")[0]
result[module_name] = base
return result
#classmethod
def _proxy(self, module_name):
class base:
def __dir__(base_self):
return dir(self._base_modules[module_name])
def __getattr__(base_self, attr):
original_value = self._base_modules[module_name].__dict__[attr]
if hasattr(original_value, "__get__"):
original_value = original_value.__get__(self, self.__class__)
return original_value
base.__name__ = module_name
return base()
def __init_subclass__(cls):
cls._base_modules = cls._get_base_modules()
cls._shadowed = {name for module_class in cls._base_modules.values() for name in module_class.__dict__ if not name.startswith("_")}
def __getattribute__(self, attr):
if attr.startswith("_"):
return super().__getattribute__(attr)
cls = self.__class__
if attr in cls._shadowed:
raise AttributeError(attr)
if attr in cls._base_modules:
return cls._proxy(attr)
return super().__getattribute__(attr)
def __dir__(self):
return super().dir() + list(self._base_modules)
class A(Base):
...
class B(Base):
...
class C(A, B):
...
As you can see - this is some fun, but starts getting really complicated - and all the hoola-boops that are needed to retrieve the actual attributes from the superclasses after ading an artificial namespace seem to indicate your problem is not calling for using inheritance after all, as I suggested above.
Since you have your small, functional, atomic classes for each "service" , you could use a plain, simple, non-meta-at-all class that would work as a registry for the various services - and you can even enhance it to call the equivalent method in several of the services it is handling with a single call:
class Services:
def __init__(self):
self.registry = {}
def register(self, cls, key, secret):
name = cls.__module__.split(".")[0]
service= cls(key, secret)
self.registry[name] = service
def __getattr__(self, attr):
if attr in self.registry:
return self.registry[attr]

How remove a variable that appears in __init__?

If i have this:
class One(object):
def __init__(self, name):
self.name = name
I want to use One but altering the name name and relace it by other
The solution I supposed is inheriting:
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
The idea is : How to delete or change the variable names that appears in __init__ ?
There is no relation at all between the name of the parameter passed to __init__ and the name of the instance variable that might eventuality be initialized by that argument. This is only a matter of convention than both are called the same.
Both code fragments below will perform exactly the same:
class One(object):
def __init__(self, name):
self.name = name
class One(object):
def __init__(self, xyz):
self.name = xyz
As about renaming an instance variable, you might do something like that, but this is (very) bad style and has (great) chances to break something in (the base class and/or in any client code that expects a proper One instance):
class Two(One):
def __init__(self, other):
super(Two, self).__init__(other)
self.other = self.name # <- no, seriously,
del self.name # <- don't do that !!!
You can't do what you want, not if you are calling One.__init__ from Two.__init__.
If you want to alter what attributes are set, simply don't call One.__init__() here. Set your own attributes instead:
class One(object):
def __init__(self, name):
self.name = name
class Two(One):
def __init__(self, other):
self.other = other
Now self.name will never be set. This most likely will break the rest of functionality in One, something you probably don't want to do. The rest of the methods in that class are likely to rely on certain attributes having been set.
In OOP terms, if Two is not a special kind of One object, don't inherit from One. If Two is a kind of One object, don't try to make it into something else.

How to execute BaseClass method before it gets overridden by DerivedClass method in Python

I am almost sure that there is a proper term for what I want to do but since I'm not familiar with it, I will try to describe the whole idea explicitly. So what I have is a collection of classes that all inherit from one base class. All the classes consist almost entirely of different methods that are relevant within each class only. However, there are several methods that share similar name, general functionality and also some logic but their implementation is still mostly different. So what I want to know is whether it's possible to create a method in a base class that will execute some logic that is similar to all the methods but still continue the execution in the class specific method. Hopefully that makes sense but I will try to give a basic example of what I want.
So consider a base class that looks something like that:
class App(object):
def __init__(self, testName):
self.localLog = logging.getLogger(testName)
def access(self):
LOGIC_SHARED
And an example of a derived class:
class App1(App):
def __init__(self, testName):
. . .
super(App1, self).__init__(testName)
def access(self):
LOGIC_SPECIFIC
So what I'd like to achieve is that the LOGIC_SHARED part in base class access method to be executed when calling the access method of any App class before executing the LOGIC_SPECIFIC part which is(as it says) specific for each access method of all derived classes.
If that makes any difference, the LOGIC_SHARED mostly consists of logging and maintenance tasks.
Hope that is clear enough and the idea makes sense.
NOTE 1:
There are class specific parameters which are being used in the LOGIC_SHARED section.
NOTE 2:
It is important to implement that behavior using only Python built-in functions and modules.
NOTE 3:
The LOGIC_SHARED part looks something like that:
try:
self.localLog.info("Checking the actual link for %s", self.application)
self.link = self.checkLink(self.application)
self.localLog.info("Actual link found!: %s", self.link)
except:
self.localLog.info("No links found. Going to use the default link: %s", self.link)
So, there are plenty of specific class instance attributes that I use and I'm not sure how to use these attributes from the base class.
Sure, just put the specific logic in its own "private" function, which can overridden by the derived classes, and leave access in the Base.
class Base(object):
def access(self):
# Shared logic 1
self._specific_logic()
# Shared logic 2
def _specific_logic(self):
# Nothing special to do in the base class
pass
# Or you could even raise an exception
raise Exception('Called access on Base class instance')
class DerivedA(Base):
# overrides Base implementation
def _specific_logic(self):
# DerivedA specific logic
class DerivedB(Base):
# overrides Base implementation
def _specific_logic(self):
# DerivedB specific logic
def test():
x = Base()
x.access() # Shared logic 1
# Shared logic 2
a = DerivedA()
a.access() # Shared logic 1
# Derived A specific logic
# Shared logic 2
b = DerivedB()
b.access() # Shared logic 1
# Derived B specific logic
# Shared logic 2
The easiest method to do what you want is to simply call the parent's class access method inside the child's access method.
class App(object):
def __init__(self, testName):
self.localLog = logging.getLogger(testName)
def access(self):
LOGIC_SHARED
class App1(App):
def __init__(self, testName):
super(App1, self).__init__(testName)
def access(self):
App.access(self)
# or use super
super(App1, self).access()
However, your shared functionality is mostly logging and maintenance. Unless there is a pressing reason to put this inside the parent class, you may want to consider is to refactor the shared functionality into a decorator function. This is particularly useful if you want to reuse similar logging and maintenance functionality for a range of methods inside your class.
You can read more about function decorators here: http://www.artima.com/weblogs/viewpost.jsp?thread=240808, or here on Stack Overflow: How to make a chain of function decorators?.
def decorated(method):
def decorated_method(self, *args, **kwargs):
LOGIC_SHARED
method(self, *args, **kwargs)
return decorated_method
Remember than in python, functions are first class objects. That means that you can take a function and pass it as a parameter to another function. A decorator function make use of this. The decorator function takes another function as a parameter (here called method) and then creates a new function (here called decorated_method) that takes the place of the original function.
Your App1 class then would look like this:
class App1(App):
#logged
def access(self):
LOGIC_SPECIFIC
This really is shorthand for this:
class App1(App):
def access(self):
LOGIC_SPECIFIC
decorated_access = logged(App.access)
App.access = decorated_access
I would find this more elegant than adding methods to the superclass to capture shared functionality.
If I understand well this commment (How to execute BaseClass method before it gets overridden by DerivedClass method in Python) you want that additional arguments passed to the parent class used in derived class
based on Jonathon Reinhart's answer
it's how you could do
class Base(object):
def access(self,
param1 ,param2, #first common parameters
*args, #second positional parameters
**kwargs #third keyword arguments
):
# Shared logic 1
self._specific_logic(param1, param2, *args, **kwargs)
# Shared logic 2
def _specific_logic(self, param1, param2, *args, **kwargs):
# Nothing special to do in the base class
pass
# Or you could even raise an exception
raise Exception('Called access on Base class instance')
class DerivedA(Base):
# overrides Base implementation
def _specific_logic(self, param1, param2, param3):
# DerivedA specific logic
class DerivedB(Base):
# overrides Base implementation
def _specific_logic(self, param1, param2, param4):
# DerivedB specific logic
def test():
x = Base()
a = DerivedA()
a.access("param1", "param2", "param3") # Shared logic 1
# Derived A specific logic
# Shared logic 2
b = DerivedB()
b.access("param1", "param2", param4="param4") # Shared logic 1
# Derived B specific logic
# Shared logic 2
I personally prefer Jonathon Reinhart's answer, but seeing as you seem to want more options, here's two more. I would probably never use the metaclass one, as cool as it is, but I might consider the second one with decorators.
With Metaclasses
This method uses a metaclass for the base class that will force the base class's access method to be called first, without having a separate private function, and without having to explicitly call super or anything like that. End result: no extra work/code goes into inheriting classes.
Plus, it works like maaaagiiiiic </spongebob>
Below is the code that will do this. Here http://dbgr.cc/W you can step through the code live and see how it works :
#!/usr/bin/env python
class ForceBaseClassFirst(type):
def __new__(cls, name, bases, attrs):
"""
"""
print("Creating class '%s'" % name)
def wrap_function(fn_name, base_fn, other_fn):
def new_fn(*args, **kwargs):
print("calling base '%s' function" % fn_name)
base_fn(*args, **kwargs)
print("calling other '%s' function" % fn_name)
other_fn(*args, **kwargs)
new_fn.__name__ = "wrapped_%s" % fn_name
return new_fn
if name != "BaseClass":
print("setting attrs['access'] to wrapped function")
attrs["access"] = wrap_function(
"access",
getattr(bases[0], "access", lambda: None),
attrs.setdefault("access", lambda: None)
)
return type.__new__(cls, name, bases, attrs)
class BaseClass(object):
__metaclass__ = ForceBaseClassFirst
def access(self):
print("in BaseClass access function")
class OtherClass(BaseClass):
def access(self):
print("in OtherClass access function")
print("OtherClass attributes:")
for k,v in OtherClass.__dict__.iteritems():
print("%15s: %r" % (k, v))
o = OtherClass()
print("Calling access on OtherClass instance")
print("-------------------------------------")
o.access()
This uses a metaclass to replace OtherClass's access function with a function that wraps a call to BaseClass's access function and a call to OtherClass's access function. See the best explanation of metaclasses here https://stackoverflow.com/a/6581949.
Stepping through the code should really help you understand the order of things.
With Decorators
This functionality could also easily be put into a decorator, as shown below. Again, a steppable/debuggable/runnable version of the code below can be found here http://dbgr.cc/0
#!/usr/bin/env python
def superfy(some_func):
def wrapped(self, *args, **kwargs):
# NOTE might need to be changed when dealing with
# multiple inheritance
base_fn = getattr(self.__class__.__bases__[0], some_func.__name__, lambda *args, **kwargs: None)
# bind the parent class' function and call it
base_fn.__get__(self, self.__class__)(*args, **kwargs)
# call the child class' function
some_func(self, *args, **kwargs)
wrapped.__name__ = "superfy(%s)" % some_func.__name__
return wrapped
class BaseClass(object):
def access(self):
print("in BaseClass access function")
class OtherClass(BaseClass):
#superfy
def access(self):
print("in OtherClass access function")
print("OtherClass attributes")
print("----------------------")
for k,v in OtherClass.__dict__.iteritems():
print("%15s: %r" % (k, v))
print("")
o = OtherClass()
print("Calling access on OtherClass instance")
print("-------------------------------------")
o.access()
The decorator above retrieves the BaseClass' function of the same name, and calls that first before calling the OtherClass' function.
May this simple approach can help.
class App:
def __init__(self, testName):
self.localLog = logging.getLogger(testName)
self.application = None
self.link = None
def access(self):
print('There is something BaseClass must do')
print('The application is ', self.application)
print('The link is ', self.link)
class App1(App):
def __init__(self, testName):
# ...
super(App1, self).__init__(testName)
def access(self):
self.application = 'Application created by App1'
self.link = 'Link created by App1'
super(App1, self).access()
print('There is something App1 must do')
class App2(App):
def __init__(self, testName):
# ...
super(App2, self).__init__(testName)
def access(self):
self.application = 'Application created by App2'
self.link = 'Link created by App2'
super(App2, self).access()
print('There is something App2 must do')
and the test result:
>>>
>>> app = App('Baseclass')
>>> app.access()
There is something BaseClass must do
The application is None
The link is None
>>> app1 = App1('App1 test')
>>> app1.access()
There is something BaseClass must do
The application is Application created by App1
The link is Link created by App1
There is something App1 must do
>>> app2 = App2('App2 text')
>>> app2.access()
There is something BaseClass must do
The application is Application created by App2
The link is Link created by App2
There is something App2 must do
>>>
Adding a combine function we can combine two functions and execute them one after other as bellow
def combine(*fun):
def new(*s):
for i in fun:
i(*s)
return new
class base():
def x(self,i):
print 'i',i
class derived(base):
def x(self,i):
print 'i*i',i*i
x=combine(base.x,x)
new_obj=derived():
new_obj.x(3)
Output Bellow
i 3
i*i 9
it need not be single level hierarchy it can have any number of levels or nested

Categories

Resources