Methods are attributes too. They just happen to be callable objects.
You can detect if an object is callable by using the callable()
function:
>>> def foo(): pass
...
>>> callable(foo)
True
>>> callable(1)
False
When you call a method, you look up the attribute (a getattr()
operation) and then call the result:
c.setAttr(newvalue)
is two steps; finding the attribute (which in this case looks up the attribute on the class, and treats it as a descriptor), then calls the resulting object, a method.
When you assign to an attribute, you rebind that name to a new value:
c.setAttr = 'something else'
would be a setattr()
operation.
If you wanted to intercept getting and setting attributes on instances of your class, you could provide the attribute access hooks, __getattr__
, __setattr__
and __delattr__
.
If you wanted to add a method to an instance, you would have to treat the function as a descriptor object, which produces a method object:
>>> class Foo: pass
...
>>> foo = Foo() # instance
>>> def bar(self): pass
...
>>> bar
<function bar at 0x10b85a320>
>>> bar.__get__(foo, Foo)
<bound method Foo.bar of <__main__.Foo instance at 0x10b85b830>>
The return value of function.__get__()
, when given an instance and a class, is a bound method. Calling that method will call the underlying function with self
bound to the instance.
And speaking of descriptors, the property()
function returns a descriptor too, making it possible to have functions that behave like attributes; they can intercept the getattr()
, setattr()
and delattr()
operations for just that attribute and turn it into a function call:
>>> class Foo:
... @property
... def bar(self):
... return "Hello World!"
...
>>> foo = Foo()
>>> foo.bar
"Hello World!"
Accessing .bar
invoked the bar
property get hook, which then calls the original bar
method.
In almost all situations, you are not going to need the callable()
function; you document your API, and provide methods and attributes and the user of your API will figure it out without testing each and every attribute to see if it is callable. With properties, you have the flexibility of providing attributes that are really callables in any case.