A question about Python Program {4.11a}: fibonacci_action_server.py

Dear all,

some python programming I could not understand.
_feedback = FibonacciFeedback()
_feedback is an instance of FibonacciFeedback()

then the same name is again defiend as an attribute of self()
self._feedback.sequence = []
Does self._feedback heritage FibonacciFeedback()?

Can someone explain how does this work?

thank you very much!

BR,
Kailin

class FibonacciClass(object):
    
  # create messages that are used to publish feedback/result
  _feedback = FibonacciFeedback()
  _result   = FibonacciResult()

  def __init__(self):
    # creates the action server
    self._as = actionlib.SimpleActionServer("fibonacci_as", FibonacciAction, self.goal_callback, False)
    self._as.start()
    
  def goal_callback(self, goal):
    # this callback is called when the action server is called.
    # this is the function that computes the Fibonacci sequence
    # and returns the sequence to the node that called the action server
    
    # helper variables
    r = rospy.Rate(1)
    success = True
    
    # append the seeds for the fibonacci sequence
    self._feedback.sequence = []
    self._feedback.sequence.append(0)
    self._feedback.sequence.append(1)
    
    # publish info to the console for the user
    rospy.loginfo('"fibonacci_as": Executing, creating fibonacci sequence of order %i with seeds %i, %i' % ( goal.order, self._feedback.sequence[0], self._feedback.sequence[1]))
    
    # starts calculating the Fibonacci sequence
    fibonacciOrder = goal.order
    for i in xrange(1, fibonacciOrder):
    
      # check that preempt (cancelation) has not been requested by the action client
      if self._as.is_preempt_requested():
        rospy.loginfo('The goal has been cancelled/preempted')
        # the following line, sets the client in preempted state (goal cancelled)
        self._as.set_preempted()
        success = False
        # we end the calculation of the Fibonacci sequence
        break
      
      # builds the next feedback msg to be sent
      self._feedback.sequence.append(self._feedback.sequence[i] + self._feedback.sequence[i-1])
      # publish the feedback
      self._as.publish_feedback(self._feedback)
      # the sequence is computed at 1 Hz frequency
      r.sleep()
    
    # at this point, either the goal has been achieved (success==true)
    # or the client preempted the goal (success==false)
    # If success, then we publish the final result
    # If not success, we do not publish anything in the result
    if success:
      self._result.sequence = self._feedback.sequence
      rospy.loginfo('Succeeded calculating the Fibonacci of order %i' % fibonacciOrder )
      self._as.set_succeeded(self._result)

A related link is here:

Hi @kailin.tong,

Yes, the _feedback in:

_feedback = FibonacciFeedback()

is the same as the one in:

self._feedback.sequence = []

This is because _feedback is a class variable, which is automatically available to all instances of the class FibonacciClass.

That said, I think it would have been clearer to define both _feedback and _result in def init since we are going to change them in the instance variable. Typically, class variables are used for static values that we don’t intend to change across instances of the class.

Something like this would be clearer.

def __init__(self):
    # create messages that are used to publish feedback/result
    self._feedback = FibonacciFeedback()
    self._result   = FibonacciResult()

    # creates the action server
    self._as = actionlib.SimpleActionServer("fibonacci_as", FibonacciAction, self.goal_callback, False)
    self._as.start()


You can read more about class variables here:

PS: Defining _feedback and _result as class variable as is done in the code works but it has the probably unintended effect of changing these variables across all instances of FibonacciClass whenever it’s changed in any one of them.