1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 <2014> 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 | Index | 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 <2014> 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 |
<== Date ==> | <== Thread ==> |
---|
Subject: | RE: subclassing Pyepics epics.motor doesn't work as expected |
From: | Terry Cornall <[email protected]> |
To: | Matt Newville <[email protected]> |
Cc: | "[email protected] \([email protected]\)" <[email protected]> |
Date: | Sun, 1 Jun 2014 23:43:42 +0000 |
Thanks Mark, I figured it would be to do with this dynamic behaviour. I got around it by ‘side-classing’ rather than subclassing. That is, made a new class
that had an epics.Motor object as an attribute rather than inheriting from it. Terry From: [email protected] [mailto:[email protected]]
On Behalf Of Matt Newville Hi Terry, Sorry for the trouble. The Device classes in pyepics, including the Motor class, are a little unusual in that they override __getattr__() and __setattr__() for getting/setting instance attributes that are internally mapped to fields of the appropriate device. That is, one can do >>> print m1.VAL # gets VAL field >>> m1.VAL = 2.0 # puts to VAL field >>> print m1.RBV Since there are many fields in the Motor record, many of which aren't needed for most use cases, a Motor object does not try to connect to all fields at initialization but instead trie to connect to a particular
field only when necessary (ie, as late as possible). Currently this means that __getattr__() and __setattr__() will treat any unknown attribute as a potential PV, so that
>>> print m1.FOO
Right now, the list of "attributes to not convert to PVs" is hardwired into the Motor class (and other Devices too), and I don't think it can be easily altered by a subclass. At least, I don't see an easy way
to do that. It would certainly be reasonable to change this behavior. I think it might be preferable to have an explicit list of attributes that will be converted to PVs rather than trying to convert every unknown attribute
to a PV. That would better allow subclassing, and avoid silent hanging on typos. This might take a little effort, but is probably worth it. --Matt On Wed, May 28, 2014 at 7:33 PM, Terry Cornall <[email protected]> wrote: Hi All. I’ve been trying to subclass the Motor class in Pyepics, so I can add my own variables and methods to it, and I’ve run into problems. Adding new methods seems to work just fine, but I find a strange problem when I try to add new ‘instance variables’ to my subclass. They all end up having value ‘None’ if I add them after doing the __init__ of the base Motor class. Worse, if I try to define Any ideas what I am doing wrong, or why epics.Motor can’t be subclassed in this way? Any suggestions as to how I can subclass it and add my own variables, or why I shouldn’t?
I suppose an alternative would be to make a class that contains an epics.Motor object rather than subclass it. Thanks,
Terry import epics #get the Motor class (and everything else) from Pyepics class myBaseClass(): #make a simple base class so I can test that subclassing works as expected def __init__(self,*args,**kwargs): self.baseVar=kwargs.pop('mssg') #have some variable just so we aren't empty class myClass(myBaseClass): #subclass myBaseClass def __init__(self,*args,**kwargs): self.before_init_base_instance_variable=1 #add an instance variable before I init base class. This works myBaseClass.__init__(self,*args,**kwargs) #init the base class self.new_instance_variable=1000 #here’s a new instance variable that I want in my subclass self.another_new_instance_variable=2000 #here’s another new instance variable that I want in my subclass class myMotor(epics.Motor): #subclass epics.Motor def __init__(self,*args,**kwargs): # self.before_init_instance_variable=1 #This would get an exception from motor.py, so I comment it out epics.Motor.__init__(self,*args,**kwargs) #call the epics.Motor ‘s __init__ to set it up self.new_instance_variable=1000 #here’s a new instance variable that I want in my subclass self.another_new_instance_variable=2000 #here’s a new instance variable that I want in my subclass c=myClass(mssg='fred') print c.before_init_base_instance_variable #This works. print c.baseVar #print the variable in the myBaseClass just to show it worked print c.new_instance_variable #access the variable I added. It works print c.another_new_instance_variable #access the second variable I added. It works c.new_instance_variable=3000 print c.new_instance_variable #see if writing to it worked. It does m=myMotor('SR10BM01DMC05:MTR01') #create an instance of my subclass, based on epics.Motor # print m.before_init_base_instance_variable #Just trying to define this causes an exception so it’s commented out print m.new_instance_variable #try to access the variable I added, get 'None' print m.another_new_instance_variable #try to access the second variable I added, get 'None' m.new_instance_variable=3000 print m.new_instance_variable #see if writing to it worked. Get 'None' This is what I get when I run the code. 1 #this is from the instance variable I added before calling myBaseClass.__init__ and it works Fred #this is the variable in the the myBaseClass and it is initialised as expected. 1000 #The next three are instance variables I added to my sublass based on myBaseClass 2000 #and they work as expected 3000 #This is the instance variable I wrote to and it worked. #The next three are from the instance variables I added to the subclass based on epics.Motor None #should have been 1000 None #should have been 2000 None #should have been 3000
|