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/

Reply via email to