Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #38112 > unrolled thread
| Started by | Saul Spatz <saul.spatz@gmail.com> |
|---|---|
| First post | 2013-02-03 17:08 -0800 |
| Last post | 2013-02-04 09:29 -0800 |
| Articles | 11 — 5 participants |
Back to article view | Back to comp.lang.python
__getattr__ Confusion Saul Spatz <saul.spatz@gmail.com> - 2013-02-03 17:08 -0800
Re: __getattr__ Confusion Chris Angelico <rosuav@gmail.com> - 2013-02-04 12:28 +1100
Re: __getattr__ Confusion Terry Reedy <tjreedy@udel.edu> - 2013-02-03 21:47 -0500
Re: __getattr__ Confusion Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-02-04 04:35 +0000
Re: __getattr__ Confusion Saul Spatz <saul.spatz@gmail.com> - 2013-02-04 05:44 -0800
Re: __getattr__ Confusion Peter Otten <__peter__@web.de> - 2013-02-04 15:15 +0100
Re: __getattr__ Confusion Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-02-05 01:46 +1100
Re: __getattr__ Confusion Saul Spatz <saul.spatz@gmail.com> - 2013-02-04 09:29 -0800
Re: __getattr__ Confusion Peter Otten <__peter__@web.de> - 2013-02-04 19:23 +0100
Re: __getattr__ Confusion Chris Angelico <rosuav@gmail.com> - 2013-02-05 12:00 +1100
Re: __getattr__ Confusion Saul Spatz <saul.spatz@gmail.com> - 2013-02-04 09:29 -0800
| From | Saul Spatz <saul.spatz@gmail.com> |
|---|---|
| Date | 2013-02-03 17:08 -0800 |
| Subject | __getattr__ Confusion |
| Message-ID | <095a0432-f8a4-40b4-96ed-1588896fba66@googlegroups.com> |
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')
app.pack()
root.mainloop()
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.
[toc] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-02-04 12:28 +1100 |
| Message-ID | <mailman.1319.1359941331.2939.python-list@python.org> |
| In reply to | #38112 |
On Mon, Feb 4, 2013 at 12:08 PM, Saul Spatz <saul.spatz@gmail.com> wrote: > class ScrolledCanvas(Frame): > def __init__(self, master, width, height, bg, cursor): > canv = self.canvas = Canvas(self, bg=bg, relief=SUNKEN) > > def __getattr__(self, name): > return getattr(self.canvas, name) Trying to get my head around what you're doing here. Why are you inheriting Frame, but passing all attribute queries through to the Canvas? Why not inherit Canvas? It looks to me like you're going to have some kind of bootstrap problem no matter how you do it. You're creating a cyclic reference (you pass self to Canvas), so one way or another, you need to start the loop. Dunder methods (like __getattr__) are looked up in the class, not the instance, so you can't simply set it in the way you describe. I think your best bet is going to be the "set up a stub, then fill in the details" method, which is more or less what you're doing (a stubby __nonzero__). ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2013-02-03 21:47 -0500 |
| Message-ID | <mailman.1320.1359946086.2939.python-list@python.org> |
| In reply to | #38112 |
On 2/3/2013 8:08 PM, Saul Spatz wrote: > 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') > app.pack() root.mainloop() > > 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? Because you made __nonzero__ an instance function attribute instead of an instance method class attribute as would be the case if you wrote def __nonzero__(self): return True outside of __init__. > > 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) I presume __getattr__ is only looked up on the class and never on the instance, not even as a backup. > 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.) If you are just starting out, consider 3.3 unless you really have to use 2.7. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-02-04 04:35 +0000 |
| Message-ID | <510f3a91$0$29866$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #38112 |
On Sun, 03 Feb 2013 17:08:47 -0800, Saul Spatz wrote:
> I don't understand what's going on at all. Can't I dynamically define
> __getattr__? How should I go about it?
Special "dunder" methods (DoubleUNDERscore) are looked up only on the
class, not on instances. That means that if you try to dynamically
provide a dunder method by assigning it to an instance, say with:
self.__nonzero__ = lambda self: True
it will *not* be automatically used by Python. The only way to
dynamically add dunder methods is to add them to the class, but of course
that means that all instances see the same method.
In your case, you try doing this inside the __init__:
self.__getattr__ = lambda x, name: getattr(self.canvas, name)
Why not just define a __getattr__ method the normal way? In your class,
define a method:
def __getattr__(self, name):
return getattr(self.canvas, name)
This technique is called automatic delegation.
Even if this does not quite do what you are trying to do, you will
eliminate one major stumbling block and be that much closer to a working
solution.
> 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.
It is pointless to tell us that Python "crashed" if you don't show us
*exactly* what you did, by copying and pasting the *actual* code,
complete with the full traceback. Otherwise we are just guessing what you
did and what error you saw.
I'm pretty confident that Python didn't "crash", in the commonly accepted
meaning of the word meaning a core dump or equivalent. I'm guessing you
meant that Python raised a perfectly normal exception, like
Traceback (most recent call last):
...
NameError: name 'self' is not defined
If you pay attention to the exception messages that Python prints for
you, you will not only find it easier to debug your code, but you can
also ask more sensible questions using accepted terminology.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | Saul Spatz <saul.spatz@gmail.com> |
|---|---|
| Date | 2013-02-04 05:44 -0800 |
| Message-ID | <863c89b0-2125-4525-8ad8-1111b0a6beae@googlegroups.com> |
| In reply to | #38115 |
On Sunday, February 3, 2013 10:35:30 PM UTC-6, Steven D'Aprano wrote: > On Sun, 03 Feb 2013 17:08:47 -0800, Saul Spatz wrote: > > > > > I don't understand what's going on at all. Can't I dynamically define > > > __getattr__? How should I go about it? > > > > Special "dunder" methods (DoubleUNDERscore) are looked up only on the > > class, not on instances. That means that if you try to dynamically > > provide a dunder method by assigning it to an instance, say with: > > > > self.__nonzero__ = lambda self: True > > > > it will *not* be automatically used by Python. The only way to > > dynamically add dunder methods is to add them to the class, but of course > > that means that all instances see the same method. > > > > > > In your case, you try doing this inside the __init__: > > > > self.__getattr__ = lambda x, name: getattr(self.canvas, name) > > > > > > Why not just define a __getattr__ method the normal way? In your class, > > define a method: > > > > def __getattr__(self, name): > > return getattr(self.canvas, name) > > > > This technique is called automatic delegation. > > > > Even if this does not quite do what you are trying to do, you will > > eliminate one major stumbling block and be that much closer to a working > > solution. > > > > > > > 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. > > > > It is pointless to tell us that Python "crashed" if you don't show us > > *exactly* what you did, by copying and pasting the *actual* code, > > complete with the full traceback. Otherwise we are just guessing what you > > did and what error you saw. > > > > I'm pretty confident that Python didn't "crash", in the commonly accepted > > meaning of the word meaning a core dump or equivalent. I'm guessing you > > meant that Python raised a perfectly normal exception, like > > > > > > Traceback (most recent call last): > > ... > > NameError: name 'self' is not defined > > > > > > If you pay attention to the exception messages that Python prints for > > you, you will not only find it easier to debug your code, but you can > > also ask more sensible questions using accepted terminology. > > > > > > > > -- > > Steven Thanks. The class versus instance lookup explains it. I didn't mean that python crashed, but that my app did. Now I have another question. If dunder methods are looked up only in the class, not the instance, why did defining __nonzero__ the way I did work? Shouldn't I have had to define it with a def? Is __nonzero__ a special case?
[toc] | [prev] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2013-02-04 15:15 +0100 |
| Message-ID | <mailman.1322.1359987291.2939.python-list@python.org> |
| In reply to | #38121 |
Saul Spatz wrote: > Now I have another question. If dunder methods are looked up only in the > class, not the instance, why did defining __nonzero__ the way I did work? > Shouldn't I have had to define it with a def? Is __nonzero__ a special > case? Unfortunately the situation is a bit more complex. Classic classes (like Tkinter.Frame) behave differently from newstyle classes (subclasses of object): >>> def nz(): ... print "nonzero" ... return 0 ... >>> class Classic: pass ... >>> c = Classic() >>> c.__nonzero__ = nz >>> not c nonzero True >>> class New(object): pass ... >>> n = New() >>> n.__nonzero__ = nz >>> not n False So Steven is wrong here. > Shouldn't I have had to define it with a def? If you mean as opposed to a lambda, there is no difference between f = lambda ... and def f(...): ... other than that the last one gives you a nice name in a traceback.
[toc] | [prev] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-02-05 01:46 +1100 |
| Message-ID | <510fc9ba$0$29965$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #38123 |
Peter Otten wrote: > Saul Spatz wrote: > >> Now I have another question. If dunder methods are looked up only in the >> class, not the instance, why did defining __nonzero__ the way I did work? >> Shouldn't I have had to define it with a def? Is __nonzero__ a special >> case? > > Unfortunately the situation is a bit more complex. Classic classes (like > Tkinter.Frame) behave differently from newstyle classes (subclasses of > object): [...] > So Steven is wrong here. Peter is correct -- I've been using Python 3 too much, and completely forgot about old-style classic classes, which only exist in Python 2. Sorry for any confusion. Nevertheless, everything I said about dunder methods applies to "new style" classes in Python 2, and all classes in Python 3. -- Steven
[toc] | [prev] | [next] | [standalone]
| From | Saul Spatz <saul.spatz@gmail.com> |
|---|---|
| Date | 2013-02-04 09:29 -0800 |
| Message-ID | <fbbc805f-b92a-42b6-adb6-7d0e1fdfab77@googlegroups.com> |
| In reply to | #38123 |
Thanks, Peter. I realize this is getting sort of academic now, as I know how to do exactly what I want, but I'm still confused. Is __getattr__ a special case then, even for classic classes?
class Adder(): # python 2.7, classic class
def __init__(self, x):
self.x = x
self.__add__= lambda other: Adder(self.x+other.x)
self.__getattr__ = lambda name: self.test(name)
def __str__(self):
return str(self.x)
def test(self, name):
print("Hello from test")
raise AttributeError
x = Adder(3)
y = Adder(4)
print(x+y)
x.junk()
7
Traceback (most recent call last):
File "C:\Users\Saul\Documents\PythonProjects\test.py", line 18
AttributeError: Adder instance has no attribute 'junk'
Why does this work for __add__ and not for __getattr__?
Of course, they both work if I write instead
def __add__self, other):
return Adder(self.x+other.x)
def __getattr__(self, name):
print(name)
raise AttributeError
like a sensible person.
Saul
On Monday, February 4, 2013 8:15:47 AM UTC-6, Peter Otten wrote:
> Saul Spatz wrote:
>
>
>
> > Now I have another question. If dunder methods are looked up only in the
>
> > class, not the instance, why did defining __nonzero__ the way I did work?
>
> > Shouldn't I have had to define it with a def? Is __nonzero__ a special
>
> > case?
>
>
>
> Unfortunately the situation is a bit more complex. Classic classes (like
>
> Tkinter.Frame) behave differently from newstyle classes (subclasses of
>
> object):
>
>
>
> >>> def nz():
>
> ... print "nonzero"
>
> ... return 0
>
> ...
>
> >>> class Classic: pass
>
> ...
>
> >>> c = Classic()
>
> >>> c.__nonzero__ = nz
>
> >>> not c
>
> nonzero
>
> True
>
> >>> class New(object): pass
>
> ...
>
> >>> n = New()
>
> >>> n.__nonzero__ = nz
>
> >>> not n
>
> False
>
>
>
> So Steven is wrong here.
>
>
>
> > Shouldn't I have had to define it with a def?
>
>
>
> If you mean as opposed to a lambda, there is no difference between
>
>
>
> f = lambda ...
>
>
>
> and
>
>
>
> def f(...): ...
>
>
>
> other than that the last one gives you a nice name in a traceback.
[toc] | [prev] | [next] | [standalone]
| From | Peter Otten <__peter__@web.de> |
|---|---|
| Date | 2013-02-04 19:23 +0100 |
| Message-ID | <mailman.1330.1360002178.2939.python-list@python.org> |
| In reply to | #38132 |
Saul Spatz wrote:
> Thanks, Peter. I realize this is getting sort of academic now, as I know
> how to do exactly what I want, but I'm still confused. Is __getattr__ a
> special case then, even for classic classes?
Well, it never occured to me to try a per-instance __getattr__(), but you
are about to answer your own question:
> class Adder(): # python 2.7, classic class
> def __init__(self, x):
> self.x = x
> self.__add__= lambda other: Adder(self.x+other.x)
> self.__getattr__ = lambda name: self.test(name)
>
> def __str__(self):
> return str(self.x)
>
> def test(self, name):
> print("Hello from test")
> raise AttributeError
>
> x = Adder(3)
> y = Adder(4)
> print(x+y)
> x.junk()
>
> 7
> Traceback (most recent call last):
> File "C:\Users\Saul\Documents\PythonProjects\test.py", line 18
> AttributeError: Adder instance has no attribute 'junk'
>
> Why does this work for __add__ and not for __getattr__?
I don't know, I wasn't around when these decisions were made. It could be
the initial performance tweak that would lead to a generalisation with
newstyle classes. Or it is some kind of bootstrapping issue...
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2013-02-05 12:00 +1100 |
| Message-ID | <mailman.1347.1360026058.2939.python-list@python.org> |
| In reply to | #38132 |
On Tue, Feb 5, 2013 at 4:29 AM, Saul Spatz <saul.spatz@gmail.com> wrote: > class Adder(): # python 2.7, classic class > > Why does this work for __add__ and not for __getattr__? Is this a case of "why bother trying to understand it, just use new-style classes"? They do make more sense in many ways. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Saul Spatz <saul.spatz@gmail.com> |
|---|---|
| Date | 2013-02-04 09:29 -0800 |
| Message-ID | <mailman.1328.1359998954.2939.python-list@python.org> |
| In reply to | #38123 |
Thanks, Peter. I realize this is getting sort of academic now, as I know how to do exactly what I want, but I'm still confused. Is __getattr__ a special case then, even for classic classes?
class Adder(): # python 2.7, classic class
def __init__(self, x):
self.x = x
self.__add__= lambda other: Adder(self.x+other.x)
self.__getattr__ = lambda name: self.test(name)
def __str__(self):
return str(self.x)
def test(self, name):
print("Hello from test")
raise AttributeError
x = Adder(3)
y = Adder(4)
print(x+y)
x.junk()
7
Traceback (most recent call last):
File "C:\Users\Saul\Documents\PythonProjects\test.py", line 18
AttributeError: Adder instance has no attribute 'junk'
Why does this work for __add__ and not for __getattr__?
Of course, they both work if I write instead
def __add__self, other):
return Adder(self.x+other.x)
def __getattr__(self, name):
print(name)
raise AttributeError
like a sensible person.
Saul
On Monday, February 4, 2013 8:15:47 AM UTC-6, Peter Otten wrote:
> Saul Spatz wrote:
>
>
>
> > Now I have another question. If dunder methods are looked up only in the
>
> > class, not the instance, why did defining __nonzero__ the way I did work?
>
> > Shouldn't I have had to define it with a def? Is __nonzero__ a special
>
> > case?
>
>
>
> Unfortunately the situation is a bit more complex. Classic classes (like
>
> Tkinter.Frame) behave differently from newstyle classes (subclasses of
>
> object):
>
>
>
> >>> def nz():
>
> ... print "nonzero"
>
> ... return 0
>
> ...
>
> >>> class Classic: pass
>
> ...
>
> >>> c = Classic()
>
> >>> c.__nonzero__ = nz
>
> >>> not c
>
> nonzero
>
> True
>
> >>> class New(object): pass
>
> ...
>
> >>> n = New()
>
> >>> n.__nonzero__ = nz
>
> >>> not n
>
> False
>
>
>
> So Steven is wrong here.
>
>
>
> > Shouldn't I have had to define it with a def?
>
>
>
> If you mean as opposed to a lambda, there is no difference between
>
>
>
> f = lambda ...
>
>
>
> and
>
>
>
> def f(...): ...
>
>
>
> other than that the last one gives you a nice name in a traceback.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web