350 likes | 548 Views
Python Classes. Python Classes Require Politeness:. Python does not have the privacy mechanisms of C++ and Java and relies upon the user to not take advantage of this. The class hierarchy allows multiple inheritance derived class methods can override base class methods
E N D
Programming Robot Python Classes
Programming Robot Python Classes Require Politeness: • Python does not have the privacy mechanisms of C++ and Java and relies upon the user to not take advantage of this. • The class hierarchy allows multiple inheritance • derived class methods can override base class methods • derived class methods can invoke methods of the same name in the base class • In C++ jargon, all methods are public and virtual (overridable). • operation overloading is possible
Programming Robot Objects: • Objects have individuality: • Objects can have multiple references and a mutable object, passed as an argument to a method, can be modified inside the method • We only need a single syntax for parameters (unlike Pascal, C, C++). x = 3 y = 3 # both reference the same place in memory
Programming Robot Scopes and Namespaces • A namespace is a mapping of names to objects. • Namespaces are implemented with a Python dictionary but this is not noticeable except for its effect on performance. • Namespace examples: • reserved words and built-in names (like abs()). • global names in a module • local names within a method invocation • Different namespaces can reuse the same name. • If a namespace is associated with a module then import myModule print myModule.myName
Programming Robot Scopes and Namespaces • An attribute is anything that follows a “.”. • Attributes are read-only or writable. • Module attributes are writeable • You can delete a writable attribute • Namespace survival: • the built-in namespace is created as the interpreter loads • a module namespace is created when the module loads • method namespaces survive only while the method executes an attribute just like a data field or method in a class print myModule.myName del myModule.myName
Programming Robot Well-known Namespaces: • __main__: the top-level namespace • __builtin__: - the namespace for built-in names.
Programming Robot Scope: • A scope is a text region where unqualified names can be used from some namespace. • There are three or more namespaces active at any moment: • the inner-most namespace • the calling environment namespaces (at least 1) • the built-in namespace • Names from an outer namespace are read-only unless labeled global. • If you attempt to write to an outer namespace name you will just create a new local name # prints 4 3 a = 3 def foo(): a = 4; print a foo() print a
Programming Robot Local and Global Scopes • Local Scope inside a method references the local names of the method • Outside a function or method, local scope is the module scope. • Class definitions introduce a new scope • The global scope of a function is scope of the module it is defined in; not where it is used. We describe this by saying that scope is defined “textually”. • By default, global scope is not in effect. You must specify it explicitly # prints 4 4 a = 3 def foo(): global a a = 4; print a foo() print a
Programming Robot Classes • Class definitions must be executed before the classes are used. • Entering a class definition creates a new namespace which becomes the local scope and new names are part of this scope. • Exiting a class definition creates a class object (not an instance) which wraps the class namespace and the original scope is reinstated. • Inside the new scope the new class object is bound to the name, MyClass. class MyClass: <statement-1> ... <statement-N> most statements are method definitions
Programming Robot Class Objects: • Support two kinds of operations: • attribute references: Standard notation (MyClass.a). • instantiation: Doesn't use new. class MyClass: """ A simple class example""" a = 4 def f(self): return "Hello, world!" print MyClass.a MyClass.b = 5 myClass = MyClass() print myClass.b print myClass.__doc__ print MyClass.__doc__ a docstring creates a new MyClass attribute; future instantiations will include this attribute __doc__ is a valid attribute of all classes that prints out docstrings. # output 4 5 A simple class example A simple class example
Programming Robot ClassAttributes: • Classes are usually defined with no data attributes but rather just a list of method definitions. • Data attributes are added to the instantiated class objects during instantiation by invoking the method __init__(). class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart x = Complex(3.0, -4.5) print x.r, x.i (3.0, -4.5) define an __init__() method to add data attributes to a class self is the local scope name of the instantiate object.
Programming Robot Data Attributes: • The same as “data members” in C++. • Don't need to be pre-declared before use. • They can spring into existence and disappear just as easily x = AnotherClass() . . . x.counter = 1 while x.counter < 10: x.counter = x.counter * 2 print x.counter del x.counter
Programming Robot Method Attributes: • Just notation but function attributes in a class are called method attributes in an instantiation of the class. • Methods are objects just like other things and so you can create new references to them as you wish x = MyClass() xf = x.f while True: print xf() xf is just another reference to the method f() in the MyClass object called x.
Programming Robot What happens when a method is called? • In the MyClass definition we defined f() with parameter self. • When we call x.f() we don't pass f an argument. • What is happening? • Referencing a method, x.f(), causes a new method object to be created consisting of (pointers to) the function object in the class definition packed together with the instance object. • Calling a method, x.f(), the method object is unpacked and the necessary arguments are passed to it. x.f() is really MyClass.f(x)
Programming Robot Random Observations: • There is no shorthand for referencing an instance data attribute inside a method; you must use self.x. • The name, self, is just a convention. • Class methods can actually be defined outside the class: # Function defined outside the class def f1(self, x, y): return min(x, x+y) class C: f = f1 def g(self): return 'hello world' h = g f, g and h are all function objects in the class, C. This example shows what is “possible” but its use would only confuse people.
Programming Robot Inheritence: • What would a class be without inheritance? • Resolving attribute or variable references proceeds from the derived class scope to the base class scope to the module scope. • Method overriding is ok. • All Python methods are virtual in C++ terms. • Calling a base class method of the same name is ok: class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N> BaseClassName(self,arguments)
Programming Robot Creating an empty class: Class Employee: pass
Programming Robot Robot class: • Myro allows you to create objects from the robot class: • In your robot object I want you to save two lists: • List of all methods you have written or inherited • List of all methods you will use in a particular program • The first list gives me a way to access all MyScribbler methods by their string name. • The second list tells me the methods used in the current program in the order in which they will appear in the behaviours list. from myro import * class MyScribbler(Scribbler):
Programming Robot MyScribbler.__init__(): def __init__(self,port): Scribbler.__init__(self,port,t=0,r=0) # a dictionary of <str(methodname),methodName> pairs self.masterMethodsList = {'move':Scribbler.move, 'beep':Scribbler.beep, 'motors':Scribbler.motors, 'avoidLight':self.avoidLight, 'nudgeTowardsLight':self.nudgeTowardsLight, 'nudgeAwayFromLight':self.nudgeAwayFromLight, 'obstacleHit':self.obstacleHit, 'obstacleSensed':self.obstacleSensed, 'dflt':self.dflt } # remember, no comma after the # last method name self.programMethodslist = [ ] # a list of method names in use self.T = t self.R = r self.setAmbientLight() self.brightestLight = 50.0 self.debug = False
Programming Robot Printing out Info about Scribbler: • The Scribbler class has a method called __str__() that needs to be overridden. • This method prints out info about the base class and we can extend it to print our info about our class. def __str__(self): retVal = '' retVal += '\nCurrent Program Methods:' for meth in self.programMethodsList: for (methodName,method) in self.masterMethodsList.items(): if method == meth: retVal += '\n %s ' % methodName retVal += '\nTranslate: %f' % self.T retVal += '\nRotate: %f' % self.R retVal += '\nAmbient: %f' % self.ambient retVal += '\nDebug: %r'% self.debug return retVal
Programming Robot Additional Useful Methods (more to come): def getMasterList(self): return self.masterMethodsList def getProgramList(self): return self.programMethodsList def addToProgramList(self,methodName): if methodName in self.masterMethodsList.keys(): self.programMethodslist.append(self.masterMethodsList[methodName]) def setAmbientLight(self): self.ambient = sum(getLight('all'))/3.0
Programming Robot Main Methods: def arbitrate(self): for behaviour in self.programMethodsList: if self.debug: print behaviour option,T,R,action = behaviour() if option: return T,R,action def main(self,tr): while(timeRemaining(tr)): T,R,action = self.arbitrate() if self.debug: print T, R, action action(T,R) stop()
Programming Robot MyScribbler Behavioural Methods (behaviours): # turns Left if dir == 1; Right if dir == -1 # speed = speed of turn def reactToLight(self,dir,speed): if dir != 1: dir = -1 return True, self.T, (dir*speed),self.move def nudgeTowardsLight(self): (L,C,R) = self.getLight('all') print L,C,R if (L < R - 300): return self.reactToLight(1,0.2) elif (R < L - 300): return self.reactToLight(-1,0.2) else: return False,self.T,self.R,self.move
Programming Robot MyScribbler Behavioural Methods (behaviours): # obstacleHit() really only useful if speed above 0.75 def obstacleHit(self): # obstacle if getStall() returns 1; ob = self.getStall() if ob: return True, 0,-1,self.move else: return False,self.T,self.R,self.move def obstacleSensed(self): L,R = getIR() L = 1 - L R = 1 - R if (L == 1 and R == 1): return True,0, 0.5,self.move elif (L == 1): return True,self.T,-0.5,self.move elif (R == 1): return True,self.T,0.5,self.move else: return False,self.T,self.R,self.move
Programming Robot MyScribbler Behavioural Methods (behaviours): def dflt(self): if self.debug: print 'dflt' return True,self.T,self.R,self.move
Programming Robot How to Use MyScribbler: from myro import * from myRobot import * class Template (MyScribbler): def __init__(self,port,t=0,r=0): MyScribbler.__init__(self,port,t,r) # add additional behaviours here for this program only self.masterMethodsList['myBehaviour'] = self.myBehaviour self.masterMethodsList['dflt'] = self.dflt self.debug = True # turn on debug if wanted def __str__(self): retVal = MyScribbler.__str__(self) return retVal # determines if it is time to charge the cape def myBehaviour(self): # this test behaviour alwasy fails so only dflt is used if self.debug: print 'myBehaviour' return [False,self.T,self.R,self.move] change the name
Programming Robot How to Use MyScribbler: # a normal function def main(): temp = Template('/dev/rfcomm0',0.5,0) temp.addToProgramList('myBehaviour') temp.addToProgramList('dflt') print temp.__str__() MyScribbler.main(temp,2) main()
Programming Robot Dictionary: • A dictionary is a set of <key,value> pairs. • You can initialize a dictionary by putting elements in the dictionary as a key:value pair. self.masterMethodsList = {'move':Scribbler.move, 'beep':Scribbler.beep, 'moveForward5':self.moveForward5 }
Programming Robot How To Write Programs: • You first need to think about the behaviours you want to capture. For example, in simulating the bull in a bull ring you need to think about • bull-behaviour (find and charge the red cape) • incidental behaviour (avoiding other obstacles). • Next you have to think about the behaviours in the order in which you should determine their appropriateness. chargeCape(), followLight() obstacleHit(), obstacleSensed(), dflt()
Programming Robot How To Write Programs: • Next you have to think about the behaviours in the order in which you should determine their appropriateness. • For example, the bull begins to detect a cape by sensing a light source. As it gets closer to the light source it detects an obstacle. • The program mechanism asks for the first desirable behaviour. • Since the cape is an obstacle together with a light, if we use the behaviours1 list we will never find the cape since we will always see the light first and only perform light-seeking behaviour. behaviours1 = [..., 'followLight', 'chargeCape', ... ] vs behaviours2 = [..., 'chargeCape', 'followLight', ... ]
Programming Robot bull_class.py from myro import * from myRobot import * class Bull (MyScribbler): def __init__(self,port,t=0,r=0): MyScribbler.__init__(self,port,t,r) self.foundObstacle = False self.masterMethodsList['chargeCape'] = self.chargeCape self.masterMethodsList['obstacleHit'] = self.obstacleHit self.masterMethodsList['obstacleSensed'] = self.obstacleSensed self.masterMethodsList['followLight'] = self.followLight self.masterMethodsList['dflt'] = self.dflt self.lightThreshhold = 0.4*self.ambient self.debug = True
Programming Robot bull_class.py def __str__(self): retVal = MyScribbler.__str__(self) retVal += '\nThreshhold: %f' % self.lightThreshhold retVal += '\nDebug: %r' % self.debug return retVal # determines if it is time to charge the cape def chargeCape(self): C = getLight('center') lightAhead = (C < self.lightThreshhold) if self.debug: print 'chargeCape: ', 'center < thresh: %r'% lightAhead if lightAhead: [Option,T,R,action] = MyScribbler.obstacleSensed(self) if Option and T == 0: return [Option,1,0,self.doCharge] return [False,self.T,self.R,self.move]
Programming Robot bull_class.py # no cape, but perhaps an obstacle def obstacleSensed(self): [Option,T,R,action] = MyScribbler.obstacleSensed(self) return [Option,T,R,action] # keep going towards the light def followLight(self): L, C, R = getLight() if L < R-200: # straight but adjust slightly left if self.debug: print 'followLight: left' return [True, self.T,0.2,self.move] elif R < L-200: # straight but adjust slightly right if self.debug: print 'followLight: right' return [True, self.T,-0.2,self.move] else: if self.debug: print 'followLight: no' return [False, self.T, self.R, self.move]
Programming Robot bull_class.py def doCharge(self,t,r): if self.debug: print 'doCharge: ' self.backward(0.5,0.3) self.beep(0.5,800) self.move(t,r) wait(1.5) self.move(0,-0.3) wait(2) def flourish(self): self.motors(1,0.8) wait(1.8) #stop() self.rotate(1) wait(1) #stop() self.rotate(-1) wait(1) self.stop() self.rotate(0.4)
Programming Robot bull_class.py def main(): bull = Bull('/dev/rfcomm0',0.6,0) # bull.addToProgramList('chargeCape') # bull.addToProgramList('obstacleHit') # bull.addToProgramList('obstacleSensed') # bull.addToProgramList('followLight') bull.addToProgramList('dflt') print bull.__str__() wait(4) bull.flourish() MyScribbler.main(bull,60) main()