single argument super() - what's it for? [duplicate] - python

I wonder when to use what flavour of Python 3 super().
Help on class super in module builtins:
class super(object)
| super() -> same as super(__class__, <first argument>)
| super(type) -> unbound super object
| super(type, obj) -> bound super object; requires isinstance(obj, type)
| super(type, type2) -> bound super object; requires issubclass(type2, type)
Until now I've used super() only without arguments and it worked as expected (by a Java developer).
Questions:
What does "bound" mean in this context?
What is the difference between bound and unbound super object?
When to use super(type, obj) and when super(type, type2)?
Would it be better to name the super class like in Mother.__init__(...)?

Let's use the following classes for demonstration:
class A(object):
def m(self):
print('m')
class B(A): pass
Unbound super object doesn't dispatch attribute access to class, you have to use descriptor protocol:
>>> super(B).m
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'm'
>>> super(B).__get__(B(), B)
<super: <class 'B'>, <B object>>
super object bound to instance gives bound methods:
>>> super(B, B()).m
<bound method B.m of <__main__.B object at 0xb765dacc>>
>>> super(B, B()).m()
m
super object bound to class gives function (unbound methods in terms of Python 2):
>>> super(B, B).m
<function m at 0xb761482c>
>>> super(B, B).m()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: m() takes exactly 1 positional argument (0 given)
>>> super(B, B).m(B())
m
See Michele Simionato's "Things to Know About Python Super" blog posts series (1, 2, 3) for more information

A quick note, the new usage of super is outlined in PEP3135 New Super which was implemented in python 3.0. Of particular relevance;
super().foo(1, 2)
to replace the old:
super(Foo, self).foo(1, 2)

Related

Python base class can be object without __mro_entries__

Today I have discovered that python object without __mro_entries__ can be used as a base class.
Example:
class Base:
def __init__(self, *args):
self.args = args
def __repr__(self):
return f'{type(self).__name__}(*{self.args!r})'
class Delivered(Base):
pass
b = Base()
d = Delivered()
class Foo(b, d):
pass
print(type(Foo) is Delivered)
print(Foo)
True
Delivered(*('Foo', (Base(*()), Delivered(*())), {'__module__': '__main__', '__qualname__': 'Foo'}))
As a result Foo will be instance of a Delivered class and it's not a valid type.
I do understand use case of __mro_entries__ but what use case of using object without __mro_entries__ as a base class. Is it a bug at python?
TL;DR Not a bug, but an extreme abuse of the class statement.
A class statement is equivalent to a call to a metaclass. Lacking an explicit metaclass keyword argument, the metaclass has to be inferred from the base class(es). Here, the "metaclass" of the "class" b is Base, while the metaclass of d is Delivered. Since each is a non-strict subclass of a common metaclass (Base), Delivered is chosen as the more specific metaclass.
>>> Delivered('Foo', (b, d), {})
Delivered(*('Foo', (Base(*()), Delivered(*())), {}))
Delivered can be used as a metaclass because it accepts the same arguments that the class statement expects a metaclass to accept: a string for the name of the type, a sequence of parent classes, and a mapping to use as the namespace. In this case, Delivered doesn't use them to create a type; it simply prints the arguments.
As a result, Foo is bound to an instance of Delivered, not a type. So Foo is a class only in the sense that it was produced by a class statement: it is decidedly not a type.
>>> issubclass(Foo, Delivered)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: issubclass() arg 1 must be a class
>>> Foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Delivered' object is not callable

Setting attributes on __func__

In the documentation on instance methods it states that:
Methods also support accessing (but not setting) the arbitrary function attributes on the underlying function object.
But I can't seem to be able to verify that restriction. I tried setting both an arbitrary value and one of the "Special Attributes" of functions:
class cls:
def foo(self):
f = self.foo.__func__
f.a = "some value" # arbitrary value
f.__doc__ = "Documentation"
print(f.a, f.__doc__)
When executed, no errors are produced and the output is as expected:
cls().foo() # prints out f.a, f.__doc__
What is it that I'm misunderstanding with the documentation?
You are misunderstanding what is being said. It says that you can access but not set the attributes of the underlying function object from the method!
>>> class Foo:
... def foo(self):
... self.foo.__func__.a = 1
... print(self.foo.a)
... self.foo.a = 2
...
>>> Foo().foo()
1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in foo
AttributeError: 'method' object has no attribute 'a'
Note how foo.a is updated when you set it on the __func__ value, but you cannot set it directly using self.foo.a = value.
So the function object can be modified as you please, the method wrapper only provides read-only access to the attributes on the underlying function.

Does python consider any method without cls or self arguments implicitly as static?

The following are test classes with methods not taking in cls or self arguments and dont have #staticmethod decorator. They work like normal static methods without complaining about arguments. This seems contrary to my understanding of python methods. Does python automatically treat non-class, non-instance methods as static?
>>> class Test():
... def testme(s):
... print(s)
...
>>> Test.testme('hello')
hello
>>> class Test():
... def testme():
... print('no')
...
>>> Test.testme()
no
P.S: I am using python3.4
It sort of does, yes. However, note that if you call such an "implicit static method" on an instance, you will get an error:
>>> Test().testme()
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
Test().testme()
TypeError: testme() takes 0 positional arguments but 1 was given
This is because the self parameter still gets passed, which doesn't happen with a proper #staticmethod:
>>> class Test:
#staticmethod
def testme():
print('no')
>>> Test.testme()
no
>>> Test().testme()
no
Note that this doesn't work in Python 2:
>>> class Test(object):
... def testme():
... print 'no'
...
>>> Test.testme()
Traceback (most recent call last):
File "<ipython-input-74-09d78063da08>", line 1, in <module>
Test.testme()
TypeError: unbound method testme() must be called with Test instance as first argument (got nothing instead)
But in Python 3, unbound methods were removed, as Alex Martelli points out in this answer. So really all you're doing is calling a plain function that happens to be defined inside the Test class.

About dynamic assignment of attributes in Python 2.x

When I try to dynamically add attributes to instances of object class, I get an AttributeError. However, it is possible do it with instances of subclasses of object.
Does anybody know why?
>>> obj = object()
>>> obj.new_attr = "some value"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'new_attr'
>>> class MyClass(object):
... pass
...
>>> obj = MyClass()
>>> obj.new_attr = "some value"
>>> print obj.new_attr
some value
There is a note in the documentation about that:
http://docs.python.org/3/library/functions.html#object
Note: object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.
There is also a discussion about this on python mailing list:
https://mail.python.org/pipermail/python-list/2011-October/614249.html

How python attribute lookup process works?

When i say "python attribute lookup proccess" i mean: what python does when you write x.foo??
Searching the web i didn't found to much docs about this, one of the best papers i found resumed the proccess to the following steps (you can see the full article here)
If attrname is a special (i.e. Python-provided) attribute for objectname, return it.
Check objectname.__class__.__dict__ for attrname. If it exists and is a data-descriptor, return the descriptor result. Search all bases of objectname.__class__ for the same case.
Check objectname.__dict__ for attrname, and return if found. If objectname is a class, search its bases too. If it is a class and a descriptor exists in it or its bases, return the descriptor result.
Check objectname.__class__.__dict__ for attrname. If it exists and is a non-data descriptor, return the descriptor result. If it exists, and is not a descriptor, just return it. If it exists and is a data descriptor, we shouldn't be here because we would have returned at point 2. Search all bases of objectname.__class__ for same case.
Raise AttributeError.
At first this might seem right, but the attribute lookup process is a little bit more complicated, for example for x.foo, it doesn't behave the same if x is a class or an instance.
I have a found some samples that can't be explained by this way. Consider the following python code:
class Meta(type):
def __getattribute__(self, name):
print("Metaclass getattribute invoked:", self)
return type.__getattribute__(self, name)
def __getattr__(self, item):
print('Metaclass getattr invoked: ', item)
return None
class C(object, metaclass=Meta):
def __getattribute__(self, name):
print("Class getattribute invoked:", args)
return object.__getattribute__(self, name)
c=C()
Now check the following lines with the corresponding output:
>> C.__new__
Metaclass getattribute invoked: <class '__main__.C'>
<built-in method __new__ of type object at 0x1E1B80B0>
>> C.__getattribute__
Metaclass getattribute invoked: <class '__main__.C'>
<function __getattribute__ at 0x01457F18>
>> C.xyz
Metaclass getattribute invoked: <class '__main__.C'>
Metaclass getattr invoked: xyz
None
>> c.__new__
Class getattribute invoked: (<__main__.C object at 0x013E7550>, '__new__')
<built-in method __new__ of type object at 0x1E1B80B0>
>> c.__getattribute__
Class getattribute invoked: (<__main__.C object at 0x01438DB0>, '__getattribute__')
Metaclass getattribute invoked: <class '__main__.C'>
<bound method C.__getattribute__ of <__main__.C object at 0x01438DB0>>
>>
The conclusions i have been are (considering we're searching for x.foo):
__getattribute__ is different for instances of < type 'type' > and < type 'object' >. For C.foo(), 'foo' is searched first on C.__dict__ and returned if found (instead of searching type(C)) and for x.foo() 'foo' is searched on type(x).__dict__ and on x.__dict__.
__getattribute__ method is always resolved on type(x), what i don't understand here is the last case: c.__getattribute__, isn't object contains a method __getattribute__ (and C inherits from object), so why does metaclass getattribute method gets called.
Can someone explain this please?? or at less tell me where can i find some documentation about this, thanks.
If you added print("Metaclass getattribute invoked:", self, name) you'd see:
>>> c.__getattribute__
Class getattribute invoked: <__main__.C object at 0x2acdbb1430d0> __getattribute__
Metaclass getattribute invoked: <class '__main__.C'> __name__
<bound method C.__getattribute__ of <__main__.C object at 0x2acdbb1430d0>>
The metaclass __getattribute__ is getting invoked in order to build the repr of the expression c.__getattribute__, so that it can print C's __name__.
btw, __getattribute__ works the same for classes and metaclasses; the attribute is looked up first on the instance then on the instance's type.
>>> Meta.foo = 1
>>> C.foo
('Metaclass getattribute invoked:', <class '__main__.C'>, 'foo')
1
>>> c.foo
('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __getattribute__
AttributeError: 'C' object has no attribute 'foo'
>>> C.bar = 2
>>> c.bar
('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'bar')
2

Categories

Resources