Path: csiph.com!x330-a1.tempe.blueboxinc.net!usenet.pasdenom.info!weretis.net!feeder4.news.weretis.net!ecngs!feeder2.ecngs.de!newsfeed.freenet.ag!news2.euro.net!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.001 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'python,': 0.01; 'interpreter': 0.05; 'attribute': 0.07; 'python': 0.08; 'dynamically': 0.09; 'foo': 0.09; 'foo,': 0.09; 'none)': 0.09; 'statement.': 0.09; 'underlying': 0.09; 'def': 0.13; 'class,': 0.15; '(lambda': 0.16; '**kwargs):': 0.16; '*args,': 0.16; 'class:': 0.16; 'foo(object):': 0.16; 'magic.': 0.16; 'name):': 0.16; 'outputs': 0.16; '\xa0def': 0.16; '\xa0here': 0.16; 'cc:addr :python-list': 0.16; 'wrote:': 0.18; '>>>': 0.18; 'instance': 0.18; 'cc:no real name:2**0': 0.20; '(most': 0.21; 'mechanism': 0.21; 'dec': 0.22; 'header:In-Reply-To:1': 0.22; 'pieces': 0.23; 'wrong?': 0.23; 'steve': 0.24; 'cc:2**0': 0.24; 'fine': 0.24; 'traceback': 0.24; "i'm": 0.26; 'function': 0.27; 'variable': 0.28; 'subject:" ': 0.28; 'message-id:@mail.gmail.com': 0.28; 'pass': 0.29; 'explicit': 0.29; 'cc:addr:python.org': 0.29; 'error': 0.29; 'pm,': 0.29; 'class': 0.29; 'definition': 0.30; 'object.': 0.30; 'python3': 0.30; 'does': 0.32; 'tue,': 0.32; 'object': 0.33; 'eric': 0.34; '17,': 0.34; 'approach.': 0.34; 'last):': 0.34; 'url:python': 0.36; 'however,': 0.36; 'received:209.85.161': 0.36; 'subject:with': 0.36; 'skip:" 10': 0.37; 'but': 0.37; 'received:google.com': 0.37; 'skip:_ 10': 0.37; 'doing': 0.38; 'using': 0.38; 'received:209.85': 0.38; 'put': 0.38; 'skip:\xa0 10': 0.39; 'url:docs': 0.39; 'url:org': 0.39; 'being': 0.39; 'subject: (': 0.40; 'received:209': 0.40; '8bit%:8': 0.40; 'more': 0.61; '2011': 0.61; 'your': 0.61; 'full': 0.62; 'special': 0.68; 'to:addr:yahoo.com': 0.83; 'foo:"': 0.84; 'howell': 0.84; 'self:': 0.84; 'tricky': 0.84; 'url:datamodel': 0.84; 'url:reference': 0.84; 'snow': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:content-transfer-encoding; bh=WC9AItxQFXZIdz7o+/LgsRZemmhFxnWBHAe69NkZp40=; b=XQO+I4RTxpL1v9qSS8RFxz+MTO4AlhesYa0iuYG7R7Je3po6wSHg5WBCl5TJfEqciE KdchZAt97D8zyfvkBluRrmbJFTt0n3duCyWILNKFpugPTjFj/f95KliGLakhJlnmqH0G 2wxONhi+/VNQzpfLqjsybeOUq5B3hPtglMMWQ= MIME-Version: 1.0 In-Reply-To: References: Date: Wed, 14 Dec 2011 09:56:10 -0700 Subject: Re: AttributeError in "with" statement (3.2.2) From: Eric Snow To: Steve Howell Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Cc: python-list@python.org X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 99 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1323881774 news.xs4all.nl 6892 [2001:888:2000:d::a6]:37796 X-Complaints-To: abuse@xs4all.nl Xref: x330-a1.tempe.blueboxinc.net comp.lang.python:17224 On Tue, Dec 13, 2011 at 11:05 PM, Eric Snow w= rote: > On Tue, Dec 13, 2011 at 10:42 PM, Steve Howell wrot= e: >> I'm using Python 3.2.2, and the following program gives me an error >> that I don't understand: >> >> class Foo: >> =A0pass >> >> foo =3D Foo() >> foo.name =3D "Steve" >> >> def add_goodbye_function(obj): >> =A0def goodbye(): >> =A0 =A0print("goodbye " + obj.name) >> =A0obj.goodbye =3D goodbye >> >> add_goodbye_function(foo) >> foo.goodbye() # outputs goodbye Steve >> foo.__exit__ =3D foo.goodbye >> foo.__exit__() # outputs goodbye Steve >> >> with foo: # fails with AttributeError: =A0__exit__ >> =A0print("doing stuff") >> >> I am dynamically adding an attribute __exit__ to the variable foo, >> which works fine when I call it directly, but it fails when I try to >> use foo as the expression in the with statement. =A0Here is the full >> output: >> >>> python3 with.coffee >> goodbye Steve >> goodbye Steve >> Traceback (most recent call last): >> =A0File "with.coffee", line 17, in >> =A0 =A0with foo: # fails with AttributeError: >> AttributeError: __exit__ >> >> What am I doing wrong? > > That is a tricky one. > > As with many of the special methods (start and end with __) in Python, > the underlying mechanism in the interpreter is directly pulling the > function from the class object. =A0It does not look to the instance > object for the function at any time. =A0See > http://docs.python.org/reference/datamodel.html#special-method-lookup-for= -new-style-classes. > > -eric Incidently, if you add the function directly to the class, everything works= out: class Foo(object): # or "class Foo:" under Python 3 pass foo =3D Foo() foo.name =3D "Steve" def add_goodbye_function(cls): def goodbye(self, *args, **kwargs): print("goodbye " + self.name) cls.goodbye =3D goodbye add_goodbye_function(type(foo)) foo.goodbye() # outputs goodbye Steve Foo.__exit__ =3D foo.goodbye foo.__exit__() # outputs goodbye Steve Foo.__enter__ =3D (lambda self: None) with foo: print("doing stuff") However, perhaps a better approach would be to put the different pieces directly into the class: class Foo: # python 3 def __init__(self, name): self.name =3D name def goodbye(self): print("goodbye " + self.name) def __enter__(self): pass def __exit__(self, *args, **kwargs): self.goodbye() foo =3D Foo("Steve") foo.goodbye() # outputs goodbye Steve foo.__exit__() # outputs goodbye Steve with foo: print("doing stuff") If you want to be more dynamic about it you can do it, but it involves black magic. Chances are really good that being explicit through your class definition is the right approach. -eric