Are x.method() and method(x) equivalent? [duplicate] - python

I'm doing Code Academy's tutorials on Python, and I'm a bit confused about the definition of method and function. From the tutorial:
You already know about some of the built-in functions we've used on (or to create) strings, such as .upper(), .lower(), str(), and len().
Coming from C++, I would think .upper() and .lower() would be called methods and len() and str() functions. In the tutorial, the terms seem to be used interchangeably.
Does Python distinguish between methods and functions in the way C++ does?
Unlike Difference between a method and a function, I'm asking about the particulars of Python. The terms 'method' and 'function' do not seem to always follow the definition given in the accepted answer of the linked question.

Needs Attention: This answer seems to be outdated. Check this
A function is a callable object in Python, i.e. can be called using the call operator (though other objects can emulate a function by implementing __call__). For example:
>>> def a(): pass
>>> a
<function a at 0x107063aa0>
>>> type(a)
<type 'function'>
A method is a special class of function, one that can be bound or unbound.
>>> class A:
... def a(self): pass
>>> A.a
<unbound method A.a>
>>> type(A.a)
<type 'instancemethod'>
>>> A().a
<bound method A.a of <__main__.A instance at 0x107070d88>>
>>> type(A().a)
<type 'instancemethod'>
Of course, an unbound method cannot be called (at least not directly without passing an instance as an argument):
>>> A.a()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method a() must be called with A instance as first argument (got nothing instead)
In Python, in most cases, you won't notice the difference between a bound method, a function, or a callable object (i.e. an object that implements __call__), or a class constructor. They all look the same, they just have different naming conventions. Under the hood, the objects may look vastly different though.
This means that a bound method can be used as a function, this is one of the many small things that makes Python so powerful
>>> b = A().a
>>> b()
It also means that even though there is a fundamental difference between len(...) and str(...) (the latter is a type constructor), you won't notice the difference until you dig a little deeper:
>>> len
<built-in function len>
>>> str
<type 'str'>

If you still don’t understand how methods work, a look at the
implementation can perhaps clarify matters. When an instance attribute
is referenced that isn’t a data attribute, its class is searched. If
the name denotes a valid class attribute that is a function object, a
method object is created by packing (pointers to) the instance object
and the function object just found together in an abstract object:
this is the method object. When the method object is called with an
argument list, a new argument list is constructed from the instance
object and the argument list, and the function object is called with
this new argument list.
http://docs.python.org/2/tutorial/classes.html#method-objects
Read carefully this excerpt.
It means :
1) An instance doesn't really hold an object being a method that would be its attribute.
In fact, there is not at all a "method" attribute in the __dict__ of an instance (__dict__ is the namespace of an object)
2) The fact that an instance seems to have a "method" when a "method" attribute is called, is due to a process, not the presence of a method object inside the namespace of an instance
3) Also, there doesn't really exist a method object in the namespace of a class.
But there's a difference with an instance, because there must be somewhere something that leads to a real method object when such a call is done, must not ?
What is called a "method" attribute of a class, for easiness of wording, is in reality a function object being attribute in the namespace of the class.
That is to say, a pair (identifier of the function, function) is a member of the __dict__ of a class, and this attribute allows the intepreter to construct a method object when a method call is performed.
4) Again, the fact that a class seems to have a "method" when a "method" attribute is called, is due to a process, not to the presence of a method object inside the namespace of a class
EDIT I'm no more sure of that; see at the end
5) A method object (not "method" object; I mean the real object being really a method`, what is described in the excerpt) is created at the moment of the call, it doesn't exists before.
It is a kind of wrapper : it packs pointers to the instance object and the function object on which the method is based.
So, a method is based on a function. This function is for me the real attribute of the class holding the said "method", because this function really belongs to the namespace ( __dict__ ) of the class: this function is described as a <function ......> when the __dict__ is printed.
This function can be reached from the method object using the alias im_func or __func__ (see the below code)
.
I believe that these notions are not very commonly known and understood. But the following code proves what I said.
class A(object):
def __init__(self,b=0):
self.b = b
print 'The __init__ object :\n',__init__
def addu(self):
self.b = self.b + 10
print '\nThe addu object :\n',addu
print '\nThe A.__dict__ items :\n',
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in A.__dict__.items())
a1 = A(101)
a2 = A(2002)
print '\nThe a1.__dict__ items:'
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in a1.__dict__.items())
print '\nThe a2.__dict__ items:'
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in a2.__dict__.items())
print '\nA.addu.__func__ :',A.addu.__func__
print id(A.addu.__func__),'==',hex(id(A.addu.__func__))
print
print 'A.addu :\n ',
print A.addu,'\n ',id(A.addu),'==',hex(id(A.addu))
print 'a1.addu :\n ',
print a1.addu,'\n ',id(a1.addu),'==',hex(id(a1.addu))
print 'a2.addu :\n ',
print a2.addu,'\n ',id(a2.addu),'==',hex(id(a2.addu))
a2.addu()
print '\na2.b ==',a2.b
print '\nThe A.__dict__ items :\n',
print '\n'.join(' {0:{align}11} : {1}'.format(*it,align='^')
for it in A.__dict__.items())
result
The __init__ object :
<function __init__ at 0x011E54B0>
The addu object :
<function addu at 0x011E54F0>
The A.__dict__ items :
__module__ : __main__
addu : <function addu at 0x011E54F0>
__dict__ : <attribute '__dict__' of 'A' objects>
__weakref__ : <attribute '__weakref__' of 'A' objects>
__doc__ : None
__init__ : <function __init__ at 0x011E54B0>
The a1.__dict__ items:
b : 101
The a2.__dict__ items:
b : 2002
A.addu.__func__ : <function addu at 0x011E54F0>
18765040 == 0x11e54f0
A.addu :
<unbound method A.addu>
18668040 == 0x11cda08
a1.addu :
<bound method A.addu of <__main__.A object at 0x00CAA850>>
18668040 == 0x11cda08
a2.addu :
<bound method A.addu of <__main__.A object at 0x011E2B90>>
18668040 == 0x11cda08
a2.b == 2012
The A.__dict__ items :
__module__ : __main__
addu : <function addu at 0x011E54F0>
__dict__ : <attribute '__dict__' of 'A' objects>
__weakref__ : <attribute '__weakref__' of 'A' objects>
__doc__ : None
__init__ : <function __init__ at 0x011E54B0>
.
EDIT
Something is troubling me and I don't know the deep innards of the subject:
The above code shows that A.addu , a1.addu and a2.addu are all the same method object, with a unique identity.
However A.addu is said an unbound method because it doesn't have any information concerning an particular instance,
and a1.addu and a2.addu are said bound methods because each one has information designating the instance that must be concerned by the operations of the method.
Logically, for me, that would mean that the method should be different for each of these 3 cases.
BUT the identity is the same for all three, and moreover this identity is different from the identity of the function on which the method is based.
It leads to the conclusion that the method is really an object living in the memory, and that it doesn't change from one call from an instance to another cal from another instance.
HOWEVER , printing the namespace __dict__ of the class, even after the creation of instances and the call of the "method" addu(), this namespace doesn't exposes a new object that could be identified to the method object different from the addu function.
What does it mean ?
It gives me the impression that as soon as a method object is created, it isn't destroyed, it lives in the memory (RAM).
But it lives hidden and only the processes that form the interpeter's functionning know how and where to find it.
This hidden object, the real method object, must have the ability to change the reference to the instance to which the function must be applied, or to reference None if it is called as an unbound method. That's what it seems to me, but it's only brain-storming hypothesis.
Does anybody know something on this interrogation ?
To answer to the question, it can be considered correct to call .upper and .lower functions , since in reality they are based on functionsas every method of a class.
However, the following result is special, probably because they are builtin methods/functions, not user's methods/functions as in my code.
x = 'hello'
print x.upper.__func__
result
print x.upper.__func__
AttributeError: 'builtin_function_or_method' object has no attribute '__func__'

In the following class definition:
class MyClass:
"""A simple example class"""
def f(self):
return 'hello world'
Class : MyClass
Function: f()
Method: None (Actually, not applicable)
Lets create an instance of the above class. We'll do this by assigning class object, i.e. MyClass() to var x
x = MyClass()
Here,
Function: None
Method: x.f()
And lets not forget that the function object MyClass.f was used to define (internally) the method object x.f when we assigned x to MyClass()

Basically, yes, Python does distinguish them, but in Python it is common to view methods as a subset of functions. Methods are associated with a class or instance, and "standalone functions" are not. Something that is a method is also a function, but there can be functions that are not methods.
As Jon Clements mentioned in his comment, though, the distinction is not so ironclad as in C++. Standalone functions can be "converted" into methods at runtime, and methods can be assigned to variables in such a way that they effectively behave no differently than standalone functions. So the boundary between methods and functions is permeable.

Took snippet from this answer
Following are the changes
Unbound methods (methods bound to a class object) are no longer available.
unbound method -> function
type instancemethod are removed from Python 3
More about methods on this answer
About methods on Python docs
More about classes on Python docs
import sys
print(sys.version)
# 3.9.0rc2 (tags/v3.9.0rc2:2bd31b5, Sep 17 2020, 00:58:12) [MSC v.1927 64 bit (AMD64)]
class A:
def a(self): pass
print(A.a)
# <unbound method A.a>
# <function A.a at 0x00000200FBE121F0>
print(type(A.a))
# <type 'instancemethod'>
# <class 'function'>
print(A().a)
# <bound method A.a of <__main__.A instance at 0x107070d88>>
# <bound method A.a of <__main__.A object at 0x00000200FBB12640>>
print(type(A().a))
# <type 'instancemethod'>
# <class 'method'>

Short answer:
Methods are bounded functions. Functions become methods when function is bound to an object.
To understand the difference between function and method in python you need to understand
Everything is an object in python. Even function is an object
I searched python.org to find exact definition of object in python. But I could not find exact definition.
In your python console define an integer
my_int = 1
my_int.__class__
output:
<class 'int'>
Define a lambda function
my_lambda = lambda x:x+1
my_lambda.__class__
output
<class 'function'>
Define a function.
def mycounter(x):
return x+1
mycounter.__class__
output
<class 'function'>
From this we can surmise that everything is an object in python. Even function is an object.
Class object and instance object are different. When you define a class, class object is created. When you instantiate a class, instance object is created.
In python console define a class.
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
In your python console you can check MyClass object is created
MyClass.__class__
output
<class 'type'>
Create instance object of the class
mc = MyClass()
mc.__class__
output
<class 'MyClass'>
Check attribute i for instance object
mc.i
output
12345
Check attribute i for class object
MyClass.i
output
12345
Check attribute f for instance object.
mc.f
output
<bound method MyClass.f of <MyClass object at 0x7fbea6c18d00>>
Check attribute f for class object.
MyClass.f
output
<function MyClass.f at 0x7f1730f9e1f0>
We can see that for Class object f is a function object and for instance object, f is a bound method object.
Call f() on instance object
mc.f()
output
'hello world'
Call f() on class object.
MyClass.f()
output
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: f() missing 1 required positional argument: 'self'
Pass an instance object when calling class object f function.
MyClass.f(mc)
output
'hello world'
From above examples we can say that -
MyClass is a Class object and mc is an instance object and they are different from one another.
Functions become methods when functions are bound to an object. These objects need not be just class instance object. They can any other python objects.
References:
As per python documentation for function -
Blockquote
A method is a function that ‘belongs’ to an object and is named obj.methodname, where obj is some object (this may be an expression), and methodname is the name of a method that is defined by the object’s type. Different types define different methods. Methods of different types may have the same name without causing ambiguity.
As per python documentation for class definition -
When a class definition is entered, a new namespace is created, and used as the local scope — thus, all assignments to local variables go into this new namespace. In particular, function definitions bind the name of the new function here. When a class definition is left normally (via the end), a class object is created.
As per python documentation for class objects
Class objects support two kinds of operations: attribute references and instantiation.
As per python documentation for instance objects
The only operations understood by instance objects are attribute references. There are two kinds of valid attribute names: data attributes and methods.
A method is a function that “belongs to” an object. (In Python, the term method is not unique to class instances: other object types can have methods as well. For example, list objects have methods called append, insert, remove, sort, and so on. However, in the following discussion, we’ll use the term method exclusively to mean methods of class instance objects, unless explicitly stated otherwise.)
By definition, all attributes of a class that are function objects define corresponding methods of its instances. Valid method names of an instance object depend on its class. By definition, all attributes of a class that are function objects define corresponding methods of its instances. So in our example, x.f is a valid method reference, since MyClass.f is a function,
but x.i is not, since MyClass.i is not. But x.f is not the same thing as MyClass.f — it is a method object, not a function object.

Related

Differences between methods of a class, which are "function" and which are "bound method"?

I have experimented a little. By checking __dict__ of a class or an instance, I can see some method has type function and some bound method. The experiment is messy, and I can't figure the following questions out.
In Python 3, what are the differences between methods of a class or instance, which are "function" and which are "bound method"?
How are they created respectively?
Can they both be called on a class and on an instance? Will they both be implicitly given an instance as their first argument?
Is "bound method" an attribute of a class or an instance of a class?
Thanks.
This answer will be really technical, I hope it's still understandable though. The problem is that it requires knowledge of the descriptor protocol to understand how methods in Python work.
All functions in Python 3 are descriptors, to be precise they are non-data descriptors. That means they implements a __get__ method - but no __set__ method.
That's interesting because descriptors can do (almost) anything if they are looked up on a class or an instance.
By the definition of the __get__ method in Pythons data model:
object.__get__(self, instance, owner)
Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access). owner is always the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner. This method should return the (computed) attribute value or raise an AttributeError exception.
So what does this have to do with the difference between function and bound_method?
It's easy, a function accessed through __get__ with an instance=None will return itself:
>>> def func(x): return x
>>> func.__get__(None, object)
<function __main__.func>
>>> func.__get__(None, object) is func
True
While it will be a bound_method if accessed with an not-None instance:
>>> func.__get__(object())
<bound method func of <object object at 0x00000155614A0610>>
It's basically just a wrapper around func with the instance stored:
>>> m = func.__get__(object())
>>> m.__self__ # stored instance
<object at 0x155614a0650>
>>> m.__func__ # stored function
<function __main__.func>
However, when called, it will pass the instance as first argument to the wrapped function:
>>> m()
<object at 0x155614a0650>
So, bound methods will pass the instance as first argument, while functions do not (they requires all attributes).
So when you look at a class all normal methods will display as functions while all normal methods on an instance will be bound methods.
Why did I mention normal methods? Because you can define arbitrary descriptors. For example the Python built-ins already contain several exceptions:
classmethod
staticmethod
property (this is in fact a data-descriptor so I'll neglect it in the following discussion)
classmethods will display as bound method even when looked up on the class. That's because they are meant to be callable on the class and pass the class to the function, no matter if they are called on the class or the instance:
class Test(object):
#classmethod
def func(x):
return x
>>> Test.func
<bound method Test.func of <class '__main__.Test'>>
>>> Test().func
<bound method Test.func of <class '__main__.Test'>>
And staticmethods always display as functions because they never pass anything additional to the function:
class Test(object):
#staticmethod
def func(x):
return x
>>> Test().func
<function __main__.Test.func>
>>> Test.func
<function __main__.Test.func>
So it's easily possible to see also bound methods on the class (e.g. classmethods) and likewise one could also find normal functions on instances (e.g. staticmethods).

Not sure what the Python documentation says about user-defined method objects here

From the Python Language Reference Data Models section, under the User-defined methods heading:
When the attribute is a user-defined method object, a new method object is only created if the class from which it is being retrieved is the same as, or a derived class of, the class stored in the original method object; otherwise, the original method object is used as it is.
What does this sentence mean?
Python 2 binds functios when you look up a name on a class or an instance, see the descriptor protocol. When doing this on a class, the result is an unbound method:
>>> class Foo(object):
... def bar(self):
... return 'Method bound to {!r}, from class Foo'.format(self)
... def __repr__(self):
... return '<Instance of type {!r}>'.format(type(self))
...
>>> Foo.bar
<unbound method Foo.bar>
This object contains a reference to the original class still, as well as the original function object:
>>> Foo.bar.im_class
<class '__main__.Foo'>
>>> Foo.bar.__func__ # new name, old name im_self still works too
<function bar at 0x105d2a9b0>
The term unbound here refers to the fact that it's not bound to an instance; however it is bound to the class.
The text tells you that it'll treat such unbound method objects the same as functions, when you stick them on another class. So if you do this, the method object will be rebound to the new class; the original function object is referenced and rebound to the new class:
>>> class Bar(Foo):
... bar = Foo.bar # an unbound method from the Foo class!
...
>>> Bar.bar
<unbound method Bar.bar>
>>> Bar.bar.im_class
<class '__main__.Bar'>
>>> Bar().bar() # it'll work like any other method
"Method bound to <Instance of type <class '__main__.Bar'>>, from class Foo"
Here Bar is a subclass of Foo, and the unbound method object is re-bound to the class Bar.
However, the text states that this won't be done if you stick the unbound method onto a different class that is not a subclass of Foo:
>>> class Baz(object): # not derived from Foo
... bar = Foo.bar
...
>>> Baz.bar
<unbound method Foo.bar>
>>> Baz.bar.im_class
<class '__main__.Foo'>
>>> Baz().bar
<unbound method Foo.bar>
>>> Baz().bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got nothing instead)
You'd have to access the original function via Foo.bar.__func__ or evade the descriptor protocol entirely by reaching in to the class __dict__ (so use Foo.__class__['bar']) to avoid this issue altogether.
The text in the reference documentation is rather confusing, as this doesn't apply to fully bound methods even though it implies it should; these are never re-bound even when used on a class:
>>> class Spam(object):
... bar = Foo().bar # *bound* method, taken from an instance of Foo
...
>>> Spam.bar
<bound method Foo.bar of <Instance of type <class '__main__.Foo'>>>
>>> Spam().bar
<bound method Foo.bar of <Instance of type <class '__main__.Foo'>>>
>>> Spam().bar()
"Method bound to <Instance of type <class '__main__.Foo'>>, from class Foo"
Python 3 got rid of the unbound methods altogether; retrieving a function from a class object gives you the original function object. The above restriction has been dropped altogether.
Thanks for #Pieters fantastic answer, and with a little bit of reading the docs again and again, and experiments. I finally understand what the doc is trying to say:
A user-defined method object is an object with
a class(im_class)
a class instance (im_self)
a callable (im_func)
Every time we try to get an attributes of a class, a user-define method object will be created if the attribute is the following:
user-defined function object
an unbound user-defined method object,
a class method object
1 is the normal case when the attributes is a function, 3 is when the function is a #classmethod
2 is the case mentioned by #Pieters, where the attribute is already a user-defined method object from other class, in this case, unless this object is from the same class (or derived class), no new user-defined method object will be created, and the old user-defined method will be used

What's the difference between a 'function', 'method' and 'bound method' in Python 3?

I've observed at least 3 types related to functions in Python 3:
>>> class A():
... def f(): pass
...
>>> A.f
<function A.f at 0x7fcaef304268>
>>> A().f
<bound method A.f of <__main__.A object at 0x7fcaef2fae80
>>> set.union
<method 'union' of 'set' objects>
I'm wondering what's the difference between 'function', 'method' and 'bound method'? Is 'method' a type equivalent to 'unbound method' in Python 2?
Is 'method' a type equivalent to 'unbound method' in Python 2?
Kind-a-sort-a. But not really. It is a method_descriptor object defined in C code. It is an unbound method, but not the kind you found in Python 2.
For Python types written C, all 'methods' are really C functions. The <method 'name' of 'type' objects> object you found is a special object you can use to call that function given an instance and further arguments, just like the function object does for custom Python classes. The object is defined in C in the PyMethodDescr_Type structure. It implements the descriptor protocol, just like functions do.
Python defines several other such descriptor types; if you use __slots__, each attribute is a dsescriptor of type member_descriptor (see the PyMemberDescr_Type structure), while classmethod, property and staticmethod are perhaps better known descriptor objects.
In Python 2, bound and unbound methods are really just one type, instancemethod (defined by the PyMethod_Type structure); it'll report as bound if the __self__ (im_self) attribute is set. In Python 3 using a function as a descriptor simply doesn't produce method objects without __self__ set; instead calling function.__get__() with no instance just returns the function again.
The only reason Python 2 returns unbound methods is to enforce a type check; the first argument must be an instance of the class (or a subclass thereof). This didn't make all that much sense for Python code that supports duck-typing, so in Python 3 the restriction was removed. However, with C code you can't use duck-typing, you still have to restrict the type, and that's why C-types still return a method_descriptor object that enforces this restriction.

Descriptors and direct access: Python reference

The python 3.3 documentation tells me that direct access to a property descriptor should be possible, although I'm skeptical of its syntax x.__get__(a). But the example that I constructed below fails. Am I missing something?
class MyDescriptor(object):
"""Descriptor"""
def __get__(self, instance, owner):
print "hello"
return 42
class Owner(object):
x = MyDescriptor()
def do_direct_access(self):
self.x.__get__(self)
if __name__ == '__main__':
my_instance = Owner()
print my_instance.x
my_instance.do_direct_access()
Here's the error I get in Python 2.7 (and also Python 3.2 after porting the snippet of code). The error message makes sense to me, but that doesn't seem to be how the documentation said it would work.
Traceback (most recent call last):
File "descriptor_test.py", line 15, in <module>
my_instance.do_direct_access()
File "descriptor_test.py", line 10, in do_direct_access
self.x.__get__(self)
AttributeError: 'int' object has no attribute '__get__'
shell returned 1
By accessing the descriptor on self you invoked __get__ already. The value 42 is being returned.
For any attribute access, Python will look to the type of the object (so type(self) here) to see if there is a descriptor object there (an object with a .__get__() method, for example), and will then invoke that descriptor.
That's how methods work; a function object is found, which has a .__get__() method, which is invoked and returns a method object bound to self.
If you wanted to access the descriptor directly, you'd have to bypass this mechanism; access x in the __dict__ dictionary of Owner:
>>> Owner.__dict__['x']
<__main__.MyDescriptor object at 0x100e48e10>
>>> Owner.__dict__['x'].__get__(None, Owner)
hello
42
This behaviour is documented right above where you saw the x.__get__(a) direct call:
The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting with a.__dict__['x'], then type(a).__dict__['x'], and continuing through the base classes of type(a) excluding metaclasses.
The Direct Call scenario in the documentation only applies when you have a direct reference to the descriptor object (not invoked); the Owner.__dict__['x'] expression is such a reference.
Your code on the other hand, is an example of the Instance Binding scenario:
Instance Binding
If binding to an object instance, a.x is transformed into the call: type(a).__dict__['x'].__get__(a, type(a)).

How does assignment of a function as a class attribute become a method in Python?

>>> class A(object): pass
>>> def func(cls): pass
>>> A.func = func
>>> A.func
<unbound method A.func>
How does this assignment create a method? It seems unintuitive that assignment does the following for classes:
Turn functions into unbound instance methods
Turn functions wrapped in classmethod() into class methods (actually, this is pretty intuitive)
Turn functions wrapped in staticmethod() into functions
It seems that for the first, there should be an instancemethod(), and for the last one, there shouldn't be a wrapper function at all. I understand that these are for uses within a class block, but why should they apply outside of it?
But more importantly, how exactly does assignment of the function into a class work? What magic happens that resolves those 3 things?
Even more confusing with this:
>>> A.func
<unbound method A.func>
>>> A.__dict__['func']
<function func at 0x...>
But I think this is something to do with descriptors, when retrieving attributes. I don't think it has much to do with the setting of attributes here.
You're right that this has something to do with descriptor protocol. Descriptors are how passing the receiver object as the first parameter of a method is implemented in Python. You can read more detail about Python attribute lookup from here. The following shows on a bit lower level, what is happening when you do A.func = func; A.func:
# A.func = func
A.__dict__['func'] = func # This just sets the attribute
# A.func
# The __getattribute__ method of a type object calls the __get__ method with
# None as the first parameter and the type as the second.
A.__dict__['func'].__get__(None, A) # The __get__ method of a function object
# returns an unbound method object if the
# first parameter is None.
a = A()
# a.func()
# The __getattribute__ method of object finds an attribute on the type object
# and calls the __get__ method of it with the instance as its first parameter.
a.__class__.__dict__['func'].__get__(a, a.__class__)
# This returns a bound method object that is actually just a proxy for
# inserting the object as the first parameter to the function call.
So it's the looking up of the function on a class or an instance that turns it into a method, not assigning it to a class attribute.
classmethod and staticmethod are just slightly different descriptors, classmethod returning a bound method object bound to a type object and staticmethod just returns the original function.
Descriptors are the magic1 that turns an ordinary function into a bound or unbound method when you retrieve it from an instance or class, since they’re all just functions that need different binding strategies. The classmethod and staticmethod decorators implement other binding strategies, and staticmethod actually just returns the raw function, which is the same behavior you get from a non-function callable object.
See “User-defined methods” for some gory details, but note this:
Also notice that this transformation only happens for user-defined functions; other callable objects (and all non-callable objects) are retrieved without transformation.
So if you wanted this transformation for your own callable object, you could just wrap it in a function, but you could also write a descriptor to implement your own binding strategy.
Here’s the staticmethod decorator in action, returning the underlying function when it’s accessed.
>>> #staticmethod
... def f(): pass
>>> class A(object): pass
>>> A.f = f
>>> A.f
<function f at 0x100479398>
>>> f
<staticmethod object at 0x100492750>
Whereas a normal object with a __call__ method doesn’t get transformed:
>>> class C(object):
... def __call__(self): pass
>>> c = C()
>>> A.c = c
>>> A.c
<__main__.C object at 0x10048b890>
>>> c
<__main__.C object at 0x10048b890>
1 The specific function is func_descr_get in Objects/funcobject.c.
What you have to consider is that in Python everything is an object. By establishing that it is easier to understand what is happening. If you have a function def foo(bar): print bar, you can do spam = foo and call spam(1), getting of course, 1.
Objects in Python keep their instance attributes in a dictionary called __dict__ with a "pointer" to other objects. As functions in Python are objects as well, they can be assigned and manipulated as simple variables, passed around to other functions, etc. Python's implementation of object orientation takes advantage of this, and treats methods as attributes, as functions that are in the __dict__ of the object.
Instance methods' first parameter is always the instance object itself, generally called self (but this could be called this or banana). When a method is called directly on the class, it is unbound to any instance, so you have to give it an instance object as the first parameter (A.func(A())). When you call a bound function (A().func()), the first parameter of the method, self, is implicit, but behind the curtains Python does exactly the same as calling directly on the unbound function and passing the instance object as the first parameter.
If this is understood, the fact that assigning A.func = func (which behind the curtains is doing A.__dict__["func"] = func) leaves you with an unbound method, is unsurprising.
In your example the cls in def func(cls): pass actually what will be passed on is the instance (self) of type A. When you apply the classmethod or staticmethod decorators do nothing more than take the first argument obtained during the call of the function/method, and transform it into something else, before calling the function.
classmethod takes the first argument, gets the class object of the instance, and passes that as the first argument to the function call, while staticmethod simply discards the first parameter and calls the function without it.
Point 1: The function func you defined exists as a First-Class Object in Python.
Point 2: Classes in Python store their attributes in their __dict__.
So what happens when you pass a function as the value of a class attribute in Python? That function is stored in the class' __dict__, making it a method of that class accessed by calling the attribute name you assigned it to.
Relating to MTsoul's comment to Gabriel Hurley's answer:
What is different is that func has a __call__() method, making it "callable", i.e. you can apply the () operator to it. Check out the Python docs (search for __call__ on that page).

Categories

Resources