How does python deal with this inheritance issue? - python

class A:
def __init__(self):
print('I am A')
class B(A):
def b(self):
print('I am B.b')
class C(A):
def c(self):
x.b()
if __name__ == '__main__':
x = B()
y = C()
y.c()
How does it work when it comes to 'y.c() '?
In C.c(), how can the instance x be called without instantiation before?
Thanks a lot, if someone can help.

It cannot. In your case, it just happens that when you call x.b() there is a global variable that happens to be named x have type B. It has been initialized at the previous line, with x = B().
This code depends on external variables, and will fail in general. If you want to call the objects own method, use self.b() instead.

Related

Redefine legacy function in python with calls to old

Let say I have function a() which is called in many places and I am not able to trace it or change it.
I need some replacement of this function, with calls to it and also do some additional staff.
The old code has many calls a() function so I need some redefinition a=b.
However, example below cause infinite recursion
def a():
return "hello" #do not edit!
def b():
prefix = a() # get something from a
return prefix+" world"
a=b
#...somewhere
a()
Is there any possibility to do this?
You do it with monkey-patching, by using a different variable to hold the old definition.
original_a = a
def b():
prefix = original_a()
return prefix + " world"
a = b
use inheritance
class a:
value=9
def __init__(self):
print("I'm a parent")
def fnc():
print("parent fnc")
class b(a):
def __init__(self):
#super().__init__()
print("I'm a child!")
#classmethod
def fnc(cls):
super().fnc()
print("child fnc")
#classmethod
def getValue(cls):
return super().value
output:
I'm a child!
parent fnc
child fnc
9
​
make a the Parent and b the SuperChild
b can now access the methods and attributes of a
your wrapping b with the functionality of a

In python, Two functions calls some same workflows, how to distinguish the origin function at bottom?

I have two python functions A() and B(), they both call f1(), and f1() calls f2()... In f4(), I want to get the origin function name(A or B), Is there any smart way? I thought I can add a param for each functions, but it makes my codes quite ugly. These functions are in different files and some start in new Thread, I also read some doc about context but I'm not sure if it works here.
def A():
f1()
def B():
f1()
def f1():
f2()
def f2():
f3()
def f3():
f4()
def f4():
if call_from_A:
print(123)
else:
print(456)
We can use inspect from Python to identify this.
import inspect
def f4():
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 2)
call_from_A = calframe[4].function == "A"
if call_from_A:
print(123)
else:
print(456)
Note that calframe[4] in the line call_from_A = calframe[4].function == "A" has to be modified appropriately if the nesting of your functions is changed. In other words, [4] is hard-coded to "go back in the frame stack 4 times" (i.e. f4 <- f3 <- f2 <- f1 <-source).
You can use the traceback module to do this. It will give you the whole call stack, which you can parse to get the information you need.
Example:
import traceback
def A():
f1()
def B():
f1()
def f1():
f2()
def f2():
f3()
def f3():
f4()
def f4():
tb = traceback.extract_stack()
call_chain = [f.name for f in tb]
if 'A' in call_chain:
print('Called from A')
elif 'B' in call_chain:
print('Called from B')
def main():
A()
B()
if __name__ == '__main__':
main()
Your question boils down to "what kind of workflow am I in?" and the traditional way to solve such problems is using object-oriented patterns. Using object-oriented design properly often results in the elimination of conditionals.
Put the methods on a class. You can either pass the "kind of thing it is" at instantiation, or you can model that using inheritance, depending on your needs.
Using a single class and passing the "kind" at instantiation:
class Workflow:
def __init__(self, kind):
self.kind = kind
def f1(self):
self.f2()
def f2(self):
self.f3()
def f3(self):
self.f4()
def f4(self):
print(self.kind)
# now use it
a = Workflow(123)
b = Workflow(456)
a.f1() # prints 123
b.f1() # prints 456
Notice there's no if involved. There doesn't need to be. Something like this would be missing the point:
def f4(self):
if self.kind == "A":
print(123)
else if self.kind == "B":
print(456)
The distinction is made earlier, when the object is instantiated. Thereafter each instance has its own behavior which is not predicated on anything.
If the two workflows have more complex behavior (e.g. f4() actually does some significant work) then you probably want inheritance. Define a base class, then subclass it for each kind of thing you want to be able to create.
class WorkflowBase:
def f1(self):
self.f2()
def f2(self):
self.f3()
def f3(self):
self.f4()
def f4(self):
return
class WorkflowA(WorkflowBase):
def f4(self):
print(123)
class WorkflowB(WorkflowBase):
def f4(self):
print(456)
# using them
a = WorkflowA()
b = WorkflowB()
a.f1() # prints 123
b.f1() # prints 456
Here the behavior is baked into either WorkflowA or WorkflowB and the fact that you have an instance of one class or the other is what activates the differing result.
It also does not require you to inspect the call stack, which is quite a complicated condition. That is fun to do, I understand the allure well, but it is slow-ish, and it makes your code difficult for others to understand.

Dynamically adopt the methods of an instance of another class

I have a case, where I have an instance of a class in python which holds instances of other classes. For my use case, I would like a way to use the methods of the "inner" classes from the outer class without referencing the attribute holding the inner class.
I have made a simplistic example here:
class A:
def __init__(self):
pass
def say_hi(self):
print("Hi")
def say_goodbye(self):
print("Goodbye")
class C:
def __init__(self, other_instance):
self.other_instance= other_instance
def say_good_night(self):
print("Good night")
my_a = A()
my_c = C(other_instance=my_a)
# How to make this possible:
my_c.say_hi()
# Instead of
my_c.other_instance.say_hi()
Class inheritance is not possible, as the object passed to C may be an instance of a range of classes. Is this possible in Python?
I think this is the simplest solution although it is possible with metaprogramming.
class A:
def __init__(self):
pass
def say_hi(self):
print("Hi")
def say_goodbye(self):
print("Goodbye")
class C:
def __init__(self, other_class):
self.other_class = other_class
C._add_methods(other_class)
def say_good_night(self):
print("Good night")
#classmethod
def _add_methods(cls, obj):
type_ = type(obj)
for k, v in type_.__dict__.items():
if not k.startswith('__'):
setattr(cls, k, v)
my_a = A()
my_c = C(other_class=my_a)
my_c.say_hi()
output :
Hi
First we get the type of passed instance, then we iterate through it's attribute (because methods are attributes of the class not the instance).
If self.other_class is only needed for this purpose, you can omit it as well.
So, because you have done:
my_a = A() and my_c = C(other_class=my_a).
my_c.other_class is the same as my_a asthey point to the same location in memory.
Therefore, as you can do my_a.say_hi() you could also do my_c.other_class.say_hi().
Also, just a note, as you are calling A() before you store it into other_classes, I would probably rename the variable other_classes to class_instances.
Personally, I think that would make more sense, as each of those classes would have already been instantiated.

Python, can't assert if method called from another class

I have the following code in Python:
class A():
def doSomething(self, bClass):
print(bClass.theThing)
class B():
def __init__(self, theThing):
self.theThing = theThing
def foo():
a = A()
b = B("that thing")
a.doSomething(b)
I have those classes and the function foo() stored in testing.py and I want to test that the A's method was called with:
import testing, unittest
from unittest.mock import patch
class TheTestClass(unittest.TestCase):
def test(self):
with patch('testing.A.doSomething') as do:
testing.foo()
do.assert_any_call()
But I always get 'doSomething() call not found'. I would be happier if I could understand why but at this point anything is welcome
After many hours I started figuring this out.
Like jwjhdev said assert_called_with() expects something and in my case a class but so does assert_any_call(). For some reason I was thinking assert_any_call() would just work but what I was thinking was assert_called() which just works without arguments. In the end I figured it out by adding a return b to the foo() function and:
def foo():
a = A()
b = B("that thing")
a.doSomething(b)
return b
class TheTestClass(unittest.TestCase):
def test(self):
with patch('testing.A.doSomething') as do:
b = testing.foo()
do.assert_any_call(b)

Calling an overridden method, superclass an calls overridden method

This code throws an exception, AttributeError, "wtf!", because A.foo() is calling B.foo1(), shouldn't it call A.foo1()? How can I force it to call A.foo1() (and any method call inside A.foo() should call A.*)
class A(object):
def foo(self):
print self.foo1()
def foo1(self):
return "foo"
class B(A):
def foo1(self):
raise AttributeError, "wtf!"
def foo(self):
raise AttributeError, "wtf!"
def foo2(self):
super(B, self).foo()
myB = B()
myB.foo2()
In class A instead of calling self methods you need to call A methods and pass in self manually.
This is not the normal way of doing things -- you should have a really good reason for doing it like this.
class A(object):
def foo(self):
print A.foo1(self)
def foo1(self):
return "foo"
class B(A):
def foo1(self):
raise AttributeError, "wtf!"
def foo(self):
raise AttributeError, "wtf!"
def foo2(self):
super(B, self).foo()
myB = B()
myB.foo2()
In the code:
def foo2(self):
super(B, self).foo()
self is an instance of B.
When a method derived from A is called by an instance of B it will start looking in the namespace from B, and only if the method is not found (e.g. is not overridden by B) the implementation from A is used, but always with self referring to B. At no point self is an instance of A.
It is working as intended, as 100% of world programming languages work. Subclass overrides ALL methods of parent class.
However if you really really want to call the A.foo1() you might be able to do it like this (I cannot guarantee). And in any case you must not do this as this is against all principles of good programming.
class A(object):
def foo(self):
A.foo1(self)
One can see what Python is doing here, but the manner of overriding is a bit extreme. Take the case when class A defines 100 attributes and class B inherits these and add 1 more attribute. We want to be able to have the __init__() for B call the __init__() for A and let B's code define only its single attribute. Similarly, if we define a reset() method in A to set all attributes to zero, then the corresponding reset() method for B should be able just to call the reset() method for A and then zero out the single B attribute instead of having to duplicate all of A's code. Python is making difficult what is supposed to be a major advantage of object-oriented programming; that is, the reuse of code. The best option here is avoid overriding of methods that we really want to reuse. If you want to get a sense of the complications with Python here, try this code:
class X(object):
def __init__ ( self ):
print "X"
self.x = 'x'
self.reset()
print "back to X"
def reset ( self ):
print "reset X"
self.xx = 'xx'
class Y(X):
def __init__ ( self ):
print "Y"
super(Y,self).__init__()
self.y = 'y'
self.reset()
print "back to Y"
def reset ( self ):
print "reset Y"
super(Y,self).reset()
print "back to reset Y"
self.yy = 'yy'
aY = Y()
(To make this work properly, remove the self.reset() call in __init__() for class Y.)

Categories

Resources