Cast Base Class to Derived Class Python (Or More Pythonic Way of Extending Classes)

Cast base class to derived class python (or more pythonic way of extending classes)

If you are just adding behavior, and not depending on additional instance values, you can assign to the object's __class__:

from math import pi

class Circle(object):
def __init__(self, radius):
self.radius = radius

def area(self):
return pi * self.radius**2

class CirclePlus(Circle):
def diameter(self):
return self.radius*2

def circumference(self):
return self.radius*2*pi

c = Circle(10)
print c.radius
print c.area()
print repr(c)

c.__class__ = CirclePlus
print c.diameter()
print c.circumference()
print repr(c)

Prints:

10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>

This is as close to a "cast" as you can get in Python, and like casting in C, it is not to be done without giving the matter some thought. I've posted a fairly limited example, but if you can stay within the constraints (just add behavior, no new instance vars), then this might help address your problem.

What's the pythonic way to add behavior to objects returned by an external module?

You can rewrite this in a more dynamic manner:

from other_module import Foo, FooTypeA, FooTypeB

bases = [Foo, FooTypeA, FooTypeB]

class MyMixin(object):
pass

def factory(bases, mixins, name='MyClass'):
return type(name, bases + mixins, {})

new_classes = [factory((c,), (MyMixin,)) for c in bases]

Copying values from the object of base class in Python

That is not how to do inheritance. You're passing an instance of the base class to the __init__ of the derived class, which is completely unrelated to inheriting its attributes. Instead, do:

class Base():

def __init__(self):
self.a = 10

class Derived(Base):

def __init__(self):
super(Derived, self).__init__() # calls Base.__init__
self.b = 'abc'

def main():
base = Base()
derived = Derived()

How to cast object in Python

There is no casting as the other answers already explained. You can make subclasses or make modified new types with the extra functionality using decorators.

Here's a complete example (credit to How to make a chain of function decorators?). You do not need to modify your original classes. In my example the original class is called Working.

# decorator for logging
def logging(func):
def wrapper(*args, **kwargs):
print func.__name__, args, kwargs
res = func(*args, **kwargs)
return res
return wrapper

# this is some example class you do not want to/can not modify
class Working:
def Do(c):
print("I am working")
def pr(c,printit): # other example method
print(printit)
def bla(c): # other example method
c.pr("saybla")

# this is how to make a new class with some methods logged:
class MutantWorking(Working):
pr=logging(Working.pr)
bla=logging(Working.bla)
Do=logging(Working.Do)

h=MutantWorking()
h.bla()
h.pr("Working")
h.Do()

this will print

h.bla()
bla (<__main__.MutantWorking instance at 0xb776b78c>,) {}
pr (<__main__.MutantWorking instance at 0xb776b78c>, 'saybla') {}
saybla

pr (<__main__.MutantWorking instance at 0xb776b78c>, 'Working') {}
Working

Do (<__main__.MutantWorking instance at 0xb776b78c>,) {}
I am working

In addition, I would like to understand why you can not modify a class. Did you try? Because, as an alternative to making a subclass, if you feel dynamic you can almost always modify an old class in place:

Working.Do=logging(Working.Do)
ReturnStatement.Act=logging(ReturnStatement.Act)

Update: Apply logging to all methods of a class

As you now specifically asked for this. You can loop over all members and apply logging to them all. But you need to define a rule for what kind of members to modify. The example below excludes any method with __ in its name .

import types
def hasmethod(obj, name):
return hasattr(obj, name) and type(getattr(obj, name)) == types.MethodType

def loggify(theclass):
for x in filter(lambda x:"__" not in x, dir(theclass)):
if hasmethod(theclass,x):
print(x)
setattr(theclass,x,logging(getattr(theclass,x)))
return theclass

With this all you have to do to make a new logged version of a class is:

@loggify
class loggedWorker(Working): pass

Or modify an existing class in place:

loggify(Working)

Universally create Derived class from Base in python

You can use inheritance:

class FileProxyGetter(ProxyGetter):
...
def MakeProxy(self, *args, **kwargs):
return Proxy.fromstring(*args, **kwargs)
def Get(self):
...
proxies.append(self.MakeProxy(l[:-1]))
...
...
class FileSecureProxyGetter(FileProxyGetter):
def MakeProxy(self, *args, **kwargs):
return SecureProxy.fromstring(*args, **kwargs)

but it's probably more useful in this case to use composition.

class FileProxyGetter(ProxyGetter):
def __init__(self, proxyclass, fname = "d:\\proxies.txt"):
self.proxyClass = proxyclass
self.fileName = fname
...
def Get(self):
...
proxies.append(self.proxyclass.fromstring(l[:-1]))
...
...

# use this as such
FileProxyGetter(Proxy, "proxies.txt")
FileProxyGetter(SecureProxy, "secure_proxies.txt")

EDIT: A dirty trick in python to switch the type of an object:

>>> class A(object):
... def foo(self):
... print 'hello A'
...
>>> class B(object):
... def foo(self):
... print 'hello B'
...
>>> a = A()
>>> a.foo()
hello A
>>> a.__class__
<class '__main__.A'>
>>> a.__class__ = B
>>> a.foo()
hello B

Another dirty trick for two objects of different types to share the same state:

>>> class B(object):
... def rename(self, name):
... self.name = name
...
>>> class A(object):
... def say(self):
... print 'Hello', self.name
...
>>> a, b = A(), B()
>>> a.__dict__ = b.__dict__
>>> b.rename('john')
>>> a.say()
Hello john
>>> a.rename('mary')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'rename'
>>> b.say()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'B' object has no attribute 'say'

However, these tricks, while possible in Python, I would not call them pythonic nor a good OO design.

Another possibility in Python 3.x and up, which had removed "unbound method" in place of using regular function:

>>> class A(object):
... def say(self):
... print('Hello', self.name)
...
>>> class B(object):
... def rename(self, name):
... self.name = name + name
...
>>> a = A()
>>> B.rename(a, 'josh')
>>> a.say()
Hello joshjosh


Related Topics



Leave a reply



Submit