Impossible to change methods with special names of instances ofnew-style classes?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Joseph Barillari

    Impossible to change methods with special names of instances ofnew-style classes?

    Hi python-list,

    I've just started using new-style classes and am a bit confused as to
    why I can't seem to alter methods with special names
    (__call__, etc.) of new-style class instances. In other words, I
    can do this:
    >>class Z:
    .... pass
    ....
    >>z = Z()
    >>z.__call__ = lambda : 33
    >>z()
    33

    But apparently I can't do this:
    >>class NZ(object):
    .... pass
    ....
    >>nz = NZ()
    >>nz.__call__ = lambda : 33
    >>nz()
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: 'NZ' object is not callable


    I found this post from Bengt Richter three years ago which addressed a
    related problem:


    >Apparently the issue, as stated implicitly or explicitly by most of
    >you, is that new-style class instances essentially defer their magic
    >methods to the class's static versions of same. This is good to know :)
    Actually, it's not just the "magic" methods. If you have an
    instance a of a newstyle class A, any attribute lookup a.attr will
    undergo the same search first to see if attr is a descriptor
    object, and if not, *then* to look in the instance attribute
    directory. But the descriptor search doesn't start in
    inst.__dict__, it goes through the chain of classes and base
    classes provided by type(inst).mro( ), which starts in
    type(inst). And for our class A instance a, type(a) will be A, so
    the search for a.attr starts there. Same applies to a.__str__. This
    ensures that all instances of the same class will share the same
    methods. The way a method, which is just a class variable with a
    function as its value, gets to be a callable bound method, is the
    same as any attribute lookup looking for a descriptor with a
    __get__ method (which a function also has, for this purpose). If
    the descriptor doesn't have a __set__ method as well, then an
    instance attribute takes priority. If there is a __set__ method,
    and instance attribute can't shadow the attribute name, and the
    descriptor __get__ method takes precedence. Unshadowed, a method
    search looks something like

    cbm = ((base for base in type(inst).mro( ) if 'attr' in base.__dict__)
    .next().__dict_ _['attr'].__get__(inst, type(inst)))

    if this doesn't succeed and meet the __set__ vs shadowing logic, then you get
    the instance attribute per se.

    ....but if I understand correctly, this suggests that if the runtime
    can't find the attribute in the chain of classes and base classes, it
    will look in the instance dictionary. The behavior of NZ above suggests that
    it the runtime is _not_ doing that for __call__ as it would for a non-special name:
    >>class NZ(object):
    .... pass
    ....
    >>nz = NZ()
    >>nz.f = lambda : 42
    >>nz.f()
    42

    My question is: did something about the way the special method names are
    implemented change for new-style classes?

    best, and thanks in advance,

    Joe
  • cokofreedom@gmail.com

    #2
    Re: Impossible to change methods with special names of instances ofnew-style classes?

    >
    My question is: did something about the way the special method names are
    implemented change for new-style classes?
    >
    >>class old:
    pass
    >>class new(object):
    pass
    >>testone = old()
    >>testone.__cal l__ = lambda : 33
    >>testone()
    33
    >>testtwo = new()
    >>testtwo.__cal l__ = lambda : 33
    >>testtwo()
    Traceback (most recent call last):
    File "<pyshell#3 0>", line 1, in <module>
    testtwo()
    TypeError: 'new' object is not callable
    >>old.__call_ _
    Traceback (most recent call last):
    File "<pyshell#3 1>", line 1, in <module>
    old.__call__
    AttributeError: class old has no attribute '__call__'
    >>new.__call_ _
    <method-wrapper '__call__' of type object at 0x00CC40D8>
    >>testone.__cal l__
    <function <lambdaat 0x00C35EB0>
    >>testtwo.__cal l__
    <function <lambdaat 0x00C35B70>
    >>dir(testtwo )
    ['__call__', '__class__', '__delattr__', '__dict__', '__doc__',
    '__getattribute __', '__hash__', '__init__', '__module__', '__new__',
    '__reduce__', '__reduce_ex__' , '__repr__', '__setattr__', '__str__',
    '__weakref__']
    >>dir(testone )
    ['__call__', '__doc__', '__module__']
    >>dir(new)
    ['__class__', '__delattr__', '__dict__', '__doc__',
    '__getattribute __', '__hash__', '__init__', '__module__', '__new__',
    '__reduce__', '__reduce_ex__' , '__repr__', '__setattr__', '__str__',
    '__weakref__']
    >>dir(old)
    ['__doc__', '__module__']

    I don't see __call__ in either class structures, but for new style
    classes it is a wrapper and for old it is nothing. Not sure if that
    helps, but this is rather over my head.

    Comment

    • samwyse

      #3
      Re: Impossible to change methods with special names of instances ofnew-style classes?

      On Jul 8, 4:56 pm, Joseph Barillari <pyt...@barilla ri.orgwrote:
      My question is: did something about the way the special method names are
      implemented change for new-style classes?
      Just off the top of my head, I'd guess that it's due to classes
      already having a default __call__ method, used when you instatiate.
      Remember, the Python compiler doesn't know the difference between
      this:
      a = MyClass
      instance = a()
      and this:
      a = myFunc
      result = a()

      Comment

      • Terry Reedy

        #4
        Re: Impossible to change methods with special names of instancesof new-style classes?



        samwyse wrote:
        On Jul 8, 4:56 pm, Joseph Barillari <pyt...@barilla ri.orgwrote:
        >
        >My question is: did something about the way the special method names are
        >implemented change for new-style classes?
        I believe the difference is that for new-style classes, when special
        methods are called 'behind the scenes' to implement built-in syntax and
        methods, they are looked up directly on the class instead of first on
        the instance. Note that functions attached to instances are *not*
        methods and do not get combined with the instance as a bound method.

        Comment

        • Steven D'Aprano

          #5
          Re: Impossible to change methods with special names of instances ofnew-style classes?

          On Tue, 08 Jul 2008 17:56:45 -0400, Joseph Barillari wrote:
          Hi python-list,
          >
          I've just started using new-style classes and am a bit confused as to
          why I can't seem to alter methods with special names (__call__, etc.) of
          new-style class instances.
          [deploy weapon of mass snippage]

          Here is a possible work-around:
          >>class Special(object) :
          .... def __call__(self):
          .... try:
          .... return self.__dict__['__call__']()
          .... except KeyError:
          .... return 'foo'
          ....
          >>s = Special()
          >>s()
          'foo'
          >>s.__call__ = lambda: 'bar'
          >>s()
          'bar'



          --
          Steven

          Comment

          • Bruno Desthuilliers

            #6
            Re: Impossible to change methods with special names of instancesof new-style classes?

            samwyse a écrit :
            On Jul 8, 4:56 pm, Joseph Barillari <pyt...@barilla ri.orgwrote:
            >
            >My question is: did something about the way the special method names are
            >implemented change for new-style classes?
            >
            Just off the top of my head, I'd guess that it's due to classes
            already having a default __call__ method,
            >>object.__dict __.keys()
            ['__setattr__', '__reduce_ex__' , '__new__', '__reduce__', '__str__',
            '__getattribute __', '__class__', '__delattr__', '__repr__', '__hash__',
            '__doc__', '__init__']

            No __call__ method here.
            used when you instatiate.
            The __call__ method used to instanciate a class is actually the
            metaclass __call__ method.
            >>class MyType(type):
            .... def __call__(self, *args, **kw):
            .... print "pikaboo"
            .... return type.__call__(s elf, *args, **kw)
            ....
            >>class MyClass(object) :
            .... __metaclass__ = MyType
            ....
            >>MyClass()
            pikaboo
            <__main__.MyCla ss object at 0x8334eec>
            >>>

            Comment

            Working...