Whats the correct way to implement a metaclass with a different signature than `type`?

2024/9/25 0:37:50

Say I want to implement a metaclass that should serve as a class factory. But unlike the type constructor, which takes 3 arguments, my metaclass should be callable without any arguments:

Cls1 = MyMeta()
Cls2 = MyMeta()
...

For this purpose I defined a custom __new__ method with no parameters:

class MyMeta(type):def __new__(cls):return super().__new__(cls, 'MyCls', (), {})

But the problem is that python automatically calls the __init__ method with the same arguments as the __new__ method, so trying to call MyMeta() ends up throwing an exception:

TypeError: type.__init__() takes 1 or 3 arguments

Which makes sense, since type can be called with 1 or 3 arguments. But what's the correct way to fix this? I see 3 (4?) options:

  • I could add an empty __init__ method to my metaclass, but since I'm not sure if type.__init__ does anything important, this might not be a good idea.
  • I could implement an __init__ method that calls super().__init__(cls.__name__, cls.__bases__, vars(cls)).
  • I could use a meta-metaclass and override its __call__ method, rather than messing with __new__ and __init__.
  • Bonus option: Maybe I shouldn't try to change the signature?

So my question is: Are the 3 solutions I listed correct or are there any subtle bugs hidden in them? Which solution is best (i.e. the most correct)?

Answer

An interface deviating from the parent signature is a questionable design in regular classes too. You don't need the extra complexity of metaclasses to get into this kind of mess - you can cause the same new/init jumble by subclassing a datetime or whatever.

I want to have a metaclass and an easy way to create instances of that metaclass.

The usual pattern in Python is to write a factory using a from_something classmethod. To take the example of creating datetime instances from a different init signature, there is for example datetime.fromtimestamp, but you have many other examples too (dict.fromkeys, int.from_bytes, bytes.fromhex...)

There is nothing specific to metaclasses here, so use the same pattern:

class MyMeta(type):@classmethoddef from_no_args(cls, name=None):if name is None:name = cls.__name__ + 'Instance'return cls(name, (), {})

Usage:

>>> class A(metaclass=MyMeta):
...     pass
... 
>>> B = MyMeta.from_no_args()
>>> C = MyMeta.from_no_args(name='C')
>>> A.__name__
'A'
>>> B.__name__
'MyMetaInstance'
>>> C.__name__
'C'
https://en.xdnf.cn/q/71641.html

Related Q&A

Python -- Regex -- How to find a string between two sets of strings

Consider the following:<div id=hotlinklist><a href="foo1.com">Foo1</a><div id=hotlink><a href="/">Home</a></div><div id=hotlink><a…

Kivy TextInput horizontal and vertical align (centering text)

How to center a text horizontally in a TextInput in Kivy?I have the following screen:But I want to centralize my text like this:And this is part of my kv language:BoxLayout: orientation: verticalLabe…

How to capture python SSL(HTTPS) connection through fiddler2

Im trying to capture python SSL(HTTPS) connections through Fiddler2 local proxy. But I only got an error.codeimport requests requests.get("https://www.python.org", proxies={"http": …

removing leading 0 from matplotlib tick label formatting

How can I change the ticklabels of numeric decimal data (say between 0 and 1) to be "0", ".1", ".2" rather than "0.0", "0.1", "0.2" in matplo…

How do I check if an iterator is actually an iterator container?

I have a dummy example of an iterator container below (the real one reads a file too large to fit in memory):class DummyIterator:def __init__(self, max_value):self.max_value = max_valuedef __iter__(sel…

Python Terminated Thread Cannot Restart

I have a thread that gets executed when some action occurs. Given the logic of the program, the thread cannot possibly be started while another instance of it is still running. Yet when I call it a sec…

TypeError: NoneType object is not subscriptable [duplicate]

This question already has an answer here:mysqldb .. NoneType object is not subscriptable(1 answer)Closed 8 years ago.The error: names = curfetchone()[0]TypeError: NoneType object is not subscriptable. …

Where can I find numpy.where() source code? [duplicate]

This question already has answers here:How do I use numpy.where()? What should I pass, and what does the result mean? [closed](2 answers)Closed 4 years ago.I have already found the source for the num…

NSUserNotificationCenter.defaultUserNotificationCenter() returns None in python

I am trying to connect to the Mountain Lion notification center via python. Ive installed pyobjc and am following the instructions here and here. Also see: Working with Mountain Lions Notification Cent…

Flask app hangs while processing the request

I have a simple flask app, single page, upload html and then do some processing on it on the POST; at POST request; i am using beautifulsoup, pandas and usually it takes 5-10 sec to complete the task. …