Steven D'Aprano wrote: > On Sat, Mar 05, 2022 at 04:42:55PM -0000, Jason Madden wrote: > > zope.interface relies on this behaviour. > > The example you give shows that Interface is a class. It merely has a > metaclass which is not `type`. (I presume that is what's going on > behind the scenes.)
I don't think that's *quite* correct. `Interface` is an ordinary object, an instance of `InterfaceClass`. `InterfaceClass` despite the name, is not actually a metaclass — `type` isn't anywhere in the MRO. The original example is similar in that 1 is an instance of `int` and `int` isn't a metaclass either. py> InterfaceClass.__mro__ (<class 'zope.interface.interface.InterfaceClass'>, <class 'zope.interface.interface.InterfaceClass'>, <class 'zope.interface.interface.InterfaceBase'>, <class 'zope.interface.interface.NameAndModuleComparisonMixin'>, <class 'zope.interface.interface.Specification'>, <class 'zope.interface.interface.SpecificationBase'>, <class 'zope.interface.interface.Element'>, <class 'object'>) The simplified version of what zope.interface is doing is this: py> class SomethingBase: ... def __init__(self, name, bases, ns): ... pass py> Something = SomethingBase('Something', (), {}) py> class MySomething(Something): ... pass py> MySomething <__main__.SomethingBase object at 0x100c81910> py> isinstance(MySomething, type) False Contrast with a true metaclass: py> class Meta(type): ... pass py> class WithMeta(metaclass=Meta): ... pass py> type(WithMeta) <class '__main__.Meta'> py> type(WithMeta).__mro__ (<class '__main__.Meta'>, <class 'type'>, <class 'object'>) > I'm asking about the example that Serhiy shows, where a class inherits > from something which is not a class at all. In his example, the base is > 1, although it gives a TypeError. I'm asking if that sort of thing is > suppposed to work, and if so, how? Python accepts anything callable with the right number of arguments as a metaclass. If you specify it explicitly, it can be just a function: py> def Meta(*ignored): ... return 42 ... py> class X(metaclass=Meta): ... pass ... py> X 42 If you don't specify it explicitly, Python uses the type of the first base as the metaclass (ignoring conflicts and derived metaclasses) and calls that. In neither case does it have to be, or return, a class/type. We can actually write a class statement using a number as a base if we define an appropriate `__new__` method: py> class MyInt(int): ... def __new__(cls, *args): ... if len(args) == 1: ... return int.__new__(cls, *args) ... return int.__new__(cls, 42) py> one = MyInt(1) py> one 1 py> isinstance(one, int) True py> isinstance(one, type) False py> class A(one): ... pass ... py> A 42 _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-le...@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/2WV6GXDQLRA4PYQHZLEKZUIAOH3EYKNF/ Code of Conduct: http://python.org/psf/codeofconduct/