On 01/18/2017 08:24 AM, Ethan Furman wrote:
On 01/17/2017 11:05 PM, Steven D'Aprano wrote:

I've given a metaclass that disallows subclassing:

class MyClass(MyParent, metaclass=FinalMeta):
     ...


Ethan took that one step further by giving a class you inherit from to disallow
subclassing:

class MyClass(MyParent, Final):
     ...


Could we solve this problem with a decorator?


@final
class MyClass(MyParent):
     ...


Without needing to add any special magic to MyParent or MyClass, apart from the
decorator, can we make MyClass final? That would (in principle) allow us to
make a subclass, and *then* set the class final so that no more subclasses
could be made.


I thought that the decorator could simply set the class' metaclass:

def final(cls):
     if cls.__class__ is type:
         cls.__class__ = Meta
         return cls
     raise TypeErrror('Possible metaclass conflict')


but that's disallowed. Any ideas?

You still need to have the FinalMeta type and Final class available, but to use 
a
 decorator you'll need to scavenge the bits from the old class to make a new 
class
 of the same name and return that:

def final(cls):
     new_cls = Meta(cls.__name__, (Final, )+cls.__bases__, dict(cls.__dict__))
     return new_cls

Not sure if more is needed to handle __slots__, but this should get us started.

One problem with the above is existing instances won't be modified to inherit 
from the updated class.  I am unsure if that is solvable before 3.6, but in 3.6 
one can use the new __init_subclass__ to avoid a Final base class, a FinalMeta 
type, and just update the existing class:

def final(cls):
    def init_subclass(cls, **kwargs):
        raise Exception('Final class cannot be subclassed')
    cls.__init_subclass__ = classmethod(init_subclass)
    return cls

This can be used as a decorator at class creation time, or at any later date to 
lock down a class.  The downside is it's less obvious that the class is 
final... meaning there are no clues in the MRO.

--
~Ethan~
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to