Where do nested functions live?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Steven D'Aprano

    #1

    Where do nested functions live?

    I defined a nested function:

    def foo():
    def bar():
    return "bar"
    return "foo " + bar()

    which works. Knowing how Python loves namespaces, I thought I could do
    this:
    >>foo.bar()
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    AttributeError: 'function' object has no attribute 'bar'

    but it doesn't work as I expected.


    where do nested functions live? How can you access them, for example, to
    read their doc strings?



    --
    Steven.

  • Fredrik Lundh

    #2
    Re: Where do nested functions live?

    Steven D'Aprano wrote:
    I defined a nested function:
    >
    def foo():
    def bar():
    return "bar"
    return "foo " + bar()
    >
    which works. Knowing how Python loves namespaces, I thought I could do
    this:
    >
    >>>foo.bar()
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    AttributeError: 'function' object has no attribute 'bar'
    >
    but it doesn't work as I expected.
    >
    where do nested functions live?
    in the local variable of an executing function, just like the variable
    "bar" in the following function:

    def foo():
    bar = "who am I? where do I live?"

    (yes, an inner function is *created* every time you execute the outer
    function. but it's created from prefabricated parts, so that's not a
    very expensive process).

    </F>

    Comment

    • Ben Finney

      #3
      Re: Where do nested functions live?

      "Steven D'Aprano" <steve@REMOVE.T HIS.cybersource .com.auwrites:
      I defined a nested function:
      >
      def foo():
      def bar():
      return "bar"
      return "foo " + bar()
      >
      which works. Knowing how Python loves namespaces, I thought I could
      do this:
      >
      >foo.bar()
      Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      AttributeError: 'function' object has no attribute 'bar'
      >
      but it doesn't work as I expected.
      Functions don't get attributes automatically added to them the way
      class do. The main exception is the '__doc__' attribute, referring to
      the doc string value.
      where do nested functions live?
      They live inside the scope of the function. Inaccessible from outside,
      which is as it should be. Functions interact with the outside world
      through a tightly-defined interface, defined by their input parameters
      and their return value.
      How can you access them, for example, to read their doc strings?
      If you want something that can be called *and* define its attributes,
      you want something more complex than the default function type. Define
      a class that has a '__call__' attribute, make an instance of that, and
      you'll be able to access attributes and call it like a function.

      --
      \ "Writing a book is like washing an elephant: there no good |
      `\ place to begin or end, and it's hard to keep track of what |
      _o__) you've already covered." -- Anonymous |
      Ben Finney

      Comment

      • Steve Holden

        #4
        Re: Where do nested functions live?

        Steven D'Aprano wrote:
        I defined a nested function:
        >
        def foo():
        def bar():
        return "bar"
        return "foo " + bar()
        >
        which works. Knowing how Python loves namespaces, I thought I could do
        this:
        >
        >
        >>>>foo.bar()
        >
        Traceback (most recent call last):
        File "<stdin>", line 1, in ?
        AttributeError: 'function' object has no attribute 'bar'
        >
        but it doesn't work as I expected.
        >
        >
        where do nested functions live? How can you access them, for example, to
        read their doc strings?
        >
        >
        >
        It doesn't "live" anywhere: if I wrote the function

        def foo():
        locvar = 23
        return locvar

        would you expect to be able to access foo.locvar?

        It's exactly the same thing: the statement

        def bar():

        isn't executed until the foo() function is called, and its execution
        binds the name bar in foo's local namespace to the function that is defined.

        regards
        Steve

        --
        Steve Holden +44 150 684 7255 +1 800 494 3119
        Holden Web LLC/Ltd http://www.holdenweb.com
        Skype: holdenweb http://holdenweb.blogspot.com
        Recent Ramblings http://del.icio.us/steve.holden

        Comment

        • Fredrik Lundh

          #5
          Re: Where do nested functions live?

          Ben Finney wrote:
          If you want something that can be called *and* define its attributes,
          you want something more complex than the default function type. Define
          a class that has a '__call__' attribute, make an instance of that, and
          you'll be able to access attributes and call it like a function.
          I turned Steven's question and portions of the answers into a Python FAQ
          entry:



          Hope none of the contributors mind.

          </F>

          Comment

          • Steven D'Aprano

            #6
            Re: Where do nested functions live?

            On Sat, 28 Oct 2006 09:59:29 +0200, Fredrik Lundh wrote:
            >where do nested functions live?
            >
            in the local variable of an executing function, just like the variable
            "bar" in the following function:
            >
            def foo():
            bar = "who am I? where do I live?"
            >
            (yes, an inner function is *created* every time you execute the outer
            function. but it's created from prefabricated parts, so that's not a
            very expensive process).
            Does this mean I'm wasting my time writing doc strings for nested
            functions? If there is no way of accessing them externally, should I make
            them mere # comments?


            --
            Steven.

            Comment

            • Marc 'BlackJack' Rintsch

              #7
              Re: Where do nested functions live?

              In <pan.2006.10.28 .09.27.23.18636 8@REMOVE.THIS.c ybersource.com. au>, Steven
              D'Aprano wrote:
              Does this mean I'm wasting my time writing doc strings for nested
              functions? If there is no way of accessing them externally, should I make
              them mere # comments?
              Whats the difference in "wasted time" between using """ or # as delimiters
              for the explanation what the function is doing!? Or do you ask if you
              should not document inner functions at all? Someone might read the source
              and be very happy to find docs(trings) there.

              And of course there are inner functions that are returned from the outer
              one and on those objects it is possible to inspect and read the docs.

              Ciao,
              Marc 'BlackJack' Rintsch

              Comment

              • Andrea Griffini

                #8
                Re: Where do nested functions live?

                Fredrik Lundh wrote:
                Ben Finney wrote:
                >
                >If you want something that can be called *and* define its attributes,
                >you want something more complex than the default function type. Define
                >a class that has a '__call__' attribute, make an instance of that, and
                >you'll be able to access attributes and call it like a function.
                >
                I turned Steven's question and portions of the answers into a Python FAQ
                entry:
                >

                >
                Hope none of the contributors mind.
                I'd add that while in some respect "def x" is like
                an assigment to x ...
                >>def f():
                global g
                def g():
                return "Yoo!"
                >>f()
                >>g()
                'Yoo!'

                in some other respect (unfortunately) it's not a regular assignment
                >>x = object()
                >>def x.g():
                SyntaxError: invalid syntax
                >>>
                Andrea

                Comment

                • Frederic Rentsch

                  #9
                  Re: Where do nested functions live?

                  Fredrik Lundh wrote:
                  Steven D'Aprano wrote:
                  >
                  >
                  >I defined a nested function:
                  >>
                  >def foo():
                  > def bar():
                  > return "bar"
                  > return "foo " + bar()
                  >>
                  >which works. Knowing how Python loves namespaces, I thought I could do
                  >this:
                  >>
                  >>
                  >>>>foo.bar()
                  >>>>>
                  >Traceback (most recent call last):
                  > File "<stdin>", line 1, in ?
                  >AttributeError : 'function' object has no attribute 'bar'
                  >>
                  >but it doesn't work as I expected.
                  >>
                  >where do nested functions live?
                  >>
                  >
                  in the local variable of an executing function, just like the variable
                  "bar" in the following function:
                  >
                  def foo():
                  bar = "who am I? where do I live?"
                  >
                  (yes, an inner function is *created* every time you execute the outer
                  function. but it's created from prefabricated parts, so that's not a
                  very expensive process).
                  >
                  </F>
                  >
                  >
                  If I may turn the issue around, I could see a need for an inner function
                  to be able to access the variables of the outer function, the same way a
                  function can access globals. Why? Because inner functions serve to
                  de-multiply code segments one would otherwise need to repeat or to
                  provide a code segment with a name suggestive of its function. In either
                  case the code segment moved to the inner function loses contact with its
                  environment, which rather mitigates its benefit.
                  If I have an inner function that operates on quite a few outer
                  variables it would be both convenient and surely more efficient, if I
                  could start the inner function with a declaration analogous to a
                  declaration of globals, listing the outer variables which I wish to
                  remain writable directly.
                  I guess I could put the outer variables into a list as argument to
                  the inner function. But while this relieves the inner function of
                  returning lots of values it burdens the outer function with handling the
                  list which it wouldn't otherwise need.

                  Frederic


                  Comment

                  • Diez B. Roggisch

                    #10
                    Re: Where do nested functions live?

                    If I may turn the issue around, I could see a need for an inner function
                    to be able to access the variables of the outer function, the same way a
                    function can access globals. Why? Because inner functions serve to
                    de-multiply code segments one would otherwise need to repeat or to
                    provide a code segment with a name suggestive of its function. In either
                    case the code segment moved to the inner function loses contact with its
                    environment, which rather mitigates its benefit.
                    Maybe I'm dense here, but where is your point? Python has nested lexical
                    scoping, and while some people complain about it's actual semantics, it
                    works very well:

                    def outer():
                    outer_var = 10
                    def inner():
                    return outer_var * 20
                    return inner

                    print outer()()



                    Diez

                    Comment

                    • Frederic Rentsch

                      #11
                      Re: Where do nested functions live?

                      Diez B. Roggisch wrote:
                      >If I may turn the issue around, I could see a need for an inner function
                      >to be able to access the variables of the outer function, the same way a
                      >function can access globals. Why? Because inner functions serve to
                      >de-multiply code segments one would otherwise need to repeat or to
                      >provide a code segment with a name suggestive of its function. In either
                      >case the code segment moved to the inner function loses contact with its
                      >environment, which rather mitigates its benefit.
                      >>
                      >
                      Maybe I'm dense here, but where is your point? Python has nested lexical
                      scoping, and while some people complain about it's actual semantics, it
                      works very well:
                      >
                      def outer():
                      outer_var = 10
                      def inner():
                      return outer_var * 20
                      return inner
                      >
                      print outer()()
                      >
                      >
                      >
                      Diez
                      >
                      My point is that an inner function operating on existing outer variables
                      should be allowed to do so directly. Your example in its simplicity is
                      unproblematic. Let us consider a case where several outer variables need
                      to be changed:

                      weeks = days = hours = minutes = seconds = 0
                      mseconds = 0.0

                      (code)

                      # add interval in milliseconds
                      have_ms = ((((((((((weeks * 7) + days) * 24) + hours) * 60) +
                      minutes) * 60) + seconds) * 1000) + mseconds)
                      new_ms = have_ms + interval_ms
                      # reconvert
                      s = new_ms / 1000.0
                      s = int (s)
                      mseconds = new_ms - s * 1000
                      m, seconds = divmod (s, 60)
                      h, minutes = divmod (m, 60)
                      d, hours = divmod (h, 24)
                      weeks, days = divmod (d, 7)

                      (more code)

                      At some later point I need to increment my units some more and probably
                      will again a number of times. Clearly this has to go into a function. I
                      make it an inner function, because the scope of its service happens to
                      be local to the function in which it comes to live. It operates on
                      existing variables of what is now its containing function.

                      def increment_time (interval_ms):
                      have_ms = ((((((((((weeks * 7) + days) * 24) + hours) * 60) +
                      minutes) * 60) + seconds) * 1000) + mseconds)
                      new_ms = have_ms + interval_ms
                      # reconvert
                      s = new_ms / 1000.0
                      s = int (s)
                      ms -= s * 1000 # Was mseconds = new_ms - s * 1000
                      m, s = divmod (s, 60) # Was m, seconds = divmod (s, 60)
                      h, m = divmod (m, 60) # Was h, minutes = divmod (m, 60)
                      d, h = divmod (h, 24) # Was d, hours = divmod (h, 24)
                      w, d = divmod (d, 7) # Was weeks, days = divmod (d, 7)
                      return w, d, h, m, s, ms

                      Functionizing I must change the names of the outer variables. Assigning
                      to them would make them local, their outer namesakes would become
                      invisible and I'd have to pass them all as arguments. Simpler is
                      changing assignees names, retaining visibility and therefore not having
                      to pass arguments. In either case I have to return the result for
                      reassignment by the call.

                      weeks, days, hours, minutes, seconds, milliseconds = increment_time
                      (msec)

                      This is a little like a shop where the mechanics have to get their tools
                      and work pieces from the manager and hand them back to him when they're
                      done. The following two examples are illustrations of my point. They are
                      not proposals for 'improvement' of a language I would not presume to
                      improve.

                      def increment_time (interval_ms):
                      outer weeks, days, hours, minutes, seconds, mseconds # 'outer'
                      akin to 'global'
                      (...)
                      mseconds = new_ms - s * 1000 # Assignee remains outer
                      m, seconds = divmod (s, 60)
                      h, minutes = divmod (m, 60)
                      d, hours = divmod (h, 24)
                      weeks, days = divmod (d, 7) # No return necessary

                      The call would now be:

                      increment_time (msec) # No reassignment necessary


                      Hope this makes sense

                      Frederic


                      Comment

                      • Fredrik Lundh

                        #12
                        Re: Where do nested functions live?

                        Frederic Rentsch wrote:
                        At some later point I need to increment my units some more and probably
                        will again a number of times. Clearly this has to go into a function.
                        since Python is an object-based language, clearly you could make your
                        counter into a self-contained object instead of writing endless amounts
                        of code and wasting CPU cycles by storing what's really a *single* state
                        in a whole bunch of separate variables.

                        in your specific example, you can even use an existing object:

                        t = datetime.dateti me.now()

                        # increment
                        t += datetime.timede lta(millisecond s=msec)

                        print t.timetuple() # get the contents

                        if you're doing this so much that it's worth streamlining the timedelta
                        addition, you can wrap the datetime instance in a trivial class, and do

                        t += 1500 # milliseconds

                        when you need to increment the counter.
                        This is a little like a shop where the mechanics have to get their
                        tools and work pieces from the manager and hand them back to him when
                        they're done.
                        that could of course be because when he was free to use whatever tool he
                        wanted, he always used a crowbar, because he hadn't really gotten around
                        to read that "tool kit for dummies" book.

                        </F>

                        Comment

                        • Frederic Rentsch

                          #13
                          Re: Where do nested functions live?

                          Fredrik Lundh wrote:
                          Frederic Rentsch wrote:
                          >
                          >
                          >At some later point I need to increment my units some more and probably
                          >will again a number of times. Clearly this has to go into a function.
                          >>
                          >
                          since Python is an object-based language, clearly you could make your
                          counter into a self-contained object instead of writing endless amounts
                          of code and wasting CPU cycles by storing what's really a *single* state
                          in a whole bunch of separate variables.
                          >
                          This is surely a good point I'll have to think about.
                          in your specific example, you can even use an existing object:
                          >
                          Of course. But my example wasn't about time. It was about the situation
                          t = datetime.dateti me.now()
                          >
                          # increment
                          t += datetime.timede lta(millisecond s=msec)
                          >
                          print t.timetuple() # get the contents
                          >
                          if you're doing this so much that it's worth streamlining the timedelta
                          addition, you can wrap the datetime instance in a trivial class, and do
                          >
                          t += 1500 # milliseconds
                          >
                          when you need to increment the counter.
                          >
                          This is a little like a shop where the mechanics have to get their
                          tools and work pieces from the manager and hand them back to him when
                          they're done.
                          >
                          that could of course be because when he was free to use whatever tool he
                          wanted, he always used a crowbar, because he hadn't really gotten around
                          to read that "tool kit for dummies" book.
                          >
                          No mechanic always uses a crowbar. He'd use it just once--with the same
                          employer.
                          </F>
                          >
                          >

                          Comment

                          • Rob Williscroft

                            #14
                            Re: Where do nested functions live?

                            Frederic Rentsch wrote in news:mailman.14 28.1162113628.1 1739.python-
                            list@python.org in comp.lang.pytho n:
                            def increment_time (interval_ms):
                            outer weeks, days, hours, minutes, seconds, mseconds # 'outer'
                            akin to 'global'
                            (...)
                            mseconds = new_ms - s * 1000 # Assignee remains outer
                            m, seconds = divmod (s, 60)
                            h, minutes = divmod (m, 60)
                            d, hours = divmod (h, 24)
                            weeks, days = divmod (d, 7) # No return necessary
                            >
                            The call would now be:
                            >
                            increment_time (msec) # No reassignment necessary
                            >
                            >
                            Hope this makes sense
                            Yes it does, but I prefer explicit in this case:

                            def whatever( new_ms ):
                            class namespace( object ):
                            pass
                            scope = namespace()

                            def inner():
                            scope.mseconds = new_ms - s * 1000
                            m, scope.seconds = divmod (s, 60)
                            h, scope.minutes = divmod (m, 60)
                            d, scope.hours = divmod (h, 24)
                            scope.weeks, scope.days = divmod (d, 7)

                            The only thing I find anoying is that I can't write:

                            scope = object()

                            Additionally if appropriate I can refactor further:

                            def whatever( new_ms ):
                            class namespace( object ):
                            def inner( scope ):
                            scope.mseconds = new_ms - s * 1000
                            m, scope.seconds = divmod (s, 60)
                            h, scope.minutes = divmod (m, 60)
                            d, scope.hours = divmod (h, 24)
                            scope.weeks, scope.days = divmod (d, 7)

                            scope = namespace()
                            scope.inner()

                            In short I think an "outer" keyword (or whatever it gets called)
                            will just add another way of doing something I can already do,
                            and potentially makes further refactoring harder.

                            Thats -2 import-this points already.

                            Rob.
                            --

                            Comment

                            • Frederic Rentsch

                              #15
                              Re: Where do nested functions live?

                              Rob Williscroft wrote:
                              Frederic Rentsch wrote in news:mailman.14 28.1162113628.1 1739.python-
                              list@python.org in comp.lang.pytho n:
                              >
                              >
                              > def increment_time (interval_ms):
                              > outer weeks, days, hours, minutes, seconds, mseconds # 'outer'
                              >akin to 'global'
                              > (...)
                              > mseconds = new_ms - s * 1000 # Assignee remains outer
                              > m, seconds = divmod (s, 60)
                              > h, minutes = divmod (m, 60)
                              > d, hours = divmod (h, 24)
                              > weeks, days = divmod (d, 7) # No return necessary
                              >>
                              >The call would now be:
                              >>
                              > increment_time (msec) # No reassignment necessary
                              >>
                              >>
                              >Hope this makes sense
                              >>
                              >
                              Yes it does, but I prefer explicit in this case:
                              >
                              def whatever( new_ms ):
                              class namespace( object ):
                              pass
                              scope = namespace()
                              >
                              def inner():
                              scope.mseconds = new_ms - s * 1000
                              m, scope.seconds = divmod (s, 60)
                              h, scope.minutes = divmod (m, 60)
                              d, scope.hours = divmod (h, 24)
                              scope.weeks, scope.days = divmod (d, 7)
                              >
                              >
                              This is interesting. I am not too familiar with this way of using
                              objects. Actually it isn't all that different from a list, because a
                              list is also an object. But this way it's attribute names instead of
                              list indexes which is certainly easier to work with. Very good!
                              The only thing I find anoying is that I can't write:
                              >
                              scope = object()
                              >
                              Additionally if appropriate I can refactor further:
                              >
                              def whatever( new_ms ):
                              class namespace( object ):
                              def inner( scope ):
                              scope.mseconds = new_ms - s * 1000
                              m, scope.seconds = divmod (s, 60)
                              h, scope.minutes = divmod (m, 60)
                              d, scope.hours = divmod (h, 24)
                              scope.weeks, scope.days = divmod (d, 7)
                              >
                              scope = namespace()
                              scope.inner()
                              >
                              In short I think an "outer" keyword (or whatever it gets called)
                              will just add another way of doing something I can already do,
                              and potentially makes further refactoring harder.
                              >
                              >
                              Here I'm lost. What's the advantage of this? It looks more convoluted.
                              And speaking of convoluted, what about efficiency? There is much talk of
                              efficiency on this forum. I (crudely) benchmark your previous example
                              approximately three times slower than a simple inner function taking and
                              returning three parameters. It was actually the aspect of increased
                              efficiency that prompted me to play with the idea of allowing direct
                              outer writes.
                              Thats -2 import-this points already.
                              >
                              >
                              Which ones are the two?
                              Rob.
                              >
                              Frederic


                              Comment

                              Working...