class Meta(type):
def __new__(cls, class_name, bases, attrs):
a={}
for name, val in attrs.items():
if name.startswith("__"):
a[name] = val
else:
a[name.upper()] = val
return(class_name, bases, a)
class D(metaclass=Meta):
x = 5
y = 8
def hello (self):
print("hi")
c = D()
c.hello
Why am I getting this error:
'tuple' object is not callable line 20, in c = D()
Cant finde the reason can someone help me out?
(code based on the teachings of the video at https://www.youtube.com/watch?v=NAQEj-c2CI8 )
The last line inside your metaclasse __new__
method, which reads
return(class_name, bases, a)
Returns a plain tuple - it should (and indeed, on the video you comment where you got the inspiration for your studies) call either type
or type.__new__
: there produce a new class that is then ready to be used:
return type(class_name, bases, a)
Although the video is a nice first introduction to playt around with metaclasses, (1) this is an advanced topic, and one actually can make a whole career in Python without ever needing to write a metaclass;
(2) on calling type
instead of type.__new__
, the video is not technically correct: it works, but it has a different effect in that the resulting class, although being modified upon its creation, will have the regular type
as its metaclass - by calling type
directly, all the references to the metaclass itself (Meta
in your snippet) are discarded. If instead you call super().__new__(cls, class_name, bases, a)
, the created class will be an instance of the metaclass (conveyed under the name cls
in this call), and all subclasses of the created class (D
in the example), will also have the same metaclass and be created through this __new__
method.
One extra detail on your experiment: you are testing for attributes that starts with a double underscore ("__
") - The Python compilation process automatically mangle all such names, (but names "sandwiched" between a pair of "__
") to include the class name itself as a prefix - so if D
would have a __x
attribute, it would show up as _D__x
in the attrs
dict received in the metaclass -
I see in the linked video that is the example there; however this part of the code is never tested or verified: no __
attributes are demonstrated to be preserved. It will preserve reserved "magic" names as __init__
, though. As stated earlier, the video is not 100% good for teaching, as the author itself seems to be just showing some of his own experimenting.
And one last comment regarding the specifc error you are getting: whatever the metaclass __new__
method returns is used by Python as the object bound to the name on the class statement (D
in this case). So, D
becames an "ordinary" variable containing a tuple, instead of a new class. When you execute D()
, Python tries to call the object, and you get the error that tuples are not callable.