Bypassing __setattr__ for changing special attributes

G

George Sakkis

I was kinda surprised that setting __class__ or __dict__ goes through
the __setattr__ mechanism, like a normal attribute:

class Foo(object):
def __setattr__(self, attr, value):
pass

class Bar(object):
pass
True

Is there a way (even hackish) to bypass this, or at least achieve
somehow the same goal (change f's class) ?

George
 
Z

Ziga Seilnacht

George said:
I was kinda surprised that setting __class__ or __dict__ goes through
the __setattr__ mechanism, like a normal attribute:

class Foo(object):
def __setattr__(self, attr, value):
pass

class Bar(object):
pass

True

Is there a way (even hackish) to bypass this, or at least achieve
somehow the same goal (change f's class) ?

George
True

Ziga
 
B

bruno.desthuilliers

I was kinda surprised that setting __class__ or __dict__ goes through
the __setattr__ mechanism, like a normal attribute:

But __class__ and __dict___ *are* 'normal' attributes...
 
S

Steven D'Aprano



This version is arguably more "correct", although a tad longer to write,
and doesn't need you to hard-code the class superclass:

super(f.__class__, f).__setattr__('__class__', Bar)


But what surprised me was that this *didn't* work:
f = Foo()
f.__dict__['__class__'] = Bar
f.__class__
<class '__main__.Foo'>


Unless I'm confused, it looks like instance.__class__ bypasses the usual
lookup mechanism (instance.__dict__, then instance.__class__.__dict__) for
some reason.
Foo.x = 1 # stored in class __dict__
f.x 1
f.__dict__['x'] = 2 # stored in instance __dict__
f.x 2
Foo.x
1

But __class__ doesn't behave like this. Why?
 
G

George Sakkis


This version is arguably more "correct", although a tad longer to write,
and doesn't need you to hard-code the class superclass:

super(f.__class__, f).__setattr__('__class__', Bar)

But what surprised me was that this *didn't* work:
f = Foo()
f.__dict__['__class__'] = Bar
f.__class__

<class '__main__.Foo'>

Unless I'm confused, it looks like instance.__class__ bypasses the usual
lookup mechanism (instance.__dict__, then instance.__class__.__dict__) for
some reason.
Foo.x = 1 # stored in class __dict__
f.x 1
f.__dict__['x'] = 2 # stored in instance __dict__
f.x 2
Foo.x

1

But __class__ doesn't behave like this. Why?

Perhaps because __class__ is a special descriptor:
type(object.__dict__['__class__'])
<type 'getset_descriptor'>

George
 
F

Fuzzyman


This version is arguably more "correct", although a tad longer to write,
and doesn't need you to hard-code the class superclass:

super(f.__class__, f).__setattr__('__class__', Bar)

But what surprised me was that this *didn't* work:
f = Foo()
f.__dict__['__class__'] = Bar
f.__class__

<class '__main__.Foo'>

Unless I'm confused, it looks like instance.__class__ bypasses the usual
lookup mechanism (instance.__dict__, then instance.__class__.__dict__) for
some reason.
Foo.x = 1 # stored in class __dict__
f.x 1
f.__dict__['x'] = 2 # stored in instance __dict__
f.x 2
Foo.x

1

But __class__ doesn't behave like this. Why?

Magic attributes like __class__ are looked up on the class rather than
the instance.

Fuzzyman
http://www.voidspace.org.uk/python/articles.shtml
 
F

Facundo Batista

Ziga Seilnacht wrote:


Interesting, but... why I must do this? And, I must *always* do this?

With Foo and Bar like the OP coded (just two new style classes, f is
instance of Foo), see this:
<__main__.Foo object at 0xb7d1280c>


Ok, didn't work, try your way:
<__main__.Bar object at 0xb7d1280c>


Wow! Ok, but getting back to Foo, with the *former* method:
<__main__.Foo object at 0xb7d1280c>


I can't explain this to myself, :(

Regards,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top