Hi,

I have a class SubDevice with a function action .
For each instance of SubDevice I want to define another action, so it's a parameter of SubDevice's class constructor.
Now some of the action functions need additional parameters, but that's normally not a problem because I can write something like

sd1 = SubDevice(action=lambda: f1(param1, param2))

But how do I pass the instance of SubDevice as a parameter of the action function? I can't use self , because self refers to the class Device , where I instantiate SubDevice .

Thank you


Johannes Riecken

Recommended Answers

All 9 Replies

the solution, i got from your problem as explained is to construct another class which inherit SubDevice. And this ll make you to call your function as your wish.

Hi Johannes Riecken,

But how do I pass the instance of SubDevice as a parameter of the action function? I can't use self, because self refers to the class Device, where I instantiate SubDevice.

Could you please provide, class implementation structure, so that by looking at code we can help you.

BTW, i never used function as a class constructor argument. Sounds something interesting. I would surely want to look into. I would surely like to look at it.

kath.

If I understand, you want something like this:

class SubDevice(object):

    def __init__(self, action):

        self.action = action

def action1():
    print "Hello!"

def action2():
    print "Goodbye!"


sd1 = SubDevice(action1)
sd2 = SubDevice(action2)

sd1.action()
sd2.action()

>>> 
Hello!
Goodbye!

Now, I'm not sure where the variable parameter list would come into play. It seems to me that if you want a transparent interface, then you have to make the parameter list consistent. Can you give an example of what you had in mind?

Hope it helps,
Jeff

Hi,
As Sharma said one solution would be to construct subclasses of SubDevice for every different action and I thought about doing that in the beginning. However, my instructor is against it. Of course he's more procedurally oriented than younger programmers, but I agree that it would be too much overhead to construct subclasses, as they would be too specialized and they don't need any additional parameters.

Actually what jrcagle wrote is pretty much what I have in mind:

class SubDevice(object):

    def __init__(self, action, super_device):

        self.action = action
        self.state = "ready"
        self.super_device = super_device

def action1():

    if super_device.plate_count > 0:
        super_device.plate_count -= 1

    else:
        XXX.state = "error"

def action2():

    print "Goodbye!"

class SuperDevice(object):

    def __init__(self, ...):

        self.plate_count = 100

SuperDevice is larger device consisting of many different SubDevices. There could be numerous events when the plate_count has to decrease, so I have a separate method for it.
Now the problem is that I can't set the state field of SubDevice with action1 , as I don't know what to replace XXX with.

Hope this helps!

Johannes

Hi,

I am not sure i understand your problem. I have some questions before i say something.

def action1():

    if super_device.plate_count > 0:
        super_device.plate_count -= 1

    else:
        XXX.state = "error"

in the above code, is super_device object of SuperDevice class and is global?

If I believe it is,

Now the problem is that I can't set the state field of SubDevice with action1, as I don't know what to replace XXX with.

you can return "error" so that SubDevice which is calling that action function will get the state value.


let me know how this works...


kath.

Is there any problem in making the self argument in the action functions mandatory?

import functools
class SubDevice(object):
    def __init__(self, action):
        #always pass a reference to self as first argument
        self.action = functools.partial(action, self)
        self.state = "ready"

def action1(self): #the argument list has to contain self!
    if self.state == "ready":
        self.state = "error"
    else:
        self.state = "ready"

a = SubDevice(action1)
a.action()
print a.state # 'error'

Huh. There's something funky here that I don't understand. Because I don't have the module functools, I simplified a bit:

class SubDevice(object):
    def __init__(self, action):
        #always pass a reference to self as first argument
        self.action = action # functools.partial(action, self)
        self.state = "ready"

def action1(self): #the argument list has to contain self!
    if self.state == "ready":
        self.state = "error"
    else:
        self.state = "ready"

a = SubDevice(action1)
a.action() #### <---
print a.state # 'error'

Shockingly, this throws an error:

Traceback (most recent call last):
  File "C:/Python24/subdevicetest.py", line 14, in -toplevel-
    a.action()
TypeError: action1() takes exactly 1 argument (0 given)

Now, my understanding of the paradigm was that a.action() called the member method with a as the first parameter. Yet ... it doesn't!

When I replace with:

class SubDevice(object):
    def __init__(self, action):
        #always pass a reference to self as first argument
        self.action = action # functools.partial(action, self)
        self.state = "ready"

def action1(self): #the argument list has to contain self!
    if self.state == "ready":
        self.state = "error"
    else:
        self.state = "ready"

a = SubDevice(action1)
a.action(a) ### <----
print a.state # 'error'

Then it works fine. So yes, I think there is an issue with what you want to do, and no, I don't (yet) know what it is.

Vega, thoughts?

Jeff

Followup -- here's a quote from Yoda himself:

What exactly happens when a method is called? You may have noticed that x.f() was called without an argument above, even though the function definition for f specified an argument. What happened to the argument? Surely Python raises an exception when a function that requires an argument is called without any--even if the argument isn't actually used ...
Actually, you may have guessed the answer: the special thing about methods is that the object is passed as the first argument of the function. In our example, the call x.f() is exactly equivalent to MyClass.f(x). In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list that is created by inserting the method's object before the first argument.

So Guido thinks your code oughta work. :) Something funky is going on here. Or I'm not thinking straight. LOL

BTW, the clean way to do what you want is this:

class SubDevice(object):
    def __init__(self, action):
        #always pass a reference to self as first argument
        self.action = lambda : action(self)
        self.state = "ready"

def action1(self): #the argument list has to contain self!
    if self.state == "ready":
        self.state = "error"
    else:
        self.state = "ready"

a = SubDevice(action1)
a.action()
print a.state # 'error'

Jeff

Ah. Many regards to Matthew Dixon Cowles over at Python-help, who says,

Dear Jeff,> Hi,
Hello!> I was trying to assist someone who wants to hook methods into
> objects post-instantiation.
> I'm really confused. I thought that a.action() automatically
> passed a as the first parameter.
You can stick a Python function pretty much anywhere:>>> def f():
... print "wibble"
...>>> l=[f]
>>> l[0]()
wibble

But making a function an attribute of an object doesn't automatically
make it a method:>>> class c:
... pass
...>>> o=c()
>>> o.x=f
>>> o.x()
wibble

There, f is a plain function that's an attribute.

To make a function into an instance's method, you need the
instancemethod() function of the new module:>>> def m(self):
... print "wibble"
...>>> import new
>>> o.m=new.instancemethod(m,o,c)
>>> o.m()
wibble

Regards,
Matt

There it is. Following this lead, your code could become:

import new

class SubDevice(object):
    def __init__(self, action):
        #always pass a reference to self as first argument
        self.action = new.instancemethod(action,self,SubDevice) 
        self.state = "ready"

def action1(self): #the argument list has to contain self!
    if self.state == "ready":
        self.state = "error"
    else:
        self.state = "ready"

a = SubDevice(action1)
a.action()
print a.state # 'error'

and that works.

I would not have predicted that... LOL

Jeff

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.