Scope and classes

D

David

Hi all,

I'm trying to understand how scopes work within a class definition.
I'll quickly illustrate with an example. Say I had the following class
definition:

class Abc:
message = 'Hello World'

def print_message(self):
print message
NameError: global name 'message' not defined

My question is, why? message is not defined in print_message, but it
is defined in the enclosing scope (the class)?

Thanks!
 
C

Chris Rebert

Hi all,

I'm trying to understand how scopes work within a class definition.
I'll quickly illustrate with an example. Say I had the following class
definition:

class Abc:
   message = 'Hello World'

   def print_message(self):
       print message

NameError: global name 'message' not defined

My question is, why? message is not defined in print_message, but it
is defined in the enclosing scope (the class)?

A class' scope is never consulted when resolving variable names in its methods.

The scopes consulted are roughly (and in this order):
1. Local variable scope (i.e. of the current function/method)
2. Enclosing functions (if you have functions nested inside other functions)
3. Globals (i.e. module-level variables)
3. Builtins (i.e. the built-in functions and methods, such as len())

To access class-level variables from within instance methods of the
class, you have 2 options:
A. Use the class name, i.e. Abc.message
B. Reference the class indirectly, i.e. self.__class__.message

Cheers,
Chris
 
D

David

A class' scope is never consulted when resolving variable names in its methods.

The scopes consulted are roughly (and in this order):
1. Local variable scope (i.e. of the current function/method)
2. Enclosing functions (if you have functions nested inside other functions)
3. Globals (i.e. module-level variables)
3. Builtins (i.e. the built-in functions and methods, such as len())

To access class-level variables from within instance methods of the
class, you have 2 options:
A. Use the class name, i.e. Abc.message
B. Reference the class indirectly, i.e. self.__class__.message

Cheers,
Chris
--http://blog.rebertia.com

Ah, thanks!
 
S

Steven D'Aprano

Hi all,

I'm trying to understand how scopes work within a class definition. I'll
quickly illustrate with an example. Say I had the following class
definition:

class Abc:
message = 'Hello World'

def print_message(self):
print message

NameError: global name 'message' not defined

My question is, why? message is not defined in print_message, but it is
defined in the enclosing scope (the class)?

It's a deliberate design decision. I don't recall the reasons for it, but
the class namespace is deliberately excluded from the method scope.

The correct way of writing the above is:

class Abc:
message = 'Hello World'
def print_message(self):
print self.message


self.message will first search the instance namespace for an attribute
`message`, then search the class, then any superclasses. If you
specifically want the class attribute, even if the instance masks it with
an instance attribute of the same name, you do this instead:

print self.__class__.message
 
J

Jan Kaliszewski

19-08-2009 o 00:47:09 David said:
Hi all,

I'm trying to understand how scopes work within a class definition.
I'll quickly illustrate with an example. Say I had the following class
definition:

class Abc:
message = 'Hello World'

def print_message(self):
print message

NameError: global name 'message' not defined

My question is, why? message is not defined in print_message, but it
is defined in the enclosing scope (the class)?

Note that when *running* the code that is *inside* print_message() method
called within the global scope, the outer (enclosing) scope is indeed the
global scope (the scope in which e.g. 'instance' name exists; note that
e.g. 'print instance' in that method would be ok).

As Chris wrote, class' scope (contrary to outer function scope) is not
considered as the 'enclosing' scope.

The only ways to reach Abc's attribute 'message' from that method are:
* 'Abc.message'
* 'self.__class__.message'
* 'self.message' (unless there is an instance attribute 'message' which
overrides the class attribute).

Cheers,
*j
 
J

Jan Kaliszewski

19-08-2009 o 02:10:58 Jan Kaliszewski said:
The only ways to reach Abc's attribute 'message' from that method are:
* 'Abc.message'
* 'self.__class__.message'
* 'self.message' (unless there is an instance attribute 'message' which
overrides the class attribute).

And of course getattr(Abc), getattr(self.__class__) etc. :)
 
D

David

And of course getattr(Abc), getattr(self.__class__) etc. :)

Out of 'Abc.message' and 'self.message', which is the favoured
convention? It would be very easy to accidentally override
'self.messages' with an instance attribute!
 
B

Bruno Desthuilliers

Chris Rebert a écrit :
(snip)
To access class-level variables from within instance methods of the
class, you have 2 options:
A. Use the class name, i.e. Abc.message
B. Reference the class indirectly, i.e. self.__class__.message

Or even simpler - *if* there's no synonym instance attribute:

=> self.message

Attribute lookup will try to resolve the attribute name on the class
(and it's parent classes) if it's not found on the instance.
 
B

Bruno Desthuilliers

David a écrit :
(snip)
Out of 'Abc.message' and 'self.message', which is the favoured
convention? It would be very easy to accidentally override
'self.messages' with an instance attribute!

Only use 'Abc.message' if you want to make sure you get the Abc class
'message' attribute - that is, if you want to skip possible overrides in
subclasses. As far as I'm concerned, I'd tend to consider this bad style
unless it's a very very specific implementation point of an abstract
class (in which case it would probably be named '__message' to prevent
accidental override) - but YMMV of course.

Use 'self.__class__.message' (or 'type(self).message') if you want to
make sure you get the class 'message' attribute - that is, if you want
to honor possible overrides in subclasses, but not per-instance
override. But then - at least in your example code - I'd use a classmethod:

class Abc:
message = 'Hello World'

@classmethod
def print_message(cls):
print cls.message


Now the most common idiom - that is, outside classmethods - is to just
use 'self.message'. Someone (even you) might have a very valid reason to
override the 'message' attribute on a per-instance basis. FWIW, if you
start to worry about possible accidental overrides here, then Python
might not be the right language for you - nothing prevents "accidental"
overrides of method and even the class (the '__class__' attribute)
itself - yes, they are all attributes, and you can dynamically override
them !-)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top