Reputation: 1382
I've been learning about metaclasses, and I was wondering if it's possible to add a new attribute to every class that's defined in python, not just those which inherit explicitly from a custom metaclass.
I can add a new attribute explicitly using a custom metaclass like this
class NewAttrMeta(type):
def __new__(cls, name, bases, attrs):
attrs['new_attr'] = 'new_thing'
return super().__new__(cls, name, bases, attrs)
class A(metaclass=NewAttrMeta):
...
print(A.new_attr)
$ 'new_thing'
But is it possible to force a change like this on every class that's defined, not just the ones which explicitly inherit from your custom metaclass?
I thought maybe as all classes are of type type
, if I overwrote type
itself with my custom metaclass, then all new classes might then inherit from it. And as metaclasses are subclasses of type
, then all classes defined that way would still be valid...
class NewAttrMeta(type):
def __new__(cls, name, bases, attrs):
attrs['new_attr'] = 'new_thing'
return super().__new__(cls, name, bases, attrs)
type = NewMagicMeta
But this only works if type
is passed in explicitly again:
class A(type):
...
print(A.new_attr)
$ 'new_thing'
class B():
...
print(B.new_attr)
$ AttributeError: type object 'A' has no attribute 'new_attr'
Why on earth am I trying to do this? I wanted to see if I could locally implement a version of the rejected PEP 472: "Support for indexing with keyword arguments" by overriding the __getitem__
method of every class which defined any version of __getitem__
. I'm only doing this for fun, so I would be interested in any insights or alternative ways to do that (the hackier the better!).
Upvotes: 0
Views: 84
Reputation: 110271
Python does not allow modification of built-in declared types. That means that dictionaries, lists, classes defineds in extensions like Numpy.ndarrays are "frozen" from Python code.
Even if that where possible, changing the metaclass for all classes would not change classes already defined. So, list
, etc... would not have affected. You could arrange your program so that it could "install" your class creation hooks before importing any other modules with class definition, though - so it could affect classes written in Python code. (Classes created in extensions are defined in C code and do not go through the metaclass-class creation process anyway)
type
is referenced as the metaclass for object, so even if you change type
in the builtins - which is possible, that won't automatically be used as a metaclass for anyone. It is used by default because it is what is returned by type(object)
All that said, it is possible to create something that would seek through all existing classes in a running Python program, and, whenever the class is defined in Python, to decorate a __getitem__
method if it exists, to accept keyword parameters.
But then:
The support with indexing arguments as proposed in PEP 472 requires changes to the parser and to the language specification - simply accepting keyword arguments in __getitem__
won´t make a[b=1]
work, or not be a syntax error. One would still have to write a.__getitem__(b=1)
An index name in __getitem__
is something very specific for a kind of objects. There is no way it would make sense for any class designed without that in mind. If a
is a list, what a[fish='golden']
would mean? And what if a
is a dict?
All in all, you'd already have a very cool class if you would come up with something for which it makes sense to have name passed in the index - and then you could just have any method to retrieve it, and use the regular parentheses notation for that a.get(fish="gold")
, or even, if you write the __call__
method: a(fish="gold")
Upvotes: 1