Descriptors and direct access: Python reference - python

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)).

Related

__get__() of a method/function in Python

I have a piece of code that I am trying to understand, and even with the existing answers, I really couldn't understand the purpose of the following code, Can someone please help me in understanding the same?
I have already looked a various relevant questions ( __get__() ) here and I couldnt find specific answers. I understand that the class below is trying to create a method on the fly ( possibly we get to this class from a __getattr__() method which fails to find an attribute ) and return the method to the caller. I have commented right above the lines of code I need understanding with.
class MethodGen(object):
def __getattr__(self, name):
method = self.method_gen(name)
if method:
return self.method_gen(name)
def method_gen(self, name):
def method(*args, **kwargs):
print("Creating a method here")
# Below are the two lines of code I need help understanding with
method.__name__ = name
setattr(self, name, method.__get__(self))
return method
If I am not wrong, the method() function's attribute __name__ has been set, but in setattr() function, the attribute of the class MethodGen, name is set to what ?
This question really intrigued me. The two answers provided didn't seem to tell the whole story. What bothered me was the fact that in this line:
setattr(self, name, method.__get__(self))
the code is not setting things up so that method.__get__ Will be called at some point. Rather, method.__get__ is actually Being Called! But isn't the idea that this __get__ method will be called when a particular attribute of an object, an instance of MethodGen in this case, is actually referenced? If you read the docs, this is the impression you get...that an attribute is linked to a Descriptor that implements __get__, and that implementation determines what gets returned when that attribute is referenced. But again, that's not what's going on here. This is all happening before that point. So what IS really going on here?
The answer lies HERE. The key language is this:
To support method calls, functions include the __get__() method for
binding methods during attribute access. This means that all functions
are non-data descriptors which return bound methods when they are
invoked from an object.
method.__get__(self) is exactly what's being described here. So what method.__get__(self) is actually doing is returning a reference to the "method" function that is bound to self. Since in our case, self is an instance of MethodGen, this call is returning a reference to the "method" function that is bound to an instance of MethodGen. In this case, the __get__ method has nothing to do with the act of referencing an attribute. Rather, this call is turning a function reference into a method reference!
So now we have a reference to a method we've created on the fly. But how do we set it up so it gets called at the right time, when an attribute with the right name is referenced on the instance it is bound to? That's where the setattr(self, name, X) part comes in. This call takes our new method and binds it to the attribute with name name on our instance.
All of the above then is why:
setattr(self, name, method.__get__(self))
is adding a new method to self, the instance of the MethodGen class on which method_gen has been called.
The method.__name__ = name part is not all that important. Executing just the line of code discussed above gives you all the behavior you really want. This extra step just attaches a name to our new method so that code that asks for the name of the method, like code that uses introspection to write documentation, will get the right name. It is the instance attribute's name...the name passed to setattr...that really matters, and really "names" the method.
Interesting, never seen this done before, seems tough to maintain (probably will make some fellow developers want to hang you).
I changed some code so you can see a little more of what is happening.
class MethodGen(object):
def method_gen(self, name):
print("Creating a method here")
def method(*args, **kwargs):
print("Calling method")
print(args) # so we can see what is actually being outputted
# Below are the two lines of code I need help understanding with
method.__name__ = name # These the method name equal to name (i.e. we can call the method this way)
# The following is adding the new method to the current class.
setattr(self, name, method.__get__(self)) # Adds the method to this class
# I would do: setattr(self, name, method) though and remove the __get__
return method # Returns the emthod
m = MethodGen()
test = m.method_gen("my_method") # I created a method in MethodGen class called my_method
test("test") # It returned a pointer to the method that I can use
m.my_method("test") # Or I can now call that method in the class.
m.method_gen("method_2")
m.method_2("test2")
Consider the class below:
class Foo:
def bar(self):
print("hi")
f = Foo()
f.bar()
bar is a class attribute that has a function as its value. Because function implements the descriptor protocol, however, accessing it as Foo.bar or f.bar does not immediately return the function itself; it causes the function's __get__ method to be invoked, and that returns either the original function (as in Foo.bar) or a new value of type instancemethod (as in f.bar). f.bar() is evaluated as Foo.bar.__get__(f, Foo)().
method_gen takes the function named method, and attaches an actual method retrieved by calling the function's __get__ method to an object. The intent is so that something like this works:
>>> m = MethodGen()
>>> n = MethodGen()
>>> m.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MethodGen' object has no attribute 'foo'
>>> n.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MethodGen' object has no attribute 'foo'
>>> m.method_gen('foo')
<function foo at 0x10465c758>
>>> m.foo()
Creating a method here
>>> n.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MethodGen' object has no attribute 'foo'
Initially, MethodGen does not have any methods other than method_gen. You can see the exception raised when attempting to invoke a method named foo on either of two instances. Calling method_gen, however, attaches a new method to just that particular instance. After calling m.method_gen("foo"), m.foo() calls the method defined by method_gen. That call does not affect other instances of MethodGen like n.

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

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.

How is `getattr` related to `object.__getattribute__` and to `object.__getattr__`?

From https://docs.python.org/3.6/library/functions.html#getattr
getattr(object, name[, default])
Return the value of the named attribute of object. name must be a
string. If the string is the name of one of the object’s attributes,
the result is the value of that attribute. For example, getattr(x,
'foobar') is equivalent to x.foobar. If the named attribute does
not exist, default is returned if provided, otherwise
AttributeError is raised.
How is getattr related to object.__getattribute__ and to object.__getattr__?
Does getattr call object.__getattribute__ or object.__getattr__? I would guess the former?
Summary Answer
In general, a dotted lookup invokes __getattribute__.
If the code in __getattribute__ doesn't find the attribute, it looks to see if __getattr__ is defined. If so, it is called. Otherwise, AttributeError is raised.
The getattr() function is just an alternative way to call the above methods. For example getattr(a, 'x') is equivalent to a.x.
The getattr() function is mainly useful when you don't know the name of an attribute in advance (i.e. when it is stored in a variable). For example, k = 'x'; getattr(a, k) is equivalent to a.x.
Analogy and high level point-of-view
The best way to think of it is that __getattribute__ is the first called primary method and __getattr__ is the fallback which is called when attributes are missing. In this way, it is very much like the relationship between __getitem__ and __missing__ for square bracket lookups in dictionaries.
Demonstration code
Here is a worked-out example:
>>> class A(object):
x = 10
def __getattribute__(self, attr):
print(f'Looking up {attr!r}')
return object.__getattribute__(self, attr)
def __getattr__(self, attr):
print(f'Invoked the fallback method for missing {attr!r}')
return 42
>>> a = A()
>>> a.x
Looking up 'x'
10
>>> a.y
Looking up 'y'
Invoked the fallback method for missing 'y'
42
>>> # Equivalent calls with getattr()
>>> getattr(a, 'x')
Looking up 'x'
10
>>> getattr(a, 'y')
Looking up 'y'
Invoked the fallback method for missing 'y'
42
Official Documentation
Here are the relevant parts of the docs:
object.__getattr__(self, name) Called when an attribute lookup has
not found the attribute in the usual places (i.e. it is not an
instance attribute nor is it found in the class tree for self). name
is the attribute name. This method should return the (computed)
attribute value or raise an AttributeError exception.
Note that if the attribute is found through the normal mechanism,
__getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __getattr__() would have no way to
access other attributes of the instance. Note that at least for
instance variables, you can fake total control by not inserting any
values in the instance attribute dictionary (but instead inserting
them in another object). See the __getattribute__() method below for a
way to actually get total control over attribute access.
object.__getattribute__(self, name) Called unconditionally to
implement attribute accesses for instances of the class. If the class
also defines __getattr__(), the latter will not be called unless
__getattribute__() either calls it explicitly or raises an AttributeError. This method should return the (computed) attribute
value or raise an AttributeError exception. In order to avoid infinite
recursion in this method, its implementation should always call the
base class method with the same name to access any attributes it
needs, for example, object.__getattribute__(self, name).
getattr(foo, 'bar')(which is basically foo.bar) calls __getattribute__ and from there __getattr__ can be called(if it exists) when:
AttributeError is raised by __getattribute__
__getattribute__ calls it explicitly
From docs:
If the class also defines __getattr__(), the latter will not be called
unless __getattribute__() either calls it explicitly or raises an
AttributeError.
Defining __getattr__ can be useful if you don't want to throw attribute errors on random attribute access. Mock library is great example of that.
>>> from unittest.mock import Mock
>>> m = Mock()
>>> m.foo.bar.spam
<Mock name='mock.foo.bar.spam' id='4367274280'>
If you don't want to define __getattr__ all the time nor want to handle AttributeError all the time then you could either use the 3 argument form of getattr() to return default value if attribute wasn't found or use hasattr to check for its existence.
>> getattr([], 'foo')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-60-f1834c32d3ce> in <module>()
----> 1 getattr([], 'foo')
AttributeError: 'list' object has no attribute 'foo'
>>> getattr([], 'foo', 'default')
'default'
Demo:
class A:
def __getattr__(self, attr):
print('Inside A.__getattr__')
return 'eggs'
print(A().foo)
outputs:
Inside A.__getattr__
eggs
Note that the data model documentation points it out as object.__getattr__, but that doesn't mean they exists on builtin object type. Instead they can exist on any object in general. And object in this case is a type as dunder methods are looked up on an object's type.

Why is cls.__dict__[meth] different than getattr(cls, meth) for classmethods/staticmethods?

I've never seen anything else work like this before.
Is there anything else that does this?
>>> class NothingSpecial:
#classmethod
def meth(cls): pass
>>> NothingSpecial.meth
<bound method classobj.meth of <class __main__.NothingSpecial at 0x02C68C70>>
>>> NothingSpecial.__dict__['meth']
<classmethod object at 0x03F15FD0>
>>> getattr(NothingSpecial, 'meth')
<bound method NothingSpecial.meth of <class '__main__.NothingSpecial'>>
>>> object.__getattribute__(NothingSpecial, 'meth')
<classmethod object at 0x03FAFE90>
>>> type.__getattribute__(NothingSpecial, 'meth')
<bound method NothingSpecial.meth of <class '__main__.NothingSpecial'>>
Getattr Uses Descriptor Logic
The main difference is that the dictionary lookup does no extra processing while the attribute fetch incorporates extra logic (see my Descriptor How-To Guide for all the details).
There Are Two Different Underlying Methods
1) The call NothingSpecial.__dict__['meth'] uses the square brackets operator which dispatches to dict.__getitem__ which does a simple hash table lookup or raises KeyError if not found.
2) The call NothingSpecial.meth uses the dot operator which dispatches to type.__getattribute__ which does a simple lookup followed by a special case for descriptors. If the lookup fails, an AttributeError is raised.
How It Works
The overall logic is documented here and here.
In general, a descriptor is an object attribute with “binding
behavior”, one whose attribute access has been overridden by methods
in the descriptor protocol: __get__(), __set__(), and/or __delete__(). If
any of those methods are defined for an object, it is said to be a
descriptor.
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.
However, if the looked-up value is an object defining one of the
descriptor methods, then Python may override the default behavior and
invoke the descriptor method instead. Where this occurs in the
precedence chain depends on which descriptor methods were defined and
how they were called
Hope you've found all of this to be helpful. The kind of exploring you're doing is a great way to learn about Python :-)
P.S. You might also enjoy reading the original Whatsnew in Python 2.2 entry for descriptors or looking at PEP 252 where Guido van Rossum originally proposed the idea.
object.__getattribute__(NothingSpecial, 'meth')
and
NothingSpecial.__dict__['meth']
return the same object in this case. You can quickly check it by doing:
NothingSpecial.__dict__['meth'] is object.__getattribute__(NothingSpecial, 'meth')
$True
Both of them points to the same descriptor object
on the other hand:
object.__getattribute__(NothingSpecial, 'meth') is getattr(NothingSpecial, 'meth')
$False
Basically, they aren't they are not the same object nand the same type:
type(object.__getattribute__(NothingSpecial, 'meth'))
$<class 'classmethod'>
type(getattr(NothingSpecial, 'meth'))
$<class 'method'>
So the answer is that getattr will automagically invoke an object's __get__ method if it has one, whereas object.__getattribute__ and the objects __dict__ lookup do not. The following function proves that:
class Nothing:
#classmethod
def a(cls):
return cls()
#staticmethod
def b():
return 'b'
def c(self):
return 'c'
def gitter(obj, name):
value = object.__getattribute__(obj, name)
if hasattr(value, '__get__'):
if isclass(obj):
instance, cls = None, obj
else:
instance, cls = obj, type(obj)
return value.__get__(instance, cls)
return value
>>> gitter(Nothing, 'a')()
<__main__.Nothing object at 0x03E97930>
>>> gitter(Nothing, 'b')()
'b'
>>> gitter(Nothing(), 'c')()
'c'
However, gitter(Nothing(), 'b') doesn't work currently because it's not detecting that the objtype default value is None, but this is enough.

Why do people default owner parameter to None in __get__?

I've seen this quite often:
def __get__(self, instance, owner=None):
Why do some people use the default value of None for the the owner parameter?
This is even done in the Python docs:
descr.__get__(self, obj, type=None) --> value
Because the owner can easily be derived from the instance, the second argument is optional. Only when there is no instance to derive an owner from, is the owner argument needed.
This is described in the proposal that introduced descriptors, PEP 252 - Making Types Look More Like Classes:
__get__: a function callable with one or two arguments that
retrieves the attribute value from an object. This is also
referred to as a "binding" operation, because it may return a
"bound method" object in the case of method descriptors. The
first argument, X, is the object from which the attribute must
be retrieved or to which it must be bound. When X is None,
the optional second argument, T, should be meta-object and the
binding operation may return an unbound method restricted to
instances of T.
(Bold emphasis mine).
Binding, from day one, was meant to be applicable to the instance alone, with the type being optional. Methods don't need it, for example, since they can be bound to the instance alone:
>>> class Foo: pass
...
>>> def bar(self): return self
...
>>> foo = Foo()
>>> foo.bar = bar.__get__(foo) # look ma! no class!
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x10a0c2710>>
>>> foo.bar()
<__main__.Foo object at 0x10a0c2710>
Besides, the second argument can easily be derived from the first argument; witness a classmethod still binding to the class even though we did not pass one in:
>>> classmethod(bar).__get__(foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> classmethod(bar).__get__(foo)()
<class '__main__.Foo'>
The only reason the argument is there in the first place is to support binding to class, e.g. when there is no instance to bind to. The class method again; binding to None as the instance won't work, it only works if we actually pass in the class:
>>> classmethod(bar).__get__(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __get__(None, None) is invalid
>>> classmethod(bar).__get__(None, Foo)
<bound method type.bar of <class '__main__.Foo'>>
This is the standard way to do it; all Python built-in descriptors I've seen do it, including functions, properties, staticmethods, etc. I know of no case in the descriptor protocol where __get__ will be called without the owner argument, but if you want to call __get__ manually, it can be useful not to have to pass an owner. The owner argument usually doesn't do much.
As an example, you might want a cleaner way to give individual objects new methods. The following decorator cleans up the syntax and lets the methods have access to self:
def method_of(instance):
def method_adder(function):
setattr(instance, function.__name__, function.__get__(instance))
return function
return method_adder
#method_of(a)
def foo(self, arg1, arg2):
stuff()
Now a has a foo method. We manually used the __get__ method of the foo function to create a bound method object like any other, except that since this method isn't associated with a class, we didn't pass __get__ a class. Pretty much the only difference is that when you print the method object, you see ?.foo instead of SomeClassName.foo.
Because that's how the descriptor protocol is specified:
descr.__get__(self, obj, type=None) --> value
cf https://docs.python.org/2/howto/descriptor.html#descriptor-protocol
The type argument allows access to the class on which the descriptor is looked up when it's looked up on a class instead of an instance. Since you can get the class from the instance, it's somehow redundant when the descriptor is looked up on an instance, so it has been made optional to allow the less verbose desc.__get__(obj) call (instead of desc.__get__(obj, type(obj))).

Categories

Resources