Redefine legacy function in python with calls to old - python

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

Related

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.

pyhon3 dynamically create methods from parent class

Let's say I have a class defined like this:
classA():
def do_one():
print("one")
def do_two():
print("two")
def some_other():
print("other")
I want to create a derived class and automatically define every method from the parent class that starts with do
I tried this:
class B(A):
pass
for m in dir(A):
if m[0:3] == "do_":
def dm(self):
print("somebething before")
getattr(super(),m)()
dm.__name__ = m
setattr(B,m,dm)
But i'm getting this error: RuntimeError: super(): __class__ cell not found
Also is there a non-so-hacky/pytonic way of achieving this?
You cannot use super() without arguments outside a class definition. In order to perform lookups super() needs two arguments. Additionally, the definition of dm needs to be wrapped in another function. Otherwise, when it is executed the value of m will be taken from the global scope.
Putting this together, the following should work:
class A():
def do_one(self):
print("one")
def do_two(self):
print("two")
def some_other(self):
print("other")
class B(A):
pass
for m in dir(B):
if m[0:3] == "do_":
def redef(m):
def dm(self):
print("something before")
getattr(super(B, self), m)()
return dm
setattr(B, m, redef(m))
If you run:
x = B()
x.do_one()
x.do_two()
x.some_other()
It will give:
something before
one
something before
two
other

Can I control the function to call written in the __init__ function?

I want to know if there is a way to control the function call written in the __init__ function of a class ? on an existing framework they have written two function calls inside the __init__ function but I want to call only one, say the first one only. Can I achieve this using Python?
def funA():
print('calling functionA()')
def funB():
print('calling functionB()')
class A():
def __init__(self):
a = funA()
b = funB()
c = A()
Assume I want to call only funA and not B. Can I do that?
I tried referring to the other thead which can skip the __init__ part using helper class, but that does not seem to help me.
Please advise.
You can define a class, which uses A as parent class and overrides its __init__() method as follows:
def funA():
print('calling functionA()')
def funB():
print('calling functionB()')
class A():
def __init__(self):
a = funA()
b = funB()
def othermet(self):
print('calling method of parent class')
class M(A):
def __init__(self):
a = funA()
c = M()
c.othermet()
Out:
calling functionA()
calling method of parent class

Can Python determine the class of an object accessing a method

Is there anyway to do something like this:
class A:
def foo(self):
if isinstance(caller, B):
print "B can't call methods in A"
else:
print "Foobar"
class B:
def foo(self, ref): ref.foo()
class C:
def foo(self, ref): ref.foo()
a = A();
B().foo(a) # Outputs "B can't call methods in A"
C().foo(a) # Outputs "Foobar"
Where caller in A uses some form of introspection to determine the class of the calling method's object?
EDIT:
In the end, I put this together based on some of the suggestions:
import inspect
...
def check_caller(self, klass):
frame = inspect.currentframe()
current = lambda : frame.f_locals.get('self')
while not current() is None:
if isinstance(current(), klass): return True
frame = frame.f_back
return False
It's not perfect for all the reasons supplied, but thanks for the responses: they were a big help.
Assuming the caller is a method, then yes you can, by looking in the previous frame, and picking out self from the locals.
class Reciever:
def themethod(self):
frame = sys._getframe(1)
arguments = frame.f_code.co_argcount
if arguments == 0:
print "Not called from a method"
return
caller_calls_self = frame.f_code.co_varnames[0]
thecaller = frame.f_locals[caller_calls_self]
print "Called from a", thecaller.__class__.__name__, "instance"
Üglŷ as heck, but it works. Now why you would want to do this is another question altogether, I suspect that there is a better way. The whole concept of A isn't allowed to call B is likely to be a mistake.
The caller is always an instance of A. The fact that you're calling it inside a B method doesn't change that. In other words: Insiode B.foo, ref is an instance of A, so calling ref.foo() is a call on A, B is not involved on that call (it could happen top-level).
The only sane way is to pass a reference to self so A can check if it is B or not.
class A(object):
def foo(self, caller=None):
if isinstance(caller, B):
print "B can't call methods in A"
else:
print "Foobar"
class B(object):
def foo(self, ref): ref.foo(self)
class C(object):
def foo(self, ref): ref.foo(self)
a = A();
B().foo(a) # Outputs "B can't call methods in A"
C().foo(a) # Outputs "Foobar"
a.foo() # Outputs "Foobar"
Something like this may meet your needs better:
class A(object):
def foo(self):
# do stuff
class B(A):
def foo(self):
raise NotImplementedError
class C(A):
pass
...but it's difficult to say without knowing exactly what you're trying to do.

Categories

Resources