Python inheritance - python

My base code looks like this:
class C1(object):
def f(self):
return 2*self.g()
def g(self):
return 2
class C2(C1):
def f(self):
return 3*self.g()
class C3(C1):
def g(self):
return 5
class C4(C3):
def f(self):
return 7*self.g()
obj1 = C1()
obj2 = C2()
obj3 = C3()
obj4 = C4()
Now my question is the following: I need to write three assignment statements that do the following:
assign the calling list for obj2.f() to the variable obj2_calls
assign the calling list for obj3.f() to the variable obj3_calls
assign the calling list for obj4.f() to the variable obj4_calls
Calling list being for example, when obj1.f() is called, the f method of C1 is called which calls the g method of C1. This could be represented as a calling list of the form ['C1.f', 'C1.g']
I don't quite know the proper way to write the assignment statements and I desperately want to help out my friend with her stuff.
If you could just show me how to properly right out the first assignment statement, I'm sure I could figure out the rest.

The key insight is that if a method is not defined for a class, it will default to use the method of a class that the class inherits.
Thus, ask yourself what 'obj2.f()' will do.
What class is obj2? It is C2.
Is there an f method defined for class C2? Yes, there is. So the method C2.f is called.
The C2.f method calls self.g, which means it looks for C2.g. Is there a 'C2.g' method? No, so we have to go to the class that C2 inherits from. The line class C2(C1) tells us that it is inherited from the class C1, so it will call the method C1.g.
Those are the steps to get the first calling list; the rest are up to you.

Related

Value Changed in Class B calls function in Class A to update object of class A

I want to call a function from a class A inside another class B. However, it should be called for an object of A. I mean if I have something like this:
class A:
def __init__(self, ....):
self.valuechanged=False
# do something
objectfromb=B()
self.somearray.append(objectfromb)
def updateevent(self):
self.valuechanged=True
# do some things if update event triggered
class B:
def __init__(self,...):
self.somevalue=0
self.someothervalue=1
# do something
def updatesomevalue(self,somenewvalue):
self.somevalue=somenewvalue
# !!! HERE SHOULD BE A CALL TO CLASS A FUNCTION updateevent
And in my code I use the classes like this:
a=A()
Then i would have a list somearray in a (a.somearray) which contains an object of B. So if I want to update this object B with:
a.somearray[0].updatesomevalue(10)
Then there should not only be a new value for a.somearray[0].somevalue but also the function update event of class A should trigger changing a. How can I do that?
There are two ways I can think of to achieve this without invoking any special magic.
The first is to have objects of type B know what object A they belong to so that they can call updateevent on it. This isn't a way I'm generally a fan of as there's extra admin work to do when moving instances of B between instances of A and such. If that's not a concern then it may be the best way. You'd do that something like this (with a method on A to create a B and set the correct parent for convenience):
class A:
valuechanged=False
somearray=[]
def add_b(self):
b = B(self)
somearray.append(b)
return b
def updateevent(self):
self.valuechanged=True
class B:
somevalue=0
someothervalue=1
def __init__(self, parent):
self.parent = parent
def updatesomevalue(self,somenewvalue):
self.somevalue=somenewvalue
self.parent.updateevent()
The second is to provide a method on A that does both tasks. This is only suitable if 1) you know A will always contains instances of B and only B and 2) B's interface is relatively small (to avoid providing lots of methods of this type on A). You would implement this as something like:
class A:
valuechanged=False
somearray=[]
def updatesomevalue(self, index, new_value):
somearray[index].updatesomevalue(new_value)
self.updateevent()
def updateevent(self):
self.valuechanged=True
class B:
somevalue=0
someothervalue=1
def updatesomevalue(self,somenewvalue):
self.somevalue=somenewvalue
Something I haven't addressed is that somearray, somevalue, etc are all being created as class attributes in your example (i.e. they will be shared among all instances, instead of each instance having its own ones). This is likely not what you wanted.

Is there any difference between these two cases: passing class name vs object

I'm new to Python and not sure about many idioms. I have found code where a function gets a class name as argument. Is there any reason to do it? I've simplified the code to this:
class A:
def __init__(self):
print ("A")
def foo_1(a):
inst = a()
return inst
def foo_2(a):
inst = a
return inst
if __name__ == "__main__":
i1 = foo_1(A)
i2 = foo_2(A())
Is there any difference between implementing it like foo_1 ( the way it is implemented now), and the foo_2 (The way I would consider to be more intuitive)
Thanks!
These do slightly different things.
foo_1 gets passed a class (itself an object), not just its name. It then instantiates an instance of that class, which it returns.
foo_2 gets passed an instance (actually in this simplified example any object) and just returns it.
In this example this will mean that i1 and i2 seem to be the same. But they are actually different objects and will, for example, not compare equal.
You would use something like foo_1 as a factory of instances of some class or to modify the class object before creating an instance. And you would use foo_2 if you want to do something with an instance. In particular this means that if you call the function multiple times, you can pass the same instance every time, whereas foo_1 will always generate a new instance.
So, it depends on what you want to do.
What you have as foo_2 can be simplified as just using the object, which is what you should do.
Both options work, but foo_1 is an odd way of doing things unless you don't have immediate access to the class you want to instantiate.
i = foo_2(A())
is the same as
i = A()
Say you had a new class B, which had to be instantiated with some paramters. It's more complicated to call that from foo_1.
class B:
def __init__(self, c):
self.c = c
You can do:
i = B()
j = foo_2(B())
k = foo_1(B) => TypeError: __init__() missing 1 required positional argument: 'c'
You could modify foo_1 to take paramters though, and that could be useful, if you didn't know what class you might be instantiating (if you get the class via user input).
# function that takes 1 parameter followed by any number of paramters
def foo_3(klass, *params):
inst = klass(*params) # pass the any number of paramters onto the instantiation
return inst

How to make one member of class to be both field and method?

I have one class A which extends B, and B has one method count(). Now I want to allow user call both A.count and A.count(). A.count means count is one field of A while A.count() means it is method derived from B.
This is impossible in Python, and here's why:
You can always assign a method (or really any function) to a variable and call it later.
hello = some_function
hello()
is semantically identical to
some_function()
So what would happen if you had an object of your class A called x:
x = A()
foo = x.count
foo()
The only way you could do this is by storing a special object in x.count that is callable and also turns into e.g. an integer when used in that way, but that is horrible and doesn't actually work according to specification.
As i said, it's not exactly impossible, as told by other answers. Lets see a didactic example:
class A(object):
class COUNT(object):
__val = 12345
def __call__(self, *args, **kwargs):
return self.__val
def __getattr__(self, item):
return self.__val
def __str__(self):
return str(self.__val)
count = COUNT()
if __name__ == '__main__':
your_inst = A()
print(your_inst.count)
# outputs: 12345
print(your_inst.count())
# outputs: 12345
As you may notice, you need to implement a series of things to accomplish that kind of behaviour. First, your class will need to implement the attribute count not as the value type that you intent, but as an instance of another class, which will have to implement, among other things (to make that class behave, by duck typing, as the type you intent) the __call__ method, that should return the same as you A class __getattr__, that way, the public attribute count will answer as a callable (your_inst.count()) or, as you call, a field (your_inst.count), the same way.
By the way, i don't know if the following is clear to you or not, but it may help you understand why it isn't as trivial as one may think it is to make count and count() behave the same way:
class A(object):
def count(self):
return 123
if __name__ == '__main__':
a = A()
print(type(a.count))
# outputs: <class 'method'>
print(type(a.count()))
# outputs: <class 'int'>
. invokes the a class __getattr__ to get the item count. a.count will return the referente to that function (python's function are first class objects), the second one, will do the same, but the parentheses will invoke the __call__ method from a.count.

How can I ensure that a method is called (once, if present) for every class in a hierarchy?

I have a class hierarchy in which subclasses may optionally define a method with a given name, say do_it, and want to write a function, say do_all, in the base class that ensures that each such method will be executed, in order of class hierarchy. The closest I can get to achieving this is something like:
class A(object):
def do_it(self):
print 'Do A'
def do_all(self):
# Better, from comments and answers: vars(type(self))
for c in reversed(self.__class__.__mro__[:-1]):
c.do_it(self)
class B(A):
def do_it(self):
print 'Do B'
class C(B):
def no_do_it(self):
pass
class D(C):
def do_it(self):
print 'Do D'
This almost works as intended, for example B().do_all() gives
Do A
Do B
But it results in a duplicate call to Bs do_it for all classes descended from it. For example D().do_all() gives
Do A
Do B
Do B
Do D
How can I avoid the duplicate call to a parent's do_it from classes that do not implement it? Is this even the right way to achieve what I'm trying to do?
You can check whether the function has already been seen:
def do_all(self):
seen = set()
for c in reversed(self.__class__.__mro__[:-1]):
if c.do_it not in seen:
seen.add(c.do_it)
c.do_it(self)
Note that in Python 2 you'll need to extract the function from the unbound method, as c.do_it.__func__ (or use six.get_unbound_function).
An alternative is to examine __dict__:
def do_all(self):
for c in reversed(self.__class__.__mro__[:-1]):
if 'do_it' in c.__dict__:
c.do_it(self)

classmethod as constructor and inheritance

The problem is quite simple. If a class B inherit a class A and wants to override a ´classmethod´ that is used as a constructor (I guess you call that a "factory method"). The problem is that B.classmethod will want to reuse A.classmethod, but then it will have to create an instance of the class A, while it subclasses the class A - since, as a classmethod, it has no self. And then, it doesn't seem the right way to design that.
I did the example trivial, I do more complicate stuff by reading numpy arrays, etc. But I guess there is no loss of information here.
class A:
def __init__(self, a):
self.el1 = a
#classmethod
def from_csv(cls, csv_file):
a = read_csv(csv_file)
return cls(a)
#classmethod
def from_hdf5 ...
class B(A):
def __init__(self, a, b)
A.(self, a)
self.el2 = b
#classmethod
def from_csv(cls, csv_file):
A_ = A.from_csv(csv_file) #instance of A created in B(A)
b = [a_*2 for a_ in A.el]
return cls(A.el, b)
Is there a pythonic way to deal with that?
After doing some different trials. My conclusion is that you should override a classmethod without reusing the code inside. So the best way I found, for my particular problem, is to make the classmethod as simply as possible and put the code I want to reuse in another method, static in my case, since the classmethod is a constructor.
One easy solution would be to have class B's __init__ method have a default value for its b parameter. This would let the cls(a) call made by A.from_csv work when it is inherited. If the default is used, the __init__ method could calculate a value to store from a (as you do in B.from_csv now).
class B(A):
def __init__(self, a, b=None):
super().__init__(a) # use super(B, self).__init__(a) if you're in Python 2
self.el2 = b if b is not None else [i*2 for i in a]
# don't override from_csv, B.from_csv will already return a B instance!

Categories

Resources