Let's say that I have a class defined in moduleA.py which I want to add a method to, using some sort of loader method that takes a the name of a second module and the method defined there that should be bound
class ClassA(object):
def __init__(self,config):
super(ClassA, self).__init__()
self.a = 1
self.b = 2
self.meth1 = self. bind_method(config)
def bind_method(self,config):
# load method
<return method defined in config as a str 'moduleB.meth2'>
def calling_method():
return self.meth1()
where the method defined in moduleB.py looks something like:
def meth2(self):
return self.a + self.b
The point being that I want to be able to write meth2 to be able to access class variables of ClassA once it is bound. This way, when you would have something like:
from moduleA import ClassA
A = ClassA()
aout = A.calling_method()
Calling A.calling_method() properly calls the method defined in moduleB.py.
I've seen this sort of binding done in answers on SO after ClassA is instantiated using types.MethodType, but I haven't been able to dig up how to bind inside the class definition so that it is done internally when the class is instantiated.
Any suggestions on what should go in the bind_method method would be much appreciated.
import sys
import types
def getobj(astr):
"""
getobj('scipy.stats.stats') returns the associated module
getobj('scipy.stats.stats.chisquare') returns the associated function
"""
try:
return globals()[astr]
except KeyError:
try:
return __import__(astr, fromlist=[''])
except ImportError:
modname, _, basename = astr.rpartition('.')
if modname:
mod = getobj(modname)
return getattr(mod, basename)
else:
raise
class ClassA(object):
def __init__(self, methpath):
super(ClassA, self).__init__()
self.a = 1
self.b = 2
self.meth1 = types.MethodType(getobj(methpath), self)
a = ClassA('moduleB.meth2')
print(a.meth1())
# 3
Skipping the config stuff which wasn't clear to me, the binding itself would look like this:
from moduleB import meth2
ClassA.meth1 = meth2
The important part is that you're binding to the class, not to an instance. This way if you call meth1 on an instance, it will automatically receive the instance as the first argument.
Since meth2() is a function, it is a descriptor and you can bind it by calling the __get__() method.
def meth2(self):
return self.a + self.b
class ClassA(object):
def __init__(self,config):
super(ClassA, self).__init__()
self.a = 1
self.b = 2
self.meth1 = config.__get__(self, ClassA)
c = ClassA(meth2)
print c.meth1() #correctly prints 3
There's actually a much simpler way to do this:
class ClassA(object):
def __init__(self,config):
super(ClassA, self).__init__()
self.a = 1
self.b = 2
from moduleB import meth2 as meth1
def calling_method():
return self.meth1()
Related
So basically my problem seems like this.
class A():
def func(self):
return 3
class B():
def func(self):
return 4
class AA(A):
def func(self):
return super(AA, self).func
class BB(B):
def func(self):
return super(BB, self).func
The func function is doing some work and one of the things it does is getting some attribute(or running method or whatever) from it's parent class.
Since func originally does the same logic at both cases (except that only parent class changes) I'd like to do this with decorators.
Is it possible? if so how to do it? Do I have somehow to pass parent-class as a argument?
I'll be very grateful for answers it's been bothering me for a while now.
There is no need to use super to access data attributes of a parent class.
Neither does a class need a parent in order for access to data attributes to work.
You can use a mixin to do the job:
# A and B stay the same - they still have a c attribute
class A():
c = 3
class B():
c = 4 # I've changed B to make it clear below
#Instead have a mixin which defines func()
class Mixin:
def func(self):
# func has its behaviour here
return self.c
class AA(Mixin, A):
pass
class BB(Mixin, B):
pass
a = AA()
b = BB()
print(a.func())
print(b.func())
Output:
3
4
You could do it with a single class decorator by defining a generic method inside of it that does what you want, and then adding it to the class being decorated. Here's what I mean:
def my_decorator(cls):
def call_super_func(self):
return super(type(self), self).func()
setattr(cls, 'call_super_func', call_super_func)
return cls
class A():
def func(self):
print('in A.func')
return 3
class B():
def func(self):
print('in B.func')
return 4
#my_decorator
class AA(A):
def func(self):
print('in AA.func')
return self.call_super_func()
#my_decorator
class BB(B):
def func(self):
print('in BB.func')
return self.call_super_func()
aa = AA()
aa.func()
bb = BB()
bb.func()
Output:
in AA.func
in A.func
in BB.func
in B.func
Of course you could eliminate the need to do this by just defining baseclass for A and B that has a call_super_func() method in it that they would then both inherit.
I have the following problem (stripped-down code):
class T(object):
def __init__(self):
pass
def default():
return 0
def foo():
return 2
class A(T):
def infl_(): pass
def __init__(self, infl = default):
super(A, self).__init__()
self.infl_ = infl
a = A(infl = foo)
Now I would expect that when I call a.infl_() that foo gets called. However, somehow default() is called, and I have no idea why this is happeneing. Do I do something wrong here or should I look for a mistake somewhere else in my code?
Edit: Edited the mistakes from the comments.
I want to do something like:
class A(Resource):
#dec(from_file=A.docpath)
def get(self):
pass
class B(A):
docpath = './docs/doc_for_get_b.json'
class C(A):
docpath = './docs/doc_for_get_c.json'
def dec(*args, **kwargs):
def inner(f):
docpath = kwargs.get('from_file')
f.__kwargs__ = open(path, 'r').read()
return f
return inner
The functions that will be called are B.get and C.get, never A.get.
How can I access the custom attribute docpath defined in class B or class C and pass it to the decorator of the get function in class A ?
Current solution: Put the decorator on each derived class ...
class A(Resource):
def _get(self):
pass
class B(A):
#dec(from_file='./docs/doc_for_get_b.json')
def get(self):
return self._get()
class C(A)
#dec(from_file='./docs/doc_for_get_c.json')
def get(self):
return self._get()
This works but it's pretty ugly compared to the one-line declaration of the classes in the previous code.
To access a class's attributes inside the decorator is easy:
def decorator(function):
def inner(self):
self_type = type(self)
# self_type is now the class of the instance of the method that this
# decorator is wrapping
print('The class attribute docpath is %r' % self_type.docpath)
# need to pass self through because at the point function is
# decorated it has not been bound to an instance, and so it is just a
# normal function which takes self as the first argument.
function(self)
return inner
class A:
docpath = "A's docpath"
#decorator
def a_method(self):
print('a_method')
class B(A):
docpath = "B's docpath"
a = A()
a.a_method()
b = B()
b.a_method()
In general I've found using multiple levels of decorators, i.e. decorator factory functions that create decorators such as you've used and such as:
def decorator_factory(**kwargs):
def decorator_function(function):
def wrapper(self):
print('Wrapping function %s with kwargs %s' % (function.__name__, kwargs))
function(self)
return wrapper
return decorator_function
class A:
#decorator_factory(a=2, b=3)
def do_something(self):
print('do_something')
a = A()
a.do_something()
a difficult thing to get right and not easy to comprehend when reading code, so I would err towards using class attributes and generic superclass methods in favour of lots of decorators.
So in your case, don't pass the file path in as an argument to your decorator factory, but set it as a class attribute on your derived classes, and then write a generic method in your superclass that reads the class attribute from the instance's class.
Let say I have the following script in modul1:
class IN(object):
def __init__(self):
pass
class C(object):
def __init__(self, x):
pass
def func(self):
cl = IN()
Then I want to use C class inside another script:
from modul1 import C
class IN(object):
def __init__(self):
pass
class C2(C):
def __init__(self, x):
C.__init__(self, x)
I can override C class's func method by creating a method with the same name in C2 class.
But how can I override any call of modul1's IN class inside of imported C class with IN class in the caller modul2?
I want to change some functionality of original IN class. I want C class to call in the row
cl = IN()
my own IN() class with the altered functionality.
module1.py:
class IN(object):
def __init__(self):
print "i am the original IN"
class C(object):
def __init__(self, x):
pass
def func(self):
print "going to create IN from C's func"
cl = IN()
module2.py:
import module1
class IN(object):
def __init__(self):
print "I am the new IN"
class C2(module1.C):
def __init__(self, x):
super(C2, self).__init__(x)
print "\n===Before monkey patching==="
C2(1).func()
#monkey patching old In with new In
module1.IN = IN
print "\n===After monkey patching==="
C2(1).func()
Output while running the script module2.py:
===Before monkey patching===
going to create IN from C's func
i am the original IN
===After monkey patching===
going to create IN from C's func
I am the new IN
You can see how the module2's In constructor is being called.
I have 2 classes defined as such
class class1():
self.stuff = 1
def blah(self):
foo = class2()
foo.start()
class class2(threading.Thread):
def run(self):
#access class1.stuff
How would I access class1.stuff from class2
It could look like this:
class class1(object):
stuff = 1
def blah(self):
foo = class2()
foo.start()
class class2(threading.Thread):
def run(self):
print(class1.stuff)
There is no special syntax to "access calling classes atrributes". If you want access to a object you must give it a visible name, for example by passing it to __init__ or by using the class object like this.
You would have to pass it into the function.
class class1():
self.stuff = 1
def blah(self):
foo = class2()
foo.start(self)
class class2(threading.Thread):
def run(self, obj):
obj.stuff;
There is no way to access another object's properties without having a reference to the object. The easiest way to obtain a reference to an object of class1 is to ask for it as an argument.