Bypassing __setattr__ for changing special attributes

Discussion in 'Python' started by George Sakkis, Feb 20, 2007.

  1. 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
     
    George Sakkis, Feb 20, 2007
    #1
    1. Advertisements

  2. 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
     
    Ziga Seilnacht, Feb 20, 2007
    #2
    1. Advertisements

  3. George Sakkis

    Guest

    On 20 fév, 05:39, "George Sakkis" <> wrote:
    > 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, 2007
    #3
  4. 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.
     
    Steven D'Aprano, Feb 20, 2007
    #4
  5. On Feb 20, 3:54 am, ""
    <> wrote:
    > On 20 fév, 05:39, "George Sakkis" <> wrote:
    >
    > > 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
     
    George Sakkis, Feb 20, 2007
    #5
  6. On Feb 20, 7:57 am, Steven D'Aprano
    <> wrote:
    > 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
     
    George Sakkis, Feb 20, 2007
    #6
  7. George Sakkis

    Fuzzyman Guest

    On Feb 20, 12:57 pm, Steven D'Aprano
    <> wrote:
    > 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.
     
    Fuzzyman, Feb 21, 2007
    #7
  8. 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/
     
    Facundo Batista, Feb 27, 2007
    #8
    1. Advertisements

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Anand
    Replies:
    0
    Views:
    474
    Anand
    Dec 18, 2003
  2. Chris Young

    Recursion with __setattr__

    Chris Young, Oct 16, 2005, in forum: Python
    Replies:
    0
    Views:
    389
    Chris Young
    Oct 16, 2005
  3. Thomas Heller

    __getattr__, __setattr__

    Thomas Heller, Oct 20, 2005, in forum: Python
    Replies:
    3
    Views:
    716
    Thomas Heller
    Oct 20, 2005
  4. L.C. Rees
    Replies:
    3
    Views:
    418
    Scott David Daniels
    Feb 8, 2006
  5. Joshua Kugler
    Replies:
    12
    Views:
    448
    Joshua Kugler
    Apr 29, 2008
Loading...

Share This Page