__getattr__ Confusion

Saul Spatz saul.spatz at gmail.com
Mon Feb 4 02:08:47 CET 2013

To the good people on comp.lang.python:

I have the following Tkinter class (python 2.7.3):

from Tkinter import *

class ScrolledCanvas(Frame):
  def __init__(self, master, width, height, bg, cursor):
    Frame.__init__(self, master)
    self.__nonzero__ = lambda: True
    canv = self.canvas = Canvas(self, bg=bg, relief=SUNKEN)
    # self.__getattr__ = lambda x, name: getattr(self.canvas, name)
    canv.config(width=width, height=height)           # display area size
    canv.config(scrollregion=(0, 0, width, height))   # canvas size corners
    canv.config(highlightthickness=0)                 # no pixels to border

    ybar = Scrollbar(self)
    ybar.config(command=canv.yview)                   # xlink sbar and canv
    canv.config(yscrollcommand=ybar.set)              # move one moves other

    xbar = Scrollbar(self)
    xbar.config(command=canv.xview)                   # xlink sbar and canv
    canv.config(xscrollcommand=xbar.set)              # move one moves other

    canv.grid(row = 0, column = 0, sticky = 'news')
    ybar.grid(row = 0, column = 1, sticky = 'ns')
    xbar.grid(row = 1, column = 0, sticky = 'ew')
    self.rowconfigure(0, weight = 1)
    self.columnconfigure(0, weight = 1)

    self.create_text(20, 20, text = 'Did it!', fill = 'red')

  def __getattr__(self, name):
    return getattr(self.canvas, name)

root = Tk()
app = ScrolledCanvas(root, 400, 300, 'white', 'hand2')

I just added the __getattr__ method, and the program crashed in the Canvas constructor.  There is apparently a call to self.__nonzero__ somewhere in Tkinter.py, and since the constructor hasn't exited yet, sel.fcanvas isn't defined yet, so __getattr__ recurses indefinitely.

I fixed this as you see, by defining self.__nonzero__ before the call to the constructor.  Now, I have  two questions: 

1. I originally defined self.__nonzero__ = lambda x: True, on the assumption that when self.__nonzero__ was called, the interpreter would pass self as an argument.  Wrong.  No arguments were passed.  Why is this?

2. I find this solution rather unsatisfactory, since there's a rather obscure line of code here.  I tried eliminating the def of __gertattr__ and the definition of self.__nonzero__ and adding this line after the constructor:

self.__getattr__=  lambda name: getattr(self.canvas, name)   

This get through the constructor all right, but crashes with the message that a ScrolledCanvas object has no create_text attribute.  (I've tried passing two arguments to the lambda, but it makes no difference.)  

I don't understand what's going on at all.  Can't I dynamically define __getattr__?  How should I go about it?  By the way, another thing that didn't work was calling the method delegate instead of __getattr__.  Then after the constructor call, I wrote
self.__getattr__ = self.delegate.  This crashed as before on self.create_text.

I just tried a little experiment with __add__ and had no success, so I guess my problem is with overloaded operators in general.

I'd really appreciate an explanation, or a pointer to relevant documentation.

More information about the Python-list mailing list