Difference between 'function' and 'method'

Bruno Desthuilliers bruno.42.desthuilliers at wtf.websiteburo.oops.com
Tue Mar 4 06:27:58 EST 2008


?? a écrit :
> Howdy everyone,
> 
>      This is a big problem puzzles me for a long time. The core question is:
> How to dynamically create methods on a class or an instance?

class Foo(object):
    pass

def bar(self, arg):
    print "in bar : self == % - arg == %s" % (self, str(arg))

def baaz(self, arg):
    print "in baaz : self == % - arg == %s" % (self, str(arg))


f = Foo()

# adding bar as a method to class Foo
Foo.bar = bar

# f now can use bar:
f.bar(42)

# as well as new instances of Foo, of course
g = Foo()
g.bar()

# adding baaz as a method to f, the old way:
import new
f.baaz = new.instancemethod(baaz, f, type(f))

f.baaz()

# adding baaz as a method to g, the other way:
g.baaz = baaz.__get__(g, type(g))

g.baaz()


> Let me state it step by step.
> 1.
> def gunc(self):
>     pass
> class A(object):
>     def func(self):
>         pass
> a = A()
> a.func   # gives "bound method", type is "instancemethod"
> A.func  # gives "unbound method", type is "instancemethod"
> gunc    # gives "function", type if "function"
> 
> # ?? Does this line attach a method to instance?  ... I don't think so.
> a.gunc = gunc

It doesn't.


> I found stardard library 'new' may help. Is that right?

cf above. If you work with old-style classes, you'll need 
new.instancemethod.

> 2.
> a = A()  # instance of old class A
> # Do attach a new method to class A...
> b = A()  # instance of new class A
> Does "a" can get the new method automatically?

Yes. In Python, a class is itself an object, and is an attribute of it's 
instances (instance.__class__). Names are resolved at runtime, and 
attributes not found in the instance's __dict__ are looked up in the 
class (and then in the superclasses etc).

> Does new method have the *same* concept level with old methods?

The newly added method works exactly the same way as already existing 
ones. Not a single difference. The class statement is just syntactic 
sugar anyway. The following snippets are equivalent:

# sugar-coated:
class Boo(object):
     faaz = 0

     def far(self):
         type(self).faaz += 1
         print self

     def frob(self):
         print "yadda"

# raw:
def far(self):
     type(self).faaz += 1
     print self

Boo = type('Boo', (object,), dict(faaz=0, far=far))

def frob(self):
    print "yadda"

Boo.frob = frob

> Especially, if there
> are classes inherit from class A, how does name resolution work on this case?

As usual.

> 3.
> How do I write a decroator for a method?

Mostly the same way you'd write a decorator for a function

> Eg:
> class A(object):
>     @my_dec
>     def func(self):
>         pass
> Here, my_dec should return a method rathar than a function/lambda. Am I right?

Nope. Functions defined within a class statement are plain ordinary 
functions. It's the lookup mechanism that "turns them into methods" when 
they are looked up as attribute of a class. In fact, Python "methods" 
are thin callable wrappers around a function, a class and (most of the 
time) an instance, wrappers which are created - usually at lookup time - 
by the __get__ method of the function type (you may want to read about 
the descriptor protocol on python.org - in the section about new style 
classes IIRC).

Anyway, you can explore this by yourself:

 >>> Boo.far
<unbound method Boo.far>
 >>> b.far
<bound method Boo.far of <__main__.Boo object at 0xb787f96c>>
 >>> Boo.__dict__['far']
<function far at 0xb7c28aac>
 >>> Boo.__dict__['far'] is far
True
 >>> f1 = b.far
 >>> f2 = b.far
 >>> f1 is f2
False
 >>> f1()
<__main__.Boo object at 0xb787f96c>
 >>> f2()
<__main__.Boo object at 0xb787f96c>
 >>> dir(f1)
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', 
'__get__', '__getattribute__', '__hash__', '__init__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 
'im_class', 'im_func', 'im_self']
 >>> f1.im_class
<class '__main__.Boo'>
 >>> f1.im_func
<function far at 0xb7c28aac>
 >>> f1.im_func is far
True
 >>> f1.im_self
<__main__.Boo object at 0xb787f96c>
 >>> f1.im_self is b
True
 >>> bf = Boo.far
 >>> bf.im_func
<function far at 0xb7c28aac>
 >>> bf.im_func is far
True
 >>> bf.im_class
<class '__main__.Boo'>
 >>> bf.im_self
 >>> bf.im_self is None
True
 >>> far.__get__(b, Boo)
<bound method Boo.far of <__main__.Boo object at 0xb787f96c>>
 >>> far.__get__(b, Boo).im_func is far
True
 >>>


So, to answer your question: what you are decorating are functions, not 
methods.

> What does @property  @staticmethod... really do? I cannot step-into them for
> source code.

staticmethod wraps the function into an object that, when looked up as 
an attribute, will return the raw function instead of returning a method.

property is a type that provides a simple generic implementation for 
computed attributes. You'll find more detailed explanations in the doc 
(but perhaps not in the most obvious place - it's somewhere in the peps 
or in the 'nws style classes' stuff IIRC). Anyway, using it as a 
decorator is a somewhat special case (well... to me at least), that will 
defined a property with only a getter (the decorated function).

> 4.
> If most of above questions can be solved, then it would be easy to implement
> the feature: "dynamic property attach".
> Eg:
> One class can read/store settings from/to some file based on
> the file content.
> # File: cfg.ini
> x = 1
> y = python
> config = SettingClass('cfg.ini')  # dynamically build up properties x and y.
> x = config.x  #  x will be set to 1   (str -> int convertion would be
> done by 'property x')
> y = config.y  #  y will be set to 'python'
> config.x = 9   # 'x = 9' is written to cfg.ini.
> 
> How to implement

In this case, I'd rather use the __getattr__/__setattr__ hooks. It won't 
work with properties nor custom descriptors, since descriptors only work 
as class attributes - not as instance attributes (which BTW is why 
setting a function as an instance attribute doesn't make it a method...)


here are a couple links to relevant documentation and stuffs:
http://www.python.org/download/releases/2.2.3/descrintro/
http://users.rcn.com/python/download/Descriptor.htm
http://www.python.org/doc/newstyle/

> ^_^ Maybe there are some library does the same thing. 

Indeed:
http://www.voidspace.org.uk/python/configobj.html
http://wiki.python.org/moin/ConfigParserShootout


HTH



More information about the Python-list mailing list