tags:

views:

171

answers:

5

So, I have learnt that strings have a center method.

>>> 'a'.center(3)
' a '

Then I have noticed that I can do the same thing using the 'str' object which is a type, since

>>> type(str)
<type 'type'>

Using this 'type' object I could access the string methods like they were static functions.

>>> str.center('a',5)
'  a  '

Alas! This violates the zen of python.

There should be one-- and preferably only one --obvious way to do it.

Even the types of these two methods are different.

>>> type(str.center)
<type 'method_descriptor'>
>>> type('Ni!'.center)
<type 'builtin_function_or_method'>

Now,

  1. Is this an example of how classes in python should be designed?
  2. Why are the types different?
  3. What is a method_descriptor and why should I bother?

Thanks for the answers!

+12  A: 

That's simply how classes in Python work:

class C:
    def method(self, arg):
        print "In C.method, with", arg

o = C()
o.method(1)
C.method(o, 1)
# Prints:
# In C.method, with 1
# In C.method, with 1

When you say o.method(1) you can think of it as a shorthand for C.method(o, 1). A method_descriptor is part of the machinery that makes that work.

RichieHindle
More accurately, when you access o.method, you get a wrapper function that's bound directly to o. This is one of the most useful properties of Python.
Glenn Maynard
+3  A: 

To expand on RichieHindle's answer:

In Python, all methods on a class idiomatically take a "self" parameter. For example:

def method(self, arg): pass

That "self" argument tells Python what instance of the class the method is being called on. When you call a method on a class instance, this is typically passed implicitly for you:

o.method(1)

However, you also have the option of using the class Object, and explicitly passing in the class instance:

C.method(o, 1)

To to use your string example, str.center is a method on the str object:

"hi".center(5)

is equivalent to:

str.center("hi", 5)

You are passing in the str instance "hi" to the object, explicitly doing what's normally implicit.

thedz
+5  A: 

There should be one-- and preferably only one --obvious way to do it.

Philosophically speaking, there is only one obvious way to do it: 'a'.center(3). The fact that there is an unobvious way of calling any method (i.e. the well-explained-by-previous-commentors o.method(x) and Type.method(o, x)) which is useful in many contexts is perfectly in line with the zen of python.

Your homework assignment is to read Guido's Why the Explicit Self Has to Stay.

llimllib
Thanks, very useful : )
A: 

Method descriptor is a normal class with

__ get __, __ set __ and __ del __
methods. When, e.g., __ get __ is called, it is passed 2 or 3 arguments: self, which is the descriptor class itself, inst, which is the caller objet to which the "described" method should be bound and cls, which can be None. To illustrate method_descriptor machinery, let me give this example:




    class Descriptor(object):
        def __init__(self, m):
     self._meth=m

        def __get__(self, inst, cls=None):
         if cls is None: cls=type(inst)
         delattr(cls,self._meth.func_name)
         def _inst_meth(*a):
          return self._meth(inst,*a)
         return _inst_meth

    def instanceonlymethod(f):
        return Descriptor(f)

    class Test(object):
        def meth_1(self,*a):
         return '-'.join(str(i) for i in a)

        @instanceonlymethod
        def meth_2(self,*a):
         return '-'.join(str(i) for i in a)

    t=Test()
    print t.meth_1(2,3,4,5) #returns: 2-3-4-5
    print Test.meth_1(t,1,2,3,4) #returns: 1-2-3-4
    print t.meth_2(2,3,4,5) #returns: 2-3-4-5
    try:
        print Test.meth_2(t,1,2,3,4)
    except Exception, why: #for 2.6, see changes
        print why #returns: type object 'Test' has no attribute 'meth_2'

Now, when you call Test.meth_2(t, 1,2,3,4), it won't work.

Giorgi
+1  A: 
'a'.center(3) == str.center('a',3)

There is only one way to do it.

Sam DeFabbia-Kane