Overriding Special Methods on an Instance

Overriding special methods on an instance

Python usually doesn't call the special methods (those with name surrounded by __) on the instance, but only on the class. (Although this is an implementation detail, it's characteristic of CPython, the standard interpreter.) So there's no way to override __repr__() directly on an instance and make it work. Instead, you need to do something like so:

class A(object):
def __repr__(self):
return self._repr()
def _repr(self):
return object.__repr__(self)

Now you can override __repr__() on an instance by substituting _repr().

Override a method at instance level

Please do not do this as shown. You code becomes unreadable when you monkeypatch an instance to be different from the class.

You cannot debug monkeypatched code.

When you find a bug in boby and print type(boby), you'll see that (a) it's a Dog, but (b) for some obscure reason it doesn't bark correctly. This is a nightmare. Do not do it.

Please do this instead.

class Dog:
def bark(self):
print "WOOF"

class BobyDog( Dog ):
def bark( self ):
print "WoOoOoF!!"

otherDog= Dog()
otherDog.bark() # WOOF

boby = BobyDog()
boby.bark() # WoOoOoF!!

Python overriding class (not instance) special methods

Special method __str__ defined in a class works only for the instances of that class, to have the different behavior for class objects you will have to do it in a metaclass of that class e.g. (python 2.5)

class Meta(type):
def __str__(self):
return "Klass"

class A(object):
__metaclass__ = Meta

def __str__(self):
return "instance"

print A
print A()

output:

Klass
instance

Is it a bad practice to override a method of a class in an instance?

The only valid answer here is the same as the old chestnut response the doctor gives when you ask what to do about the pain you feel when you jab yourself in the eye repeatedly: If it hurts, stop doing that. Your eye was not designed to have a finger jabbed in it.

So, yes, it is bad practice to add a method to an instance or a class when it breaks the expectations of the existing code that calls it.

Note that this has really nothing to do with the specifics of your examples. Any existing code can be broken this way, by replacing or shadowing something else with an implementation that doesn't fit the existing expectations.

Your examples can be trivially fixed; to replace the M.g method on a class, use a class method. To replace A.g on the instance, use a function that doesn't expect self. Both would fit the expectations of the existing code: that calling g does not require additional arguments.

Python is highly dynamic, and that gives you loads and loads of freedom, including the freedom to poke yourself in the proverbial eye. Try not to do that last thing or anything else that could hurt your code, and you should be fine.

How can I override a special method defined in a metaclass with a custom classmethod?

The __len__ defined in the class will always be ignored when using len(...) for the class itself: when executing its operators, and methods like "hash", "iter", "len" can be roughly said to have "operator status", Python always retrieve the corresponding method from the class of the target, by directly acessing the memory structure of the class. These dunder methods have "physical" slot in the memory layout for the class: if the method exists in the class of your instance (and in this case, the "instances" are the classes "GoodBar" and "BadBar", instances of "FooMeta"), or one of its superclasses, it is called - otherwise the operator fails.

So, this is the reasoning that applies on len(GoodBar()): it will call the __len__ defined in GoodBar()'s class, and len(GoodBar) and len(BadBar) will call the __len__ defined in their class, FooMeta

I don't really understand the interaction with the @classmethod
decorator.

The "classmethod" decorator creates a special descriptor out of the decorated function, so that when it is retrieved, via "getattr" from the class it is bound too, Python creates a "partial" object with the "cls" argument already in place. Just as retrieving an ordinary method from an instance creates an object with "self" pre-bound:

Both things are carried through the "descriptor" protocol - which means, both an ordinary method and a classmethod are retrieved by calling its __get__ method. This method takes 3 parameters: "self", the descriptor itself, "instance", the instance its bound to, and "owner": the class it is ound to. The thing is that for ordinary methods (functions), when the second (instance) parameter to __get__ is None, the function itself is returned. @classmethod wraps a function with an object with a different __get__: one that returns the equivalent to partial(method, cls), regardless of the second parameter to __get__.

In other words, this simple pure Python code replicates the working of the classmethod decorator:

class myclassmethod:
def __init__(self, meth):
self.meth = meth
def __get__(self, instance, owner):
return lambda *args, **kwargs: self.meth(owner, *args, **kwargs)

That is why you see the same behavior when calling a classmethod explicitly with klass.__get__() and klass().__get__(): the instance is ignored.

TL;DR: len(klass) will always go through the metaclass slot, and klass.__len__() will retrieve __len__ via the getattr mechanism, and then bind the classmethod properly before calling it.

Aside from the obvious metaclass solution (ie, replace/extend FooMeta)
is there a way to override or extend the metaclass function so that
len(BadBar) -> 9002?

(...)
To clarify, in my specific use case I can't edit the metaclass, and I
don't want to subclass it and/or make my own metaclass, unless it is
the only possible way of doing this.

There is no other way. len(BadBar) will always go through the metaclass __len__.

Extending the metaclass might not be all that painful, though.
It can be done with a simple call to type passing the new __len__ method:

In [13]: class BadBar(metaclass=type("", (FooMeta,), {"__len__": lambda cls:9002})):
...: pass


In [14]: len(BadBar)
Out[14]: 9002

Only if BadBar will later be combined in multiple inheritance with another class hierarchy with a different custom metaclass you will have to worry. Even if there are other classes that have FooMeta as metaclass, the snippet above will work: the dynamically created metaclass will be the metaclass for the new subclass, as the "most derived subclass".

If however, there is a hierarchy of subclasses and they have differing metaclasses, even if created by this method, you will have to combine both metaclasses in a common subclass_of_the_metaclasses before creating the new "ordinary" subclass.

If that is the case, note that you can have one single paramtrizable metaclass, extending your original one (can't dodge that, though)

class SubMeta(FooMeta):
def __new__(mcls, name, bases, ns, *,class_len):
cls = super().__new__(mcls, name, bases, ns)
cls._class_len = class_len
return cls

def __len__(cls):
return cls._class_len if hasattr(cls, "_class_len") else super().__len__()

And:


In [19]: class Foo2(metaclass=SubMeta, class_len=9002): pass

In [20]: len(Foo2)
Out[20]: 9002

Java overriding methods when creating new instance of a class

protected means (roughly) "available to sub-classes". (See this table.) Since the new View(this) { ... } creates a subclass, it is possible to override the method within it.

In this case it doesn't matter that you're in a different package. (See the protected line and second column in this table.) The fact that the method is in a subclass is sufficient to "get access" to a protected method.


Potential follow-up question: What sense does it make, if I can't call the method anyway?

All methods in Java are virtual. This means that whenever the View class performs a seemingly internal call to the onDraw method, this call will be dispatched to the overridden method.

Overriding the __str__ method for @classmethods in python

Python doesn't look for a __str__ on the class itself, just like it won't use __str__ set on an instance. This applies to all special methods, see Special method lookup in the Python datamodel:

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

In short, str(something) does not use something.__str__(), it essentially uses type(something).__str__(something) (*) exactly because you wouldn't want the definition of __str__ on a class to break when you use str(class_object), where class_object.__str__() doesn't have an instance to pass in as self.

You'll have to define a metaclass, because that's the 'thing' that makes classes and is returned by type(class_object):

class MetaAbc(type):
def __repr__(cls):
return "__repr__ on the metaclass"

def __str__(cls):
return "__str__ on the metaclass"

class Abc(metaclass=MetaAbc):
def __init__(self, name):
self.name = name

def __str__(self):
return f"Added {self.name}"

def __repr__(self):
return "instance method repr"

The metaclass=MetaAbc syntax tells Python to use MetaAbc instead of just type as metaclass of the Abc class; now type(Abc) returns MetaAbc:

>>> type(Abc)
<class '__main__.MetaAbc'>

and MetaAbc.__repr__ and MetaAbc.__str__ are used when representing a class, or converting it to a string; the methods on the class are used when dealing with an instance:

>>> Abc
__repr__ on the metaclass
>>> print(Abc)
__str__ on the metaclass
>>> Abc('foo')
instance method repr
>>> print(Abc('foo'))
Added foo

The @classmethod decorator does not put a method into a different namespace; class methods are normal attributes of a class and are simply bound differently. @classmethod's are still accessible on the instance, for example, but will always be passed the class object, even when accessed via the instance:

>>> Abc.class_method()
class method '__str__ on the metaclass'
>>> Abc("foo").class_method()
class method '__str__ on the metaclass'

(*) Python uses descriptor binding to implement methods, classmethods and staticmethods. Special method lookups look up the function object directly by traversing the class hierarchy to avoid triggering the normal binding process, then bind them manually. So str(something) translates to next(c.__dict__['__str__'] for c in type(something).__mro__ if '__str__' in c.__dict__).__get__(something, type(something))(). That's a bit of a mouthful, for normal methods this can be simplified to type(something).__str__(something) as that has the same effect.



Related Topics



Leave a reply



Submit