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__
(,
,
,
,
,
,
,
)
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)
py> type(WithMeta).__mro__
(, , )
> 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/