Method Resolution Order (MRO) is not compliant
uPy currently implements depth-first MRO, which is equivalent to one used pre-Python2.2.
References:
- http://python-history.blogspot.com/2010/06/method-resolution-order.html
- https://www.python.org/download/releases/2.3/mro/
[question] Comparing a callable with a function that gets promoted to a method during the instantiation of a class
(edited: see clarification later in thread)
Apologies if this question is answered in the documentation as I think it might be related to the already noted constraints on the method-resolution order, but I wasn't clear about it..or whom to ask since I'm not on Slack and the forum seems to be related to mostly porting issues.
Essentially I'm trying to differentiate a method/callable that's been assigned to a class from a method/callable that's been assigned to an instance. CPython splits this up by providing an instancemethod which requires one to access the im_func or nowadays the __func__ attribute in order to compare them. This is irrelevant in Micropython, however, so this is fine.
However, one thing I noticed was that Micropython doesn't seem to differentiate the layouts of the definition of a class from each of the classes that inherit from it..and any modification of that layout will also affect an object after it has already been instantiated. It seems as if the reference to the namespace of a class is actually "flattened" between all the classes that inherit from it. I'll try to explain with some examples.
The following example simply cases 1 base class, and two child classes. Assigning anything to the base class will immediately be accessible to all instances of classes that derive from it.
class base(object):
pass
class X(base):
pass
class Y(base):
pass
x, y = X(), Y()
assert(not hasattr(X, 'nothere') and not(hasattr(Y, 'nothere')))
assert(not hasattr(x, 'nothere') and not(hasattr(y, 'nothere')))
base.nothere = "i am here"
# now "X", and "Y" and their corresponding instances "x", and "y" now have this attribute.
assert(not hasattr(X, 'nothere') and not(hasattr(Y, 'nothere')))
assert(not hasattr(x, 'nothere') and not(hasattr(y, 'nothere')))
This is another example of the above, but it shows a value being passed between two already created instances without it being explictly assigned from outside a class. In this, the instance of the parent class simply adds an attribute to its class/definition, which any child classes/instances will immediately inherit.
class parent(object):
@classmethod
def flash(self):
self.hand = 'a cookie'
class child(parent):
def flash(self):
return self.hand if hasattr(self, 'hand') else 'nothing'
person1, person2 = parent(), child()
print(person2.flash(), 'in the hand')
person1.flash()
print('poof,', person2.flash())
Is this intended behaviour due to the constraints on the method resolution order as laid out in the documentation? Or is it just a side effect of the implementation? If it's the latter, I imagine that to fix it there'll be an unfortunate trade that would need to be made: If the namespace isn't just copied from the base class everytime a subclass is instantiated (or derived), then every instance of said subclass would need its own reference to the class' namespace so that when a base class is modified, it doesn't affect everything all of its references...something like that, anyways...