Python bind method to another instance - python

I need to merge two methods from different instances of different classes to a single instance of a class.
For example I define two classes:
class A:
def __init__(self):
self.name = "a"
def print_name(self):
print(self.name)
class B:
def __init__(self):
self.name = "b"
def print_name(self):
print(self.name)
and then I will try to make another object c and its print_name method must return the results of a.print_name() and b.print_name() so I tried the following :
a = A()
b = B()
c = A()
c.name = "c"
c.print_name_1 = a.print_name
c.print_name_2 = b.print_name
def final_print(self):
self.print_name_1()
self.print_name_2()
c.print_name = MethodType(final_print, c)
c.print_name()
Expected output:
c
c
but I get :
a
b
I tried to use types.MethodType as described here but it creates some kind of a method which takes two arguments : the first one will be the 'a' instance and the second one will be the 'c' instance.
Any help to do that properly?

I managed to succeed with this, thanks to this answer. I am using the __func__ attribute of a method. So here is the code :
c.name = "c"
c.print_name_1 = MethodType(lambda instance: copy.deepcopy(a.print_name).__func__(instance), c)
c.print_name_2 = MethodType(lambda instance: copy.deepcopy(b.print_name).__func__(instance), c)
def final_print(self):
self.print_name_1()
self.print_name_2()
c.print_name = MethodType(final_print, c)
c.print_name()

Related

Is there a way to pass a function call to an inner object?

Is there a way in python to pass a function call to an inner object, maybe through a decorator or wrapper? In the example below, class A holds a list of class B objects, and one of the class B objects is selected as the active object. I want class A to function as a passthrough, just identifying which of the class B objects that the call goes to. However, class A doesn't know what type of class it is going to hold beforehand, so I can't just add a set_var function to class A. It has to work for any generic function that class B has. It will only have one type of class in its objects list, so it could take class B as an input when it is instantiated and dynamically create functions, if that's a possibility. The client wouldn't know whether it's dealing with class A or class B. The code below is as far as I got.
class A:
def __init__(self):
self.objects = []
self.current_object = 0
def add_object(self, object):
self.objects.append(object)
class B:
def __init__(self):
self.var = 10
def set_var(self, new_var):
self.var = new_var
a_obj = A()
b_obj1 = B()
b_obj2 = B()
a_obj.add_object(b_obj1)
a_obj.add_object(b_obj2)
a_obj.set_var(100)
You could use the generic __getattr__ to delegate to the wrapped object.
class A:
def __init__(self):
self.objects = []
self.current_object = 0
def add_object(self, obj):
self.objects.append(obj)
self.current_object = obj
def __getattr__(self, name):
return getattr(self.current_object, name)
class B:
def __init__(self):
self.var = 10
def set_var(self, new_var):
self.var = new_var
a_obj = A()
b_obj1 = B()
b_obj2 = B()
a_obj.add_object(b_obj1)
a_obj.add_object(b_obj2)
a_obj.set_var(100)
print(b_obj2.var)
That will print "100".
You will still get an AttributeError if the wrapped object doesn't have the expected method.
It was interesting to look at this, it is intentionally rough but it does indeed allow you to call one the B instance's set_var methods.
The code below uses sets as a quick and dirty way to see the difference in callable methods, and if there is; it sets the attribute based on that name. Binding the method to the A instance.
This would only bind set_var once from the first object given.
def add_object(self, object):
self.objects.append(object)
B_methods = set([m for m in dir(object) if callable(getattr(object, m))])
A_methods = set([m for m in dir(self) if callable(getattr(self, m))])
to_set = B_methods.difference(A_methods)
for method in to_set:
setattr(self, method, getattr(object, method))

convert python objects to string

I have different python classes and I want to convert instances of these classes to ascii strings.Suppose that I have the following objects:
class A:
name = "A"
field = 2
x = "a"
class B:
name = "b"
item = "c"
a = A()
b = B()
I want a function like serialize() which converts the objects as follows:
serialize(a) # results "A2a"
serialize(b) # results "bc"
I don't want to write a serialization function for every classe, I want to have a function capable of serializing all classes. One way is to use the dir() function to get a list of object attributes and then create a string out of them, but the dir() function does not return the attributes in the same order that they are defined. For example calling dir(a) would return ['__doc__', '__module__', 'filed', 'name', 'x'], and I cannot find out which attribute is defined first in the class.
Thanks.
You can override __repr__ function for print Object the desired way:
class A:
name = "A"
field = 2
x = "a"
def __repr__(self):
return A.name + str(A.field) + A.x
You have to create methods in your classes:
ON behalf to put the value in the class the best can be to pass argument of class A during the call%
class A:
def __init__(self, name, field, x) :
self.name = name
self.field = field
self.x = x
def serialize(self) :
return f"{self.name}{self.field}{self.x}"
#main:
a=A(name="a", field="field", x="x")
result=a.serialize()
print(result)

correct way to return variables as arguments to method

A noob confused question,
I have two methods in a class as :
from example import sample2
class sample1:
def m1():
a='apple'
b='ball'
return sample2.m3(a,b)
def m2():
a='ant'
b='bat'
c='cat'
return sample2.m3(a,b,c)
in example.py:
class sample2:
def m3("here I want to access any `a`,`b`,`c` of respective m1 and m2"):
.....
Iam sorry if this question makes no sense, but when I try to access only this as:
class sample1:
def m1():
a='apple'
b='ball'
return sample2.m3(a,b)
in example.py:
class sample2:
def m3(a,b):
print(a)
a has value apple, so similar way why can't I access any value of a,b,c from that particular m1,m2 returned ?
This is how you use decorators. More information how decorator works can be found in for example here: https://www.datacamp.com/community/tutorials/decorators-python
I would suggest you to first try to better understand concept of class and objects. Example tutorial: https://www.w3schools.com/python/python_classes.asp
This post could also help you to understand how staticmethod decorator works - What is the difference between #staticmethod and #classmethod?
from example import sample2
class sample1:
#staticmethod
def m1():
a='apple'
b='ball'
return sample2.m3(a,b)
#staticmethod
def m2():
a='ant'
b='bat'
c='cat'
return sample2.m3(a,b,c)
example.py file with explanation:
class sample2:
#staticmethod
def m3(a, b, c=None): # it works exactly the same as m3 function that is outside the class
print(a)
# this can be used without creating an object of sample2 class, example:
# sample2.m3(a="apple, b="ball")
def m3_method(self, a, b): # this one requires object on which it can be called
print(a)
# you have access to sample2 class object via self parameter, example of code:
# sample2_object = sample2() # you create object of sample2 class here
# sample2_object.m3_method(a="apple", b="ball") # you call m3_method on sample2_object here
def m3(a, b, c=None): # default value of c is add so you can either call it with 2 or 3 arguments
# example calls:
# m3("a", "b")
# m3("a", "b", "c")
print(a)
You should be able to run this code and I think it gives you an idea how Python classes can be used.
Variables in Python always apply to a specific scope, such as a class, function or closure. Python uses lexical scoping, which means scopes are only connected by nesting in the source code. Most importantly, variables in different scopes are not connected at all.
When you "pass a variable" to a function, you are actually passing only the value around. The variable does not exist in other functions (unless they are nested) nor the surrounding scope.
def nested(a):
a = 3
print('a =', a) # a = 3
def parent():
a = 4
nested(a)
print('a =', a) # a = 4
parent()
print(a) # NameError: name 'a' is not defined
Functions should primarily exchange data by calling with input and returning results:
def nested(a): # receive input
a = 3
print('a =', a) # a = 3
return a # return output
def parent():
a = 4
a = nested(a) # replace a with result of nested(a)
print('a =', a) # a = 3
parent()
Note that only values are passed in and returned. The above could would behave exactly the same if you renamed a in either function.
When working with class instances, the instance itself works as a namespace (similar to a scope). Methods of that instance can exchange data by modifying attributes of the instance. The instance is always passed as the first argument to methods:
class Example():
"""An example for setting attributes on an instance"""
def __init__(self):
self.a = 0
def nested(self):
self.a = 3
print('self.a =', self.a) # self.a = 3
def parent(self):
self.a = 4
print('self.a =', self.a) # self.a = 4
self._nested()
print('self.a =', self.a) # self.a = 3
instance = Example()
print(instance.a) # 0
instance.parent() # self.a = 4
# self.a = 3
To exchange data between objects, methods should also primarily exchange data by calling with input and returning results :
class Example():
"""An example for setting attributes on an instance"""
def __init__(self, a):
self.a = a
def multiply(self, value):
return self.a * value
instance = Example(6)
print(instance.multiply(10)) # 60

Calculating the difference between the output of two class objects

I have created two class objects that retrieve information from a database and store them in pandas in order for me to use the data science libraries. They both return values that I display in a Django template. I want to create a third value that is just the calculated difference of the first two and also display that in the Django template.
First class object:
class IntDailyNumbers (object):
def __init__(self, begin_date, end_date, store=None):
self.begin_date = begin_date
self.end_date = end_date
self.store = store
self.int_daily_numbers = pd.DataFrame(list(gapayment.objects.values('Trans_Store', 'Fee_Pd', 'Trans_date')))
self.int_daily_numbers['Fee_Pd'] = pd.to_numeric(self.int_daily_numbers['Fee_Pd'])
self.int_daily_numbers['Trans_date'] = pd.to_datetime(self.int_daily_numbers['Trans_date'])
self.sum_int_daily_numbers = np.sum(self.int_daily_numbers[(self.int_daily_numbers['Trans_date'] >=self.begin_date) &
(self.int_daily_numbers['Trans_date'] <= self.end_date) &
(self.int_daily_numbers['Trans_Store'] == self.store.store_number)])
def get_sum_int_daily_numbers(self):
sum_intdailynumbers = self.sum_int_daily_numbers['Fee_Pd']
sum_intdailynumbers = round(sum_intdailynumbers.astype(float), 3)
return sum_intdailynumbers
def __str__(self):
return self.get_sum_int_daily_numbers()
Second Class Object:
class IntDailyGoals (object):
def __init__(self, begin_date, end_date, store=None):
self.begin_date = begin_date
self.end_date = end_date
self.store = store
#print(self.begin_date, self.end_date, self.store.store_number)
self.int_mnth_goal = pd.DataFrame(list(StoreGoalsInput.objects.values('store_number',
'interest',
'date')))
self.int_mnth_goal['interest'] = pd.to_numeric(self.int_mnth_goal['interest'])
self.int_mnth_goal['date'] = pd.to_datetime(self.int_mnth_goal['date'])
self.mnth_goal_int =self.int_mnth_goal[(self.int_mnth_goal['date'] >= self.begin_date) &
(self.int_mnth_goal['date'] <= self.end_date) &
(self.int_mnth_goal['store_number'] == self.store.store_number)]
self.mnth_goal_int= self.mnth_goal_int['interest']
self.tot_workingdays = np.busday_count(np.datetime64(self.begin_date),
np.datetime64(self.end_date),
weekmask='Mon Tue Wed Thu Fri Sat')
self.div_intmnthgoal_workingdays = round(np.divide(self.mnth_goal_int, self.tot_workingdays),2)
def get_div_goalsint_wdays(self):
div_goalsint_wdays = self.div_intmnthgoal_workingdays.tolist()[0]
return div_goalsint_wdays
def __str__(self):
return self.get_div_goalsint_wdays()
I believe I need to make a third class for the difference calculation but I cannot figure out how to pass the returns of the first two.
How I understand your question, it has nothing to do with django, but with passing objects from one class to another. Here are two simple solutions, given by class C and class D.
First lets define a minimal example of your two classes. Both have a method, that does something and returns something.
class A:
def foo(self):
print('foo')
return 'foo'
class B:
def bar(self):
print('bar')
return 'bar'
Solution 1: We create a third class, that has an instance of type A and an instance of type B as member variable.
# Contains a class instance of A and B and baz() accesses their methods.
class C:
def __init__(self, a, b):
self.a = a
self.b = b
def baz(self):
self.a.foo()
self.b.bar()
You can use this class with:
# main
a = A()
b = B()
c = C(a,b)
c.baz()
Solution 2: We simply pass the two return values to the function qux(). This function can also be part of a class, if you want it to be.
class D:
def qux(self, res_a, res_b):
print(res_a, res_b)
Here we can use qux() in the following way
# main
a = A()
b = B()
d = D()
d.qux(a.foo(),b.bar())
Since there was some confusion about inheritance. You can also inherit the methods. Don't confuse this with Solution 1. And I don't advise to use inheritance here. The idea of inheritance is, that a child class object is-a parent-class object, like a square is-a rectangle. And that is not what you want.
class E(A,B):
def foobar(self):
self.foo()
self.bar()
Usage from main:
e = E()
e.foobar()

Getting the class a variable resides in

Lets say I have something like this -
class A(object):
c = C()
class B(A):
pass
class C(object):
def __init__(self):
pass
def get_parent_class(self):
# This should return B
How would I implement get_parent_class, so that it will work as following -
B.c.get_parent_class() # Returns the class type B (not the instance b!)
Is this even possible?
I basically have a parent class (class A in our example), which contains a variable (var c in the example). I then have a child-class (class B which inherits A)
I want to use functions that C exposes on B, but in order to be used properly, I need C to know it's running on B
(Hope I didn't complicate things in the last explanation...)
Edit -
Please note that I'm not trying to get the class of C, that's an easy c.__class__. I need the class holding c
Thanks!
AFAIK, you cannot do that.
B.c just returns a reference to a C object. Same object can be a member of a list, of an instance of another class (say D) and of another class (say E)
Just add to your example :
class D:
def __init__(self, c):
self.c = c
class E:
c = C()
Then :
>>> c = B.c
>>> E.c = c
>>> d = D(c)
>>> c.a = 1
>>> B.c.a
1
>>> d.c.a
1
>>> E.c.a
1
At that point, c object itself does not know that it belongs to B, d and E. And what should c.get_parent_class() return ? How to decide between B and E ?
You could try to make C aware of its container :
class C(object):
def __init__(self, clazz):
self.clazz = clazz
def get_parent_class(self):
return self.clazz
class A(object):
c = C(A)
You would get A.c.get_parent_class() giving <class '__main__.A'> but as it would be the same object, B.c.get_parent_class() will also give<class '__main__.A'> ...
You could take a look at the __bases__ attribute of your object. It returns a tuple of base classes for an object.
You can see it in the docs here.
You can get the class from the name attribute, example:
>>> import itertools
>>> x = itertools.count(0)
>>> x.__class__.__name__
'count'
Or this way:
>>> type(x).__name__
'count'

Categories