I am slightly confused about the way metaclasses work, having read
"Metaclass Programming In Python, Parts 1 and 2"
I get the fact that the instance of a metaclass is a class, but in
this case I fail to see why the following does'nt work:
... def __str__(cls):
... print "I am " + repr(cls)
...
UIAM, you subclassed type, but you didn't override __new__, so ...became essentially a pass-through call to type, i.e., effectively the same as
Class = type("Fish",(),{})
and since you passed it an empty dict to make its Class.__dict__ from,
went looking in Class.__dict__ for '__str__' and couldn't find it. So it
went looking for it according to Class.mro(), which led to finding it in
the object base class -- i.e., object.__dict__['__str__'] succeeded. But what it
got was an unbound method, because neither of two things (accessing an ordinary
method as an attribute of an instance object, or accessing a class method as an attribute
of either and instance or its class) was happening to bind to the method. So you got a complaint.
Note that it's complaining about __str__ of object, not __str__ of your class Meta.
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: descriptor '__str__' of 'object' object needs an argument
Note that your def __str__(cls) *does* exist in Meta.__dict__, but that wasn't the
dict used to construct Class.
Whereas this does:
... def __str__(self):
... return "I am " + repr(self)
...
In this case you are not overriding __new__ either, but the search for __new__ is finding
object.__new__, and that is making the object instance for you, of subtype Simple.
Actually, I think this amounts to Simple.__call__(), which must call cls.__new__() and go
down the mro chain to find object.__new__ and call it with cls (which would be Simple).
Looking for __str__ in this case is looking in type(obj).__dict__ first and finding it right there.
You are triggering it via attribute access, which dynamically creates the bound method that supplies
the self arg. Alternatively, type(obj).__str__ should be the unbound method, to which you could pass
obj, and type(obj).__dict__['__str__'] should be the plain function that becomes a method by the
dynamic magic. It can be called with obj as arg like the unbound method.
I am <__main__.Simple object at 0x402f676c>
The tutorial does mention this (slightly), but doesn't make it clear
why this is happening. I am probably missing something obvious though!
Thanks for the help,
To get the effect you originally wanted, one way could be:
... def __str__(cls): return 'I am '+ repr(cls)
... __str__ = classmethod(__str__)
... def __new__(cls, name): return type.__new__(cls, name, (),cls.__dict__.copy())
... "I am <class '__main__.Fish'>"
Interestingly,
"I am <class '__main__.Meta'>"
apparently the str builtin doing str(x) goes after type(x).__str__, e.g.,
"I am <class '__main__.Fish'>"
vs
"I am <class '__main__.Meta'>"
The above is my current view of how things work. I think/hope it's pretty close, but I am not
speaking from a code walk here, so there may be some details awry. Corrections welcome.
BTW, the __metaclass__ class variable and how that works is something that belongs in
a discussion of Python metaclass programming, but someone else can fill that in ;-)
HTH and does not mislead.
Regards,
Bengt Richter