[Tutor] (no subject)

Kirby Urner urnerk@qwest.net
Wed, 06 Mar 2002 09:52:07 -0800


>
>I'm wondering why with some functions I have to type the
>function then the variable I want that function to act on
>in parentheses after the function (e.g. len(a)) and other
>functions I have to type the variable then a period then
>the function (e.g. a.strip()).

It's a good question.

Top-level functions, outside of any classes or other
functions (but inside modules), may be accessed directly,
whereas another kind of function, called a method, lives
internally to a class definition.  The difference in
code looks like:

def func(arg):
    # do stuff
    return whatever

vs.

class Myclass:

    def func(self,arg):
       # do stuff
       return whatever

In the first case, you could import the function and
go func(a), but in the second case, you need to create
an instance of Myclass and invoke its method:

    oBject = Myclass()
    oBject.func(a)

Now, in the case of strings, e.g. 'abcde', the earlier
Pythons had this model where you'd import a string module
to do stuff with strings, like strip('abcd  ').  You can
still do that.  However, the decision was made to make
most string functions invokable as methods on string
instances as well, e.g.

  >>> 'abcd '.strip()
  'abcd'

-- which looks kinda weird at first (you just have to
remember that 'abcd ' is creating a string object, and
inheriting all the functionality this entails).

To see what kind of functionality you're inheriting
by using a primitive data type, like a sequence, map,
string or number, just go dir(3) or dir({}) and see all
the methods, e.g.:

 >>> dir(3)
['__abs__', '__add__', '__and__', '__class__', '__cmp__',
'__coerce__', '__delattr__', '__div__', '__divmod__',
'__float__', '__floordiv__', '__getattribute__', '__hash__',
'__hex__', '__init__', '__int__', '__invert__', '__long__',
'__lshift__', '__mod__', '__mul__', '__neg__', '__new__',
'__nonzero__', '__oct__', '__or__', '__pos__', '__pow__',
'__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__',
'__repr__', '__rfloordiv__', '__rlshift__', '__rmod__',
'__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__',
'__rsub__', '__rtruediv__', '__rxor__', '__setattr__',
'__str__', '__sub__', '__truediv__', '__xor__']

That means another way to negate 3 would be to invoke the
__neg__ method associated with all integers:

 >>> 3 .__neg__()
-3

(note the period separated from the number by whitespace, so
that the parser doesn't confuse this as a decimal point --
a trick I learned awhile back from one of the gurus right
here on tutor@python.org).

Finally, you can write top-level functions to invoke object
methods, if you prefer that syntax.  In other words, if
object foo has method bar (as in foo.bar()) then you can
write:

   def bar(foo):
       return foo.bar()

More concretely:

   >>> def strip(somestring):
           return somestring.strip()

   >>> strip('abc  ')
   'abc'

Except in the case of strings you might as well just
go

from string import strip

if you want the top-level function syntax.  But in other
situations, doing a top-level functional equivalent of
an instance method invocation can be useful, e.g. if
you don't like v1.cross(v2) for the cross product v1 x v2
(vector algebra), just write:

    def cross(v1,v2):
       return v1.cross(v2)

etc. etc.

Kirby

"You acknowledge that Software is not designed, licensed
or intended for use in the design, construction, operation
or maintenance of any nuclear facility." -- Java license
agreement