views:

1487

answers:

4

I'm interested in hearing some discussion about class attributes in Python. For example, what is a good use case for class attributes? For the most part, I can not come up with a case where a class attribute is preferable to using a module level attribute. If this is true, then why have them around?

The problem I have with them, is that it is almost too easy to clobber a class attribute value by mistake, and then your "global" value has turned into a local instance attribute.

Feel free to comment on how you would handle the following situations:

  1. Constant values used by a class and/or sub-classes. This may include "magic number" dictionary keys or list indexes that will never change, but possible need one-time initialization.
  2. Default class attribute, that in rare occasions updated for a special instance of the class.
  3. Global data structure used to represent an internal state of a class shared between all instances.
  4. A class that initializes a number of default attributes, not influenced by constructor arguments.

Some Related Posts:
Difference Between Class and Instance Attributes

+2  A: 

Class attributes are often used to allow overriding defaults in subclasses. For example, BaseHTTPRequestHandler has class constants sys_version and server_version, the latter defaulting to "BaseHTTP/" + __version__. SimpleHTTPRequestHandler overrides server_version to "SimpleHTTP/" + __version__.

Martin v. Löwis
However, the access method of the class attribute would be a factor if this worked or not. Another reason why Class attributes are dangerous.
Casey
What "access method"? cls.version, self.version, getattr, will all give the right answer. *Define in class to allow overrides* works just as well for data as for code -- so, Casey, why do you think it's dangerous for data but fine for code?!
Alex Martelli
By using Class.value instead of self.value in a method to access the attribute will prevent an override from occuring. Sometimes this is the desired case, other times not.
Casey
If you expect your users to write Class.value, you could just as well put them on module level. You were explicitly asking for cases where it is useful to have the constant on the class. In the example I give, it's already the base class which does self.value, deliberately expecting that subclasses (or even instances) may override the value.
Martin v. Löwis
+1  A: 

Encapsulation is a good principle: when an attribute is inside the class it pertains to instead of being in the global scope, this gives additional information to people reading the code.

In your situations 1-4, I would thus avoid globals as much as I can, and prefer using class attributes, which allow one to benefit from encapsulation.

EOL
+3  A: 

#4: I never use class attributes to initialize default instance attributes (the ones you normally put in __init__). For example:

class Obj(object):
    def __init__(self):
        self.users = 0

and never:

class Obj(object):
    self.users = 0

Why? Because it's inconsistent: it doesn't do what you want when you assign anything but an invariant object:

class Obj(object):
    self.users = []

causes the users list to be shared across all objects, which in this case isn't wanted. It's confusing to split these into class attributes and assignments in __init__ depending on their type, so I always put them all in __init__, which I find clearer anyway.


As for the rest, I generally put class-specific values inside the class. This isn't so much because globals are "evil"--they're not so big a deal as in some languages, because they're still scoped to the module, unless the module itself is too big--but if external code wants to access them, it's handy to have all of the relevant values in one place. For example, in module.py:

class Obj(object):
    class Exception(Exception): pass
    ...

and then:

from module import Obj

try:
    o = Obj()
    o.go()
except o.Exception:
    print "error"

Aside from allowing subclasses to change the value (which isn't always wanted anyway), it means I don't have to laboriously import exception names and a bunch of other stuff needed to use Obj. "from module import Obj, ObjException, ..." gets tiresome quickly.

Glenn Maynard
A: 

what is a good use case for class attributes

Case 0. Class methods are just class attributes. This is not just a technical similarity - you can access and modify class methods at runtime by assigning callables to them.

Case 1. A module can easily define several classes. It's reasonable to encapsulate everything about class A into A... and everything about class B into B.... For example,

# module xxx
class X:
    MAX_THREADS = 100
    ...

# main program
from xxx import X

if nthreads < X.MAX_THREADS: ...

Case 2. This class has lots of default attributes which can be modified in an instance. Here the ability to leave attribute to be a 'global default' is a feature, not bug.

class NiceDiff:
    """Formats time difference given in seconds into a form '15 minutes ago'."""

    magic = .249
    pattern = 'in {0}', 'right now', '{0} ago'

    divisions = 1

    # there are more default attributes

One creates instance of NiceDiff to use the existing or slightly modified formatting, but a localizer to a different language subclasses the class to implement some functions in a fundamentally different way and redefine constants:

class Разница(NiceDiff): # NiceDiff localized to Russian
    '''Из разницы во времени, типа -300, делает конкретно '5 минут назад'.'''

    pattern = 'через {0}', 'прям щас', '{0} назад'

Your cases:

  • constants -- yes, I put them to class. It's strange to say self.CONSTANT = ..., so I don't see a big risk for clobbering them.
  • Default attribute -- mixed, as above may go to class, but may also go to __init__ depending on the semantics.
  • Global data structure --- goes to class if used only by the class, but may also go to module, in either case must be very well-documented.
ilya n.