On Wed, 18 Dec 2013 11:15:03 +1300, Gregory Ewing wrote: > Steven D'Aprano wrote: >> Well, that is a surprise, but I don't think that is intended behaviour. >> I think that's something which only works by accident. The intention is >> that __class__ returns the instance's type, not arbitrary values. > > Well, a proxy object would obviously return a suitable class-like > object. I was just demonstrating that it's possible to override what > __class__ returns.
You can certainly do it with a __getattribute__ method: py> class K(object): ... def __getattribute__(self, name): ... if name == '__class__': return 42 ... return super().__getattribute__(name) ... py> k = K() py> k.__class__ 42 but I think that counts as "shoot yourself in the foot" category. > I don't think it's an accident, because the weakref module uses this for > its proxy objects. Just a minute, we seem to be talking about completely different things here. You demonstrated setting __class__ to a non-class object inside a class statement, emphasis on the *non-class* part. Here's a simpler version showing the same thing: py> class Q(object): ... __class__ = 42 ... py> q = Q() py> q.__class__ 42 py> type(q) <class '__main__.Q'> Here's an equivalent way: py> Q = type("Q", (object,), {'__class__': 23}) py> Q().__class__ 23 It's the *non-class* part I reckon is an accident, or a bug. Telling me that weakproxy sets __class__ to a *class* doesn't argue for or against me. Ignoring virtual __getattribute__ attributes, I cannot see any other way to set the __class__ of an object to be a non-class. (I suppose you can do anything you like with a metaclass, but that falls firmly into "consenting adults" territory.) Unless you "break" access to the __class__ descriptor first, as in the Q class above, there doesn't seem to be any way to set it to a non-class or an incompatible class. You can't set it on the class: py> class C(object): ... pass ... py> C.__class__ = 23 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __class__ must be set to a class, not 'int' object py> C.__dict__['__class__'] = 23 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'mappingproxy' object does not support item assignment nor can you set it on the instance: py> c = C() py> c.__class__ = 23 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: __class__ must be set to a class, not 'int' object You can set it on the instance dict directly, but it doesn't do you any good because the descriptor overrides it: py> c.__dict__['__class__'] = 23 py> c.__class__ <class '__main__.C'> Somebody has gone to a *lot* of trouble to ensure that __class__ always returns an actual class, and I'm not sure the Q example above is a deliberate hole in that. Your weakref example is not a counter-example: > >>> import weakref > >>> class C(object): > ... pass > ... > >>> c = C() > >>> p = weakref.proxy(c) > >>> p.__class__ > <class '__main__.C'> > >>> type(p) > <type 'weakproxy'> since both weakproxy and C are classes. This is a good example though of when type(obj) and obj.__class__ can legitimately differ. This leads to another question: we've now seen two examples where (presumably) the internal type field and __class__ differ. In the weakproxy case, type(obj) returns the internal type field. In the "regular" case, where you set obj.__class__ to a class, type(obj) returns the new (external) type. How the hell does it decide which one to return? -- Steven -- https://mail.python.org/mailman/listinfo/python-list