7

I got a surprising PyCharm warning in the following metaclass

Usually first parameter of such methods is named 'mcs'

Is this some convention I'm unaware of like naming the first parameter self or cls? I tried searching SO but didn't find any hit. The code was taken from this post.

screenshot of warning

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)
bad_coder
  • 11,289
  • 20
  • 44
  • 72

3 Answers3

9

For metaclasses, the methods that would normally be "class methods" - like __prepare__ and __new__ will receive on the first parameter the metaclass itself.

There is no convention about the naming of that parameter. Usually people unaware, or simply not worried, about the full extent of what is really going on with a metaclass, will simply name these cls as if it were a normal class and life follows. I believe most IDEs won't recognize anything other, and if the highlighted "correct" use of "cls" and "self" as the first parameter to methods, they might even mark anything else as "wrong". One always has to keep in mind these are just conventions - and linters and syntax highlighters are far less flexible than the human mind (at least currently), and their word should not be taken as the law.

That said, unlike the names "cls" and "self", there is no strong convention for when the first parameter is the metaclass itself - I usually name it mcls, but mcs would be fine, while metaclass is a bit dangerous, as it is a reserved keyword in the class definition itself (when using the class statement).

However, it happens that the class used in the metaclass argument is exactly the one passed to the first argument of the metaclass __new__ method, so, yes, it might "fix an inconsistency" if the first parameter in a metaclass __new__ is named metaclass. (I never thought about that).

In other words, when defining a class with class MyClass(ClassA, ClassB, metaclass=MyMeta): , the MyMeta is what is passed in the first parameter of MyMeta.__new__, and if it is named metaclass, it will be as if Python was passing it as a "named parameter". Even though, on the class statement side, the metaclass name is hardcoded in the language definition, and on the def __new__ side, whatever name is on the first parameter will be used.

In [98]: class M(type):
    ...:     def __new__(metaclass, name, bases, namespace, **kwargs):
    ...:         return super().__new__(metaclass, name, bases, namespace, **kwargs)
    ...: 

In [99]: class A(metaclass=M): 
    ...:    pass

Anyway, as I said, I am usually happy with mcls.

And conversely, and equally important: any other methods on a metaclass, that on an ordinary class would be self will be receiving as parameter the class created with the metaclass - So it is usual to name those cls instead of self, as it is easier to keep in mind what an instance of the metaclass represents.

Thus, in a metaclass for the methods __init__, __getattr__, __call__ and whatever one finds useful, the first argument makes sense as being cls instead of self.

Going back to one of your snippets:

class Meta(type):
    @classmethod
    def __prepare__(mcls, metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

As I commented, you may call the first argument metacls, but the second argument to __new__ and __prepare__ should be name not cls. Again: Python passes these as ordered arguments, so it would work if you named it cls, but its contents are the declared class name as a string, nonetheless. On one hand, the cls name normally designates the class itself, and it does not even exist when these methods are called - it will exist only from the point where you call super().__new__ inside the __new__ metaclass method.

As for the fourth parameter, you call clsdict, I know of no strong convention - clsdictis clear enough, as would be namespace - but over the years, I've been using simply dct, and have used ns on occasion as well.

And finally, as stated above, I would not expect PyCharm or any other style highlighter (or "corrector") to understand better than me what are my intentions with a metaclass: I will just ignore them.

jsbueno
  • 99,910
  • 10
  • 151
  • 209
5

The preferred value could be changed in File | Settings | Editor | Inspections | Python | Methods having troubles with first parameter, see screenshot:

Inspection settings

user2235698
  • 7,053
  • 1
  • 19
  • 27
  • Pycharm is a joke if it allows this to be "configured" but just offer a choice of two configurations, and not a free field there. (Or at least the set with all reasonable options) – jsbueno Nov 16 '21 at 13:59
3

I think metaclasses are used much less frequently than regular classes, so it's only logical that a convention as strong and widespread as self and cls doesn't exist.

Google search results are slightly in favor of mcs:

__new__(mcs 3490 results

__new__(mcls 2230 results

__new__(metacls 1940 results

__new__(metaclass 919 results

Personally, I would be leaning towards metacls as it's more descriptive and feels more consistent with cls to me.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
mportes
  • 1,589
  • 5
  • 13