468,511 Members | 1,521 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,511 developers. It's quick & easy.

Bypassing __setattr__ for changing special attributes

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
>>f = Foo()
f.__class__ = Bar
print f.__class__ is Foo
True

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

George

Feb 20 '07 #1
7 2019
George Sakkis wrote:
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
>f = Foo()
f.__class__ = Bar
print f.__class__ is Foo
True

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

George
>>object.__setattr__(f, '__class__', Bar)
f.__class__ is Bar
True

Ziga

Feb 20 '07 #2
On 20 fév, 05:39, "George Sakkis" <george.sak...@gmail.comwrote:
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...
Feb 20 '07 #3
On Mon, 19 Feb 2007 23:18:02 -0800, Ziga Seilnacht wrote:
George Sakkis wrote:
>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
>>f = Foo()
f.__class__ = Bar
print f.__class__ is Foo
True

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

George
>>>object.__setattr__(f, '__class__', Bar)
f.__class__ is Bar
True

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?

--
Steven.

Feb 20 '07 #4
On Feb 20, 3:54 am, "bruno.desthuilli...@gmail.com"
<bruno.desthuilli...@gmail.comwrote:
On 20 fév, 05:39, "George Sakkis" <george.sak...@gmail.comwrote:
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...
Well, let alone for the fact that two leading and trailing underscores
means 'special' in python, these attributes are not exactly what they
seem to be, i.e. a 'normal' type or dict:
>>type(object.__dict__['__class__'])
<type 'getset_descriptor'>
>>type(type.__dict__['__dict__'])
<type 'getset_descriptor'>
George

Feb 20 '07 #5
On Feb 20, 7:57 am, Steven D'Aprano
<s...@REMOVE.THIS.cybersource.com.auwrote:
On Mon, 19 Feb 2007 23:18:02 -0800, Ziga Seilnacht wrote:
George Sakkis wrote:
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
>f = Foo()
f.__class__ = Bar
print f.__class__ is Foo
True
Is there a way (even hackish) to bypass this, or at least achieve
somehow the same goal (change f's class) ?
George
>>object.__setattr__(f, '__class__', Bar)
f.__class__ is Bar
True

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?

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

George

Feb 20 '07 #6
On Feb 20, 12:57 pm, Steven D'Aprano
<s...@REMOVE.THIS.cybersource.com.auwrote:
On Mon, 19 Feb 2007 23:18:02 -0800, Ziga Seilnacht wrote:
George Sakkis wrote:
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
>f = Foo()
f.__class__ = Bar
print f.__class__ is Foo
True
Is there a way (even hackish) to bypass this, or at least achieve
somehow the same goal (change f's class) ?
George
>>object.__setattr__(f, '__class__', Bar)
f.__class__ is Bar
True

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
--
Steven.

Feb 21 '07 #7
Ziga Seilnacht wrote:

>>>object.__setattr__(f, '__class__', Bar)
f.__class__ is Bar
True
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:
>>f
<__main__.Foo object at 0xb7d1280c>
>>setattr(f, '__class__', Bar)
f
<__main__.Foo object at 0xb7d1280c>
Ok, didn't work, try your way:
>>object.__setattr__(f, '__class__', Bar)
f
<__main__.Bar object at 0xb7d1280c>
Wow! Ok, but getting back to Foo, with the *former* method:
>>setattr(f, '__class__', Foo)
f
<__main__.Foo object at 0xb7d1280c>
I can't explain this to myself, :(

Regards,

--
.. Facundo
..
Blog: http://www.taniquetil.com.ar/plog/
PyAr: http://www.python.org/ar/

Feb 27 '07 #8

This discussion thread is closed

Replies have been disabled for this discussion.

By using this site, you agree to our Privacy Policy and Terms of Use.