Conflicts when find and "replace" a method dynamically - python-3.x

I have a class consisting of a "list" of static methods, A. I want to change its behavior with a class-decorator, Meta, which acts on a specific static method, in this example content, by performing the method m.
My original attempt, CASE=2, didn't work as expected, so I started I case study. I introduced a new class B, which has slightly different implementation of an other method, info but raised a funny error, and a new class C just without the method, info.
case 2: the greedy case
d[i] = classmethod(lambda cls, *args: mcs.m( getattr(target_cls, i)(*args)) ) it doesn't work properly, maybe too many nested dynamic expressions?
case 1: it essentially case 2 but the expression is divided in two lines, and it works
o = getattr(target_cls, i)
d[i] = classmethod(lambda cls, *args: mcs.m(o(*args)))
Here the code
class Meta:
def __new__(mcs, target_cls):
if CASE == 1:
print('case 1')
d = {}
for i in dir(target_cls):
if i == 'content':
o = getattr(target_cls, i)
d[i] = classmethod(lambda cls, *args: mcs.m(o(*args)))
if CASE == 2:
print('case 2')
d = {}
for i in dir(target_cls):
if i == 'content':
d[i] = classmethod(lambda cls, *args: mcs.m( getattr(target_cls, i)(*args)) )
return type('AAA', (target_cls,), d)
#classmethod
def m(mcs, p):
return '--> ', p
class A:
#staticmethod
def content(response):
return 'static_method', response
#staticmethod
def info(response):
return response
class B:
#staticmethod
def content(response):
return 'static_method', response
#staticmethod
def info(response):
response.sort()
return response
class C:
#staticmethod
def content(response):
return 'static_method', response
# call the "content" class-method of each class for all different cases
for cls in (A, B, C):
print(cls.__name__)
for case in range(1,3):
CASE = case
R = Meta(cls)
try:
print(R.content('ppp'))
except Exception as e: print(e)
print()
Output
A
case 1
('--> ', ('static_method', 'ppp'))
case 2
('--> ', 'ppp') # no decoration
B
case 1
('--> ', ('static_method', 'ppp'))
case 2
'str' object has no attribute 'sort' # <- complained about the other method
C # <- this is ok BUT I removed the other method!
case 1
('--> ', ('static_method', 'ppp'))
case 2
('--> ', ('static_method', 'ppp')) # <- here the decoration took place
The question is why case 2 doesn't work, if it is a limitation of the language then of what kind?
Extra question: how to explain the error of class B case 2

I guess that the issue is caused by the loop and the origin is the fact that each statement has not its own scope (in the loop). By passing i as a key parameter of the lambda fixed the problem.
class Meta:
def __new__(mcs, target_cls):
d = {}
for i in dir(target_cls):
if i == 'content':
d[i] = classmethod(lambda cls, *args, m_name=i: mcs.m( getattr(target_cls, m_name)(*args)) )
return type('AAA', (target_cls,), d)
#classmethod
def m(mcs, p):
return '--> ', p
class A:
#staticmethod
def content(response):
return 'static_method', response
#staticmethod
def info(response):
return response
print(A.content)
print(Meta(A).content)
print(Meta(A).content('a'))
print(Meta(A).info)
Output
<function A.content at 0x7f04500740d0> # original static method
<bound method Meta.__new__.<locals>.<lambda> of <class '__main__.AAA'>> # class method
('--> ', ('static_method', 'a'))
<function A.info at 0x7f0450074040>

Related

AttributeError: 'obj_2' object has no attribute 'var_1' - getattr -

I need to get data to second class from first class.
I Used - getattr - function to do it.
class obj_1:
def __init__(self):
self.var_1 = 'Hello'
self.var_2 = 'World'
def get_id(self):
i = self.var_1
j = self.var_2
return i, j
def vw(self):
print('Hello..')
class obj_2:
def __init__(self):
pass
def r_data(self):
print('called r_data')
x, y = getattr(obj_1, "get_id")(self)
print('x > ', x)
print('y > ', y)
def rd(self):
getattr(obj_1, 'vw')(self)
if __name__ == '__main__':
ob = obj_2()
ob.r_data()
It given error as - AttributeError: 'obj_2' object has no attribute 'var_1'
I think you are getting this error since the function get_id uses attributes of the class, i.e self.var_1 self.var_2
and these attributes are never initialized since the __init__ function was never called (and since you cant have attributes without an actual object )
so to fix your code I would create an object of "obj_1" and call the function
"get_id" with that object
class obj_1:
def __init__(self):
self.var_1 = 'Hello'
self.var_2 = 'World'
def get_id(self):
i = self.var_1
j = self.var_2
return i, j
def vw(self):
print('Hello..')
class obj_2:
def __init__(self):
self.o1 = obj_1()
def r_data(self):
print('called r_data')
x, y = self.o1.get_id()
print('x > ', x)
print('y > ', y)
def rd(self):
getattr(obj_1, 'vw')(self)
if __name__ == '__main__':
ob = obj_2()
ob.r_data()
hope i could help, please let me know in the comments if you didn't understand anything.
and if my comment helped you i would relly appreciate marking this comment as the answer :)

Operator overloading with python outside a class

class A:
def __init__(self,m1,m2):
self.m1 = m1
self.m2 = m2
def __add__(self, other):
''' add takes 2 objects basically objects on RHS and LHS of + say a + b '''
print("Inside add")
s3 = A(self.m1+other.m1,self.m2+other.m2)
return s3
def disp(self):
print('{} {}'.format(self.m1,self.m2))
def __str__(self):
return '{} {}'.format(self.m1,self.m2)
def __add__(self,other):
return self*other
a = 2+5
print(a)
Output seen is: 7
Whereas expected is 10 since I am trying to overwrite the implicit add function with multiply operation.
What happens here?
Does operator overloading works only with pytclass?
class A:
def __init__(self,m):
self.m=m
def __str__(self):
return str(self.m)
#multiply two objects
def __mul__(self,other):
t=self.m*other.m
return A(t)
#add two objects
def __add__(self,other):
t=self.m+other.m
return A(t)
obj1=A(2)
obj2=A(5)
#calling operator overloading of '*'
print(obj1*obj2)
#calling operator overloading of '+'
print(obj1+obj2)

python: generic function call from one instance to another

I am trying to write a method which calls another method in another instance. I do not know which combination of args and kwargs i get.
So i wrote the following method. But that looks not very elegant and not very pythonic to me.
Is there a better way to implement this?
def __call_generic_remote_function(self, rfunc_name, rargs=None, rkwargs=None):
try:
lfunc_name = getattr(self.inst_to_wrap, rfunc_name)
except AttributeError:
return f"Method {rfunc_name} is not existing!"
if rargs is None and rkwargs is None:
result = lfunc_name()
elif rargs is not None and rkwargs is not None:
result = lfunc_name(*rargs, **rkwargs)
elif rargs is None:
result = lfunc_name(**rkwargs)
else:
result = lfunc_name(*rargs)
return result
This question is probably off topic as it is more opinion based but I would write it something like the following. Can also be a standalone function.
def call_generic_remote_function(object_instance, func_name, *args, **kwargs):
try:
func_to_call = getattr(object_instance, func_name)
except AttributeError:
return f"Method {func_name} does not exist!"
return func_to_call(*args, **kwargs)
Tested Via:
class Foo:
def bar(self, a, b, k):
print(a, b, k)
def bar2(self):
print("Called")
f = Foo()
call_generic_remote_function(f, 'bar', 1, 2, k=3)
call_generic_remote_function(f, 'bar2')
print(call_generic_remote_function(f, 'bar3'))
Output:
1 2 3
Called
Method bar3 does not exist!

inheritance extending a from_json function in super but it makes an instance of the parent class

I have 2 classes:
class A:
name = 'test'
def __init__(self):
pass
#staticmethod
def from_json(json: dict) -> object:
obj = A()
obj.name = json["name"]
return obj
class B(A):
description = "desc"
def __init__(self):
super().__init__(self) # I was originally doing: A.__init__(self) but online said to use super.
#staticnmethod
def from_json(json: dict) -> object:
obj = A.from_json(json) # As seen above, A.from_json, this returns an instance of A.
obj.description = json["description"]
return obj
I know there isnt really any casting, but I want the returned class to be of type B, so it gains all the other new properties / methods.
How to i have B::from_json return type B? I was thinking there was a way to create something like:
b = B()
and then through some python magic pass all properties from A into B and then return b, but i wasnt sure if that is the right solution.
Here is now a functional test of the flaw:
x = A.from_json({'name': 'foo'})
z = B.from_json({ 'name': 'thor', 'description': 'god of thunder'})
type(x) == A # <class '__main__.A'>
type(z) == B # <class '__main__.A'>
You should use classmethod here, not staticmethod. Then you can remove all the hardcoded classes references
class A:
name = 'test'
def __init__(self):
pass
#classmethod
def from_json(cls, json: dict) -> object:
obj = cls()
obj.name = json["name"]
return obj
class B(A):
description = "desc"
def __init__(self):
super().__init__()
#classmethod
def from_json(cls, json: dict) -> object:
obj = super().from_json(json)
obj.description = json["description"]
return obj
print(type(B.from_json({'name': 'name', 'description': 'description'})))
Outputs
<class '__main__.B'>
And your tests:
x = A.from_json({'name': 'foo'})
z = B.from_json({ 'name': 'thor', 'description': 'god of thunder'})
print(type(x) == A)
print(type(z) == B)
Outputs
True
True
Using classmethod is actually the recommended way in the official Python docs to create alternative "constructors" (which is what from_json essentially is). Otherwise, you don't have any access to the correct class (as you found out).
This works because (quoted from the docs):
If a class method is called for a derived class, the derived class
object is passed as the implied first argument.

what does this <bound method Stack.pop of <__main__.Stack object at 0x03C74AB0>> mean?

class Stack:
def __init__(self):
self.items = []
def push(self,item):
self.items.append(item)
def pop(self):
return self.items.pop()
def isEmpty(self):
return (self.items ==[])
def __str__(self):
return str(self.items)
def postfixEval(postfixExpres):
operandStack = Stack()
tokenlist = postfixExpres.split()
for token in tokenlist:
if token in "0123456789":
operandStack.push(int(token))
else:
operand2 = operandStack.pop
operand1 = operandStack.pop
result = doMath(token,operand1,operand2)
operandStack.push(result)
return operandStack.pop
def doMath(op,op1,op2):
if op == "*":
return op1*op2
elif op == "/":
return op1/op2
elif op =="+":
return op1 + op2
elif op == "-":
return op1 - op2
print(postfixEval('12+'))
I believe it means you returned a function object of the pop method instead of a value at the end of postfixEval. Change operandStack.pop to operandStack.pop() (add the parentheses to apply the pop method instead of returning it).
Additionally, if I'm not mistaken, '12+'.split() will return '123' in python, since split uses whitespaces as the default delimiter. If you want to turn the string into a list of single characters, you can cast the string to a list instead list('12+') will return ['1', '2', '+'].
class stack():
def __init__(self):
self.items=[]
def push(self,item):
self.items.append(item)
def pop(self):`
self.items.pop()
def get_stack(self):
return self.items
def emp(self):
return self.items==[]
def peek(self):
if not self.emp():
return self.items[-1]
s=stack()
print("stack is empty: ",s.emp())
s.push('a')
s.push('b')
print(s.get_stack())
s.push(12)
print(s.get_stack())
s.pop()
print(s.get_stack())
s.push('c')
print(s.get_stack())
print('peek value is: ',s.peek())
print("stack is empty: ",s.emp())
In this case it means you're using the method object instead of calling it. In the doMath method you should use both variables like this: op1()*op2().

Resources