On 01/16/2017 11:32 PM, Steven D'Aprano wrote:
On Tuesday 17 January 2017 18:05, Steven D'Aprano wrote:

I wish to emulate a "final" class using Python, similar to bool:

I may have a solution: here's a singleton (so more like None than bools) where
instantiating the class returns the singleton, and subclassing the class fails:

class DoneMeta(type):
     _final = None
     def __new__(meta, name, bases, ns):
         if meta._final is None:
             meta._final = cls = super().__new__(meta, name, bases, ns)
             return cls
         elif meta._final in bases:  # Not sure this check is needed.
             raise TypeError('base class is final and cannot be subclassed')

This will make DoneMeta a one-shot, meaning you'll have to make more DoneMeta's 
if you need more than one unsubclassable class.

class DoneType(metaclass=DoneMeta):
     __slots__ = ()
     _instance = None
     def __new__(cls):
         if cls._instance is None:
             cls._instance = inst = super().__new__(cls)
             return inst
         return cls._instance
     def __repr__(self):
         return '<DONE>'

And this has to do with single instances, which is not what you asked about.

Here's some sample code that creates a Final class; any class that subclasses 
from it cannot be further subclassed:

-- 8< ------------------------------------------------------------

Final = None

class FinalMeta(type):

    def __new__(metacls, cls, bases, clsdict):
        print('-' * 50)
        print('class: ', cls)
        print('bases: ', bases)
        if Final is not None:
            for base in bases:
                if base is not Final and  issubclass(base, Final):
                    print('should raise')
        print('-' * 50)
        return type.__new__(metacls, cls, bases, clsdict)

class Final(metaclass=FinalMeta):
    pass

class One(Final):
    pass

class Two(One):
    pass

class Three(Two):
    pass

class Ten(Final):
    pass

-- 8< ------------------------------------------------------------

Change the "should raise" to a raise, remove the other print()s, and away you 
go.

Should work in any Python 3.

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

Reply via email to