If I have a class that contains a method, and I call that method from an instance, how do I get the specific instance it was called from?
e.g. I have a class:
class foo:
def __init__(self):
pass
def do_something(self):
bar()
I also have a function:
def bar():
print()
If i make an object, e.g. obj = foo(), and then i call obj.do_something(), How would i get obj from inside my bar() function?
Using the inspect module, you can get the caller frame and access its local variables, in particular the self argument representing the instance:
import inspect
def bar():
caller = inspect.stack()[1].frame
localvars = caller.f_locals
self = localvars['self']
Related
The object is created by a = Foo(), the object can be nested with a.b, after that I can perform a.b.bar() to print("Hello World")
class Foo:
def bar():
print("Hello World")
def __getattr__(self, name):
setattr(self,name,Foo)
The problem is I need to first use a.b to create an object first, then only I can do a.b.bar(), is there a way that I could do a.b.bar() straightaway to create the object and access the bar() at the same time?
I want to dynamically determine function dispatch in my python code.
Like so :
https://docs.python.org/3/faq/programming.html#how-do-i-use-strings-to-call-functions-methods
I build a dispatch dictionary wherein:
dispatch = {'foo' : foo_function, 'bar': bar:function}
And then from within code, I can call the appropriate function based on my data string by saying:
dispatch[data]()
So far so good. My issue is I want this functionality inside a class accessible to it's methods:
class Demo(object):
""" Demonstrate the issue
"""
dispatch = {'foo' : self.call_foo, 'bar': self.call_bar}
def parse_component(self, data, some_param):
self.__class__.dispatch[data](self, some_param)
def call_foo(self, some_param):
""" Do some intelligent work here"""
pass
def call_bar(self, some_param):
""" Do some intelligent work here"""
pass
The problem is that dispatch table is a class variable and does not have a notion of the instance methods self.call_foo and self.call_bar.
I tried using get_attr :
class Demo(object):
dispatch = {'foo': getattr(Demo, 'call_foo')}
I get a NameError on Demo with this. It does not recognize the class name from within the class which I guess makes sense.
Other than making dispatch another instance method, is there any other way to do this?
Ideally dispatch should be a class variable/method since it is constant to the class.
The functions Demo.call_foo and Demo.call_bar are functions which take in an instance self and some_param. When you create an instance of Demo, e.g. d = Demo(), those functions now become bound methods where the self parameter is already passed for you. But the other form still exists, so you can call instance methods with ClassName.method(instance, ...). For example:
class Demo:
def call_foo(self):
print("called foo")
Calling it both ways works:
>>> d = Demo()
>>> Demo.call_foo(d)
called foo
>>> d.call_foo()
called foo
This is relevant to your question because inside the class definition, you don't have an instance self to use, but you can refer to the functions directly. Since they are functions defined in the class scope, they can be referenced directly like any other class attribute.
But like we've seen above, you can pass the instance along to the function. You're already doing that in parse_component. So the only problem with your current code is that you tried to use self to reference the methods. But since the dict is defined in the class scope, you can just refer to them by name:
class Demo:
def parse_component(self, data, some_param):
self.__class__.dispatch[data](self, some_param)
def call_foo(self, some_param):
print("called foo with", some_param)
def call_bar(self, some_param):
print("called bar with", some_param)
dispatch = {'foo' : call_foo, 'bar': call_bar}
And using it:
>>> d = Demo()
>>> d.parse_component("foo", 2)
called foo with 2
>>> d.parse_component("bar", 3)
called bar with 3
>>> Demo.parse_component(d, "foo", 4)
called foo with 4
In module a.py
def task():
print "task called"
a = task
class A:
func = task # this show error unbound method
#func = task.__call__ # if i replace with this work
def __init__(self):
self.func_1 = task
def test_1(self):
self.func_1()
#classmethod
def test(cls):
cls.func()
a()
A().test_1()
A.test()
Output:
task called
task called
Traceback (most recent call last):
File "a.py", line 26, in <module>
A.test()
File "a.py", line 21, in test
cls.func()
TypeError: unbound method task() must be called with A instance as
first argument (got nothing instead)
In the module, I can easily assign a function to a variable. When inside class try to assign module level function to class variable func = task it shows error, to remove this error I have to replace it with func = task.__call__ But when I assign to instance variable its work self.func_1 = task.
My Question is: why I can't assign a module level function to a class variable without __call__ and when the same function I can assign to an instance variable is working.
Because you mapped a function as unbound method of A, so when you are calling cls.func you first ask something equals to getattr(cls, 'func') which returns <unbound method A.task> BUT, this unbound method needs to be called with class as first argument.
So because in this specific case cls.func means "gives me class attribute func of cls" it can not means to the same time "call class method func" - So Python doesn't translate cls.func() by func(cls).
But within the same time, because func is <unbound method A.task> (bound to A.task) it needs to be called like func(cls) to work.
Check it with something like:
#classmethod
def test(cls):
print getattr(cls, 'func') # <unbound method A.task>
You could fix it with something like:
def task(cls=None):
if cls is None:
print 'task()'
else:
print 'A.foo({})'.format(cls)
a = task
class A:
func = task # this show error unbound method
def __init__(self):
self.func_1 = task
def test_1(self):
self.func_1()
#classmethod
def test(cls):
cls.func(cls())
a()
A().test_1()
A.test()
Output:
task()
task()
A.foo(<__main__.A instance at 0x7fd0310a46c8>)
Note that python3 removes unbound methods, this works only with python2.x
hey guys need to know as of how to start a method in a classA from class B
have
classA(object):
def __init__(self):
#this is where the ClassB method'' def multiplyPeople() ''should be called or started.
classB(object):
def multiplyPeople(self):
its giving an error
TypeError: unbound method multiplyPeople() must be called
with classB instance as first argument (got nothing instead)
know this is something basic, but am trying to figure out what exactly is supposed to be done and where am I getting lost.
I have called it as
classA(object):
def__init__(self):
self.PeopleVariable=classB.multiplyPeople()
It depends on how you want the function to work. Do you just want to use the class as a placeholder? Then you can use a so called static method for which you do not need to instantiate an object.
Or you can use a regular method and use it on a created object (notice that there you have access to self)
class A():
def __init__(self):
b = B()
b.non_static()
B.multiplyPeople()
class B():
#staticmethod
def multiplyPeople():
print "this was called"
def non_static(self):
print self, " was called"
if __name__ == "__main__":
a = A()
output:
<__main__.B instance at 0x7f3d2ab5d710> was called
this was called
I was just pulling off a toy example for myself, but it is not working and I cannot make it work. Does anybody know why this is not working and how to make it work:
class A(object):
#def __init__():
#pass
def do1():
print("foo")
def do2():
print("Hello")
Al = A
Al.do1()
TypeError: unbound method do1() must be called with A instance as first argument (got nothing instead)
In your code variable A1 it's reference to your class not instance, create instance of your class
Al = A()
and run your method
Al.do1()
You need to call your class to crate a correct instance and pass the self keyword to enclosing function to provides a handle back to the instance to be processed :
class A(object):
#def __init__():
#pass
def do1(self):
print("foo")
def do2():
print("Hello")
Al = A()
Al.do1()
Note that without passing the self to your function after calling it you will get a TypeError.
TypeError: do1() takes 0 positional arguments but 1 was given
Or as #Padraic Cunningham mentioned in comment you can use staticmethod as a decorator to wrap your function which makes python doesn't pass the first default argument (self) to it.
class A(object):
#def __init__():
#pass
#staticmethod
def do1():
print("foo")
def do2():
print("Hello")
Al = A()
Al.do1()