4

Which of the following two approaches is considered best practice? The both achieve the same result.

class Foo():
    LABELS = ('One','Two','Three')

class Bar():
    def __init__(self):
        self.__labels = ('One','Two','Three')

    @property
    def labels(self):
        return self.__labels
Declan_K
  • 6,726
  • 2
  • 19
  • 30

6 Answers6

5

If you don't need custom getting or setting behavior, there's no point in making something a property. The first version is better.

Also, these are not quite identical in their behavior. In Foo, there's a class-level attribute for labels. In Bar, there isn't. Referencing Foo.LABELS will work fine, referencing Bar.labels will throw an exception.

Peter DeGlopper
  • 36,326
  • 7
  • 90
  • 83
2

The PEP 8 Style Guide for Python Code offers a 3rd way, and the Zen of Python agrees.

They suggests adding a very simple module, that creates a namespace to define the constants.

Entire of contents of e.g. package/constants.py:

LABELS = ('One', 'Two', 'Three')

Example usage:

from package.constants import LABELS
print(LABELS)  # -> ('One', 'Two', 'Three')

Note that there isn't any explicit constant "protection" here. You can jump through hoops to try to get constant constants... Or you can accept that any protection you put in place can be side-stepped by someone who really wants to, then just re-quote whoever said that stuff about consenting adults and go with the very sensible notion that variables in ALL_CAPS are properly respected by enough developers that you just shouldn't worry about enforcing your notion of constant.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
anregen
  • 1,578
  • 1
  • 13
  • 13
1

The first variant communicates, that the labels are declared on all instances the class, ...

class Foo():
    LABELS = ('One','Two','Three')

whereas the second look like the labels are special, per instance. I'd go with the first, if the labels are constant - it's more coherent.

miku
  • 181,842
  • 47
  • 306
  • 310
  • 1
    Actually, they are not declared on instances. These are static variables. Trying to find `LABELS` on an instance fails internally but succeeds when looked up on the class. For an example: http://ideone.com/qfWlbe – Noctis Skytower Jun 21 '13 at 20:26
0

This depends on the scope of the variable use. I use class variables as a reference to constants, much like #define SOMETHING in C. Instance variables, on the other hand may have different values for different instances. Perhaps an example will explain this better.

class ChessBoardTile:
    BLACK = 'black'
    WHITE = 'white'
    def __init__(self,clr):
        self._color = clr

t = ChessBoardTile(ChessBoardTile.BLACK)
Nik
  • 5,515
  • 14
  • 49
  • 75
0

I made an example to try to convince you to take the second approach but I got confused...

>>> class Foo():
...     LABELS = ('One','Two','Three')
... 
>>> Foo.LABELS
('One', 'Two', 'Three')
>>> Foo.LABELS = (1,2,3)
>>> Foo.LABELS
(1, 2, 3)
>>> f = Foo()
>>> g = Foo()
>>> f.LABELS = ('a','b','c')
>>> g.LABELS
(1, 2, 3)
>>> Foo.LABELS
(1, 2, 3)
>>> f.LABELS
('a', 'b', 'c')

"What is going on?" I thought to myself. Then I realized that the behavior depends on object ids...

>>> id(Foo.LABELS)
4562309280
>>> id(g.LABELS)
4562309280
>>> id(f.LABELS)
4562068336
>>> ('a','b','c') is ('a','b','c')
False
>>> Foo.LABELS = (4,5,6)
>>> g.LABELS
(4, 5, 6)
>>> f.LABELS
('a', 'b', 'c')
>>> id(Foo.LABELS)
4562309200
>>> id(g.LABELS)
4562309200
>>> id(f.LABELS)
4562068336

So, back to my original answer: do not take the first approach unless you don't care if your variable gets re-assigned, because what you will get is not what you expect. The first approach makes the variable belong to the class, the second makes the variable belong to the instance - but if someone reassigns the variable in the first situation, you are going to get some very strange results.

Corollary - if you have your class methods only refer to the class' variable (i.e. Foo.LABELS) then you will obviously get what you expect, but if someone re-uses your code in a different manner then who knows what they'll get?

Corollary #2 - there is no way in python to enforce reference immutability. So you really should go with the second approach.

2rs2ts
  • 10,662
  • 10
  • 51
  • 95
  • That's actually a question of where the attribute is assigned rather than anything to do with properties. If you want independent instance-level values for the attributes, assign it in the `__init__` method rather than leaving it on the class. Which is a good idea for anything that might ever change, usually. But it's better to assign it the constant value - just set `LABELS = ('One', 'Two', 'Three')` in `__init__`. – Peter DeGlopper Jun 21 '13 at 19:51
0

Definitely the first one, because:

  • it is simpler and communicates your intent better

  • it may use less memory as it is class variable evaluated only once. Whereas the second example creates the same tuple over-and-over and it might not get cached (internalized).

  • and as others have pointed out, you could ask for Foo.LABELS which might be for adding structure to your constants.

Ecir Hana
  • 10,864
  • 13
  • 67
  • 117