Default method arguments

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Duncan Booth

    #31
    Re: Default method arguments

    Steven D'Aprano wrote:[color=blue]
    > I would like to see _marker put inside the class' scope. That prevents
    > somebody from the outside scope easily passing _marker as an argument
    > to instance.f. It also neatly encapsulates everything A needs within
    > A.[/color]

    Surely that makes it easier for someone outside the scope to pass in
    marker:

    class A(object):
    _marker = []
    def __init__(self, n):
    self.data =n
    def f(self, x = _marker):
    if x is self.__class__. _marker:
    # must use "is" and not "=="
    x = self.data
    print x
    [color=blue][color=green][color=darkred]
    >>> instance = A(5)
    >>> instance.f(inst ance._marker)[/color][/color][/color]
    5

    What you really want is for the marker to exist only in its own little
    universe, but the code for that is even messier:

    class A(object):
    def __init__(self, n):
    self.data =n
    def make_f():
    marker = object()
    def f(self, x = _marker):
    if x is _marker:
    x = self.data
    print x
    return f
    f = make_f()

    [color=blue][color=green][color=darkred]
    >>> instance = A(6)
    >>> instance.f()[/color][/color][/color]
    6

    Comment

    • Steven D'Aprano

      #32
      Re: Default method arguments

      On Wed, 16 Nov 2005 02:59:15 +0000, Bengt Richter wrote:
      [color=blue]
      > On Tue, 15 Nov 2005 23:51:18 +0100, "Fredrik Lundh" <fredrik@python ware.com> wrote:[/color]
      [color=blue][color=green][color=darkred]
      >>> I would like to see _marker put inside the class' scope. That prevents
      >>> somebody from the outside scope easily passing _marker as an argument
      >>> to instance.f.[/color]
      >>
      >>if you don't want people to be able to easily pass _marker as an
      >>argument to the f method, you probably shouldn't use it as the default
      >>value.
      >>[/color]
      > LOL ;-)[/color]

      Ha ha *wink*

      What I meant was to discourage people from treating _marker as just
      another ordinary sort of object, then making pointless bugs
      reports "when you pass _marker as the argument to the f method, it doesn't
      print _marker but instead prints something else."

      My philosophy is, any time you have an object that has a magic meaning
      (e.g. as a sentinel), don't tempt your users to try to use it as if it
      were an ordinary object.


      --
      Steven.

      Comment

      • Steven D'Aprano

        #33
        Re: Default method arguments

        On Wed, 16 Nov 2005 02:59:15 +0000, Bengt Richter wrote:
        [color=blue]
        > On Tue, 15 Nov 2005 23:51:18 +0100, "Fredrik Lundh" <fredrik@python ware.com> wrote:[/color]
        [color=blue][color=green][color=darkred]
        >>> I would like to see _marker put inside the class' scope. That prevents
        >>> somebody from the outside scope easily passing _marker as an argument
        >>> to instance.f.[/color]
        >>
        >>if you don't want people to be able to easily pass _marker as an
        >>argument to the f method, you probably shouldn't use it as the default
        >>value.
        >>[/color]
        > LOL ;-)[/color]

        Ha ha *wink*

        What I meant was to discourage people from treating _marker as just
        another ordinary sort of object, then making pointless bugs
        reports "when you pass _marker as the argument to the f method, it doesn't
        print _marker but instead prints something else."

        My philosophy is, any time you have an object that has a magic meaning
        (e.g. as a sentinel), don't tempt your users to try to use it as if it
        were an ordinary object.


        --
        Steven.

        Comment

        • Steven D'Aprano

          #34
          Re: Default method arguments

          On Wed, 16 Nov 2005 09:48:47 +0000, Duncan Booth wrote:
          [color=blue]
          > Steven D'Aprano wrote:[color=green]
          >> I would like to see _marker put inside the class' scope. That prevents
          >> somebody from the outside scope easily passing _marker as an argument
          >> to instance.f. It also neatly encapsulates everything A needs within
          >> A.[/color]
          >
          > Surely that makes it easier for someone outside the scope to pass in
          > marker:[/color]
          [color=blue][color=green][color=darkred]
          > >>> instance = A(5)
          > >>> instance.f(inst ance._marker)[/color][/color]
          > 5[/color]

          Sure, but they have to explicitly qualify marker with the instance. If
          they want to do that, I'm not going to stop them. But I'm trying to avoid
          tempting them from doing this:

          instance.f(_mar ker)

          and then complain that it doesn't print _marker.

          In other words, I don't want to take away their ability to shoot
          themselves in the foot, but I want them to have to *think about it* before
          doing so.


          --
          Steven.

          Comment

          • Steven D'Aprano

            #35
            Re: Default method arguments

            On Wed, 16 Nov 2005 09:48:47 +0000, Duncan Booth wrote:
            [color=blue]
            > Steven D'Aprano wrote:[color=green]
            >> I would like to see _marker put inside the class' scope. That prevents
            >> somebody from the outside scope easily passing _marker as an argument
            >> to instance.f. It also neatly encapsulates everything A needs within
            >> A.[/color]
            >
            > Surely that makes it easier for someone outside the scope to pass in
            > marker:[/color]
            [color=blue][color=green][color=darkred]
            > >>> instance = A(5)
            > >>> instance.f(inst ance._marker)[/color][/color]
            > 5[/color]

            Sure, but they have to explicitly qualify marker with the instance. If
            they want to do that, I'm not going to stop them. But I'm trying to avoid
            tempting them from doing this:

            instance.f(_mar ker)

            and then complain that it doesn't print _marker.

            In other words, I don't want to take away their ability to shoot
            themselves in the foot, but I want them to have to *think about it* before
            doing so.


            --
            Steven.

            Comment

            • Fredrik Lundh

              #36
              Re: Default method arguments

              Duncan Booth wrote:
              [color=blue]
              > What you really want is for the marker to exist only in its own little
              > universe, but the code for that is even messier:
              >
              > class A(object):
              > def __init__(self, n):
              > self.data =n
              > def make_f():
              > marker = object()
              > def f(self, x = _marker):[/color]

              NameError: global name '_marker' is not defined
              [color=blue]
              > if x is _marker:
              > x = self.data
              > print x
              > return f
              > f = make_f()
              >[color=green][color=darkred]
              >>>> instance = A(6)
              >>>> instance.f()[/color][/color]
              > 6[/color]

              in another universe, perhaps, but not very far away:
              [color=blue][color=green][color=darkred]
              >>> instance.f.im_f unc.func_defaul ts[0][/color][/color][/color]
              <object object at 0x009EC438>
              [color=blue][color=green][color=darkred]
              >>> inspect.getargs pec(A.f)[/color][/color][/color]
              (['self', 'x'], None, None, (<object object at 0x009EC438>,))

              </F>



              Comment

              • Fredrik Lundh

                #37
                Re: Default method arguments

                Duncan Booth wrote:
                [color=blue]
                > What you really want is for the marker to exist only in its own little
                > universe, but the code for that is even messier:
                >
                > class A(object):
                > def __init__(self, n):
                > self.data =n
                > def make_f():
                > marker = object()
                > def f(self, x = _marker):[/color]

                NameError: global name '_marker' is not defined
                [color=blue]
                > if x is _marker:
                > x = self.data
                > print x
                > return f
                > f = make_f()
                >[color=green][color=darkred]
                >>>> instance = A(6)
                >>>> instance.f()[/color][/color]
                > 6[/color]

                in another universe, perhaps, but not very far away:
                [color=blue][color=green][color=darkred]
                >>> instance.f.im_f unc.func_defaul ts[0][/color][/color][/color]
                <object object at 0x009EC438>
                [color=blue][color=green][color=darkred]
                >>> inspect.getargs pec(A.f)[/color][/color][/color]
                (['self', 'x'], None, None, (<object object at 0x009EC438>,))

                </F>



                Comment

                • Duncan Booth

                  #38
                  Re: Default method arguments

                  Steven D'Aprano wrote:
                  [color=blue]
                  > My philosophy is, any time you have an object that has a magic meaning
                  > (e.g. as a sentinel), don't tempt your users to try to use it as if it
                  > were an ordinary object.[/color]

                  In that case the simplest thing is to give _marker a more appropriate name
                  such as '_use_late_boun d_default_for_a rgument' or '_foot_gun_aim_ fire'.

                  Comment

                  • Duncan Booth

                    #39
                    Re: Default method arguments

                    Steven D'Aprano wrote:
                    [color=blue]
                    > My philosophy is, any time you have an object that has a magic meaning
                    > (e.g. as a sentinel), don't tempt your users to try to use it as if it
                    > were an ordinary object.[/color]

                    In that case the simplest thing is to give _marker a more appropriate name
                    such as '_use_late_boun d_default_for_a rgument' or '_foot_gun_aim_ fire'.

                    Comment

                    • Martin Miller

                      #40
                      Re: Default method arguments

                      Mike Meyer wrote, in part::[color=blue]
                      > "Gregory Petrosyan" <gregory.petros yan@gmail.com> writes:
                      > ...[color=green]
                      > > 2) Is 'foo.s = n' a correct solution? It seems to be a little more
                      > > elegant. (I tested it, and it worked well)[/color]
                      >
                      > It's basically the same solution. You're replacing binding a variable
                      > with mutating an object bound to a name in an outer scope. In one case
                      > the container is named s and is a list that you're setting an element
                      > of. In the other case, the container is named foo and is an object
                      > that you're setting an attribute on.[/color]

                      Well, perhaps the same in the sense of name binding, but there's a
                      subtle difference in replacing the 's = [n]' with 'foo.s = n'. Namely
                      that in the former case (with the essay's original code) a separate
                      container is created when foo() is first called and is what is used in
                      subsequent calls to the function returned. Whereas in the latter case
                      where the foo object itself is used as the container, there's only a
                      single container used by all returned objects -- which would cause
                      problems if you try accumulating two or more different totals
                      simultaneously.

                      Here's a very contrived test case which illustrates the point I'm
                      trying to make:

                      def foo(n):
                      foo.s = n
                      def bar(i):
                      foo.s += i
                      return foo.s
                      return bar

                      a1 = foo(0)
                      a2 = foo(0)
                      print "before a1(0):", a1(0)
                      print "before a2(0):", a2(0)
                      a1(1)
                      a2(1)
                      print "after a1(0):", a1(0)
                      print "after a2(0):", a2(0)
                      [color=blue][color=green][color=darkred]
                      >>>> outputs[/color][/color][/color]
                      before a1(0): 0
                      before a2(0): 0
                      after a1(0): 2
                      after a2(0): 2

                      Notice that it even though each was only incremented by 1 once, they
                      interacted, and show the effects of two calls. This doesn't happen in
                      in Paul Graham's version, where the two 'after' calls would correctly
                      retrun a value of 1.

                      -Martin

                      Comment

                      • Martin Miller

                        #41
                        Re: Default method arguments

                        Mike Meyer wrote, in part::[color=blue]
                        > "Gregory Petrosyan" <gregory.petros yan@gmail.com> writes:
                        > ...[color=green]
                        > > 2) Is 'foo.s = n' a correct solution? It seems to be a little more
                        > > elegant. (I tested it, and it worked well)[/color]
                        >
                        > It's basically the same solution. You're replacing binding a variable
                        > with mutating an object bound to a name in an outer scope. In one case
                        > the container is named s and is a list that you're setting an element
                        > of. In the other case, the container is named foo and is an object
                        > that you're setting an attribute on.[/color]

                        Well, perhaps the same in the sense of name binding, but there's a
                        subtle difference in replacing the 's = [n]' with 'foo.s = n'. Namely
                        that in the former case (with the essay's original code) a separate
                        container is created when foo() is first called and is what is used in
                        subsequent calls to the function returned. Whereas in the latter case
                        where the foo object itself is used as the container, there's only a
                        single container used by all returned objects -- which would cause
                        problems if you try accumulating two or more different totals
                        simultaneously.

                        Here's a very contrived test case which illustrates the point I'm
                        trying to make:

                        def foo(n):
                        foo.s = n
                        def bar(i):
                        foo.s += i
                        return foo.s
                        return bar

                        a1 = foo(0)
                        a2 = foo(0)
                        print "before a1(0):", a1(0)
                        print "before a2(0):", a2(0)
                        a1(1)
                        a2(1)
                        print "after a1(0):", a1(0)
                        print "after a2(0):", a2(0)
                        [color=blue][color=green][color=darkred]
                        >>>> outputs[/color][/color][/color]
                        before a1(0): 0
                        before a2(0): 0
                        after a1(0): 2
                        after a2(0): 2

                        Notice that it even though each was only incremented by 1 once, they
                        interacted, and show the effects of two calls. This doesn't happen in
                        in Paul Graham's version, where the two 'after' calls would correctly
                        retrun a value of 1.

                        -Martin

                        Comment

                        • Gregory Petrosyan

                          #42
                          Re: Default method arguments

                          Thanks Martin, you are right.

                          Comment

                          • Gregory Petrosyan

                            #43
                            Re: Default method arguments

                            Thanks Martin, you are right.

                            Comment

                            • Peter Otten

                              #44
                              Re: Default method arguments

                              Martin Miller wrote:
                              [color=blue]
                              > Well, perhaps the same in the sense of name binding, but there's a
                              > subtle difference in replacing the 's = [n]'  with 'foo.s = n'.  Namely
                              > that in the former case (with the essay's original code) a separate
                              > container is created when foo() is first called and is what is used in
                              > subsequent calls to the function returned.  Whereas in the latter case
                              > where the foo object itself is used as the container, there's only a
                              > single container used by all returned objects -- which would cause
                              > problems if you try accumulating two or more different totals
                              > simultaneously.[/color]

                              [snip example using the outer foo() as a container]

                              You can easily get a unique container using the function attribute style, to
                              -- just use the inner function bar():
                              [color=blue][color=green][color=darkred]
                              >>> def foo(n):[/color][/color][/color]
                              .... def bar(i):
                              .... bar.i += 1
                              .... re
                              ....[color=blue][color=green][color=darkred]
                              >>>
                              >>> def foo(n):[/color][/color][/color]
                              .... def bar(i):
                              .... bar.s += i
                              .... return bar.s
                              .... bar.s = n
                              .... return bar
                              ....[color=blue][color=green][color=darkred]
                              >>> a1 = foo(0)
                              >>> a2 = foo(0)
                              >>> a1(0), a2(0)[/color][/color][/color]
                              (0, 0)[color=blue][color=green][color=darkred]
                              >>> a1(1), a2(1)[/color][/color][/color]
                              (1, 1)

                              Peter

                              Comment

                              • Peter Otten

                                #45
                                Re: Default method arguments

                                Martin Miller wrote:
                                [color=blue]
                                > Well, perhaps the same in the sense of name binding, but there's a
                                > subtle difference in replacing the 's = [n]'  with 'foo.s = n'.  Namely
                                > that in the former case (with the essay's original code) a separate
                                > container is created when foo() is first called and is what is used in
                                > subsequent calls to the function returned.  Whereas in the latter case
                                > where the foo object itself is used as the container, there's only a
                                > single container used by all returned objects -- which would cause
                                > problems if you try accumulating two or more different totals
                                > simultaneously.[/color]

                                [snip example using the outer foo() as a container]

                                You can easily get a unique container using the function attribute style, to
                                -- just use the inner function bar():
                                [color=blue][color=green][color=darkred]
                                >>> def foo(n):[/color][/color][/color]
                                .... def bar(i):
                                .... bar.i += 1
                                .... re
                                ....[color=blue][color=green][color=darkred]
                                >>>
                                >>> def foo(n):[/color][/color][/color]
                                .... def bar(i):
                                .... bar.s += i
                                .... return bar.s
                                .... bar.s = n
                                .... return bar
                                ....[color=blue][color=green][color=darkred]
                                >>> a1 = foo(0)
                                >>> a2 = foo(0)
                                >>> a1(0), a2(0)[/color][/color][/color]
                                (0, 0)[color=blue][color=green][color=darkred]
                                >>> a1(1), a2(1)[/color][/color][/color]
                                (1, 1)

                                Peter

                                Comment

                                Working...