1

Pep8 advises to always use cls as the first argument of a class method definition. Now suppose I want to use a class variable(in this case: cls.cartridge_state) that can also be used in an instance method (in this case: __init__). So for that I need to make the variable global(see code below). But instantiating FountainPen generates the following runtime error:

self.cartridge_state = cls.cartridge_state
NameError: global name 'cls' is not defined

But then again when I change global cartridge_state into global cls.cartridge_state I get a syntaxError when i try to import the module.

class FountainPen(object):
    cartridge_ink = "water-based"
    @classmethod
    def toggle_default_cartridge_state(cls):
        i = 0
        cartridge_states = ['non-empty','empty']
        global cartridge_state
        cls.cartridge_state = cartridge_states[i]
        i += 1

    def __init__(self):
        self.cartridge_state = cls.cartridge_state
        global number_of_refills
        self.number_of_refills = 0

    def write(self):
        print Pen.write(self)
        self.cartridge_state = "empty"
        return self.cartridge_state

    def refill(self):
        self.cartridge_state = "non-empty"
        self.number_of_refills += 1

How can I let the class variable cartridge_state be pep8 compliant and make this code work without errors?

Bentley4
  • 10,678
  • 25
  • 83
  • 134
  • 5
    This seems like a messy and over complicated design – Jakob Bowyer Jun 19 '12 at 13:19
  • `global cartridge_state` is a no-op in your code, I think. `cls.cartridge_state` is certainly a different identifier. – Martijn Pieters Jun 19 '12 at 13:19
  • Also, `toggle_default_cartridge_state` won't do what you think it does; it'll *always* set `cls.cartridge_state` to `'non-empty'`. – Martijn Pieters Jun 19 '12 at 13:21
  • You don't need or want globals to do what you are trying to do. Address the class by name to access class variables if you are not in a class method - `FountainPen.cartridge_state`. And as Martijn points out, `i` is not persisted between calls to `toggle_default_cartridge_state()`. – Gareth Latty Jun 19 '12 at 13:24
  • @Jakob Bowyer: it is messy, but the point is to experiment how class variables behave, how global can be used in a class environment, how class variables can be assigned to instance variables etc. – Bentley4 Jun 19 '12 at 13:41
  • @Martijn Pieters: Indeed, `toggle_default_cartridge_state` will just always set to `'non-empty'`. How would you implement this toggle functionality without resorting to flat files or databases? – Bentley4 Jun 19 '12 at 13:53

3 Answers3

5

A class attribute can be read via self:

class FountainPen(object):
    cartridge_ink = "water-based"
    default_cartridge_state = "empty"

    @classmethod
    def toggle_default_cartridge_state(cls):
        if cls.default_cartridge_state == "empty":
            cls.default_cartridge_state = "non-empty"  
        else:
            cls.default_cartridge_state = "empty"

    def __init__(self):
        self.cartridge_state = self.default_cartridge_state

    def write(self):
        print Pen.write(self)
        self.cartridge_state = "empty"
        return self.cartridge_state

    def refill(self):
        self.cartridge_state = "non-empty"
        self.number_of_refills += 1
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
1

What PEP 8 says is that cls should be the first argument. The same way when you define the first argument self in instance methods. I suppose this is for avoiding troubles when you call a class method like this self.yourclassmethod(). But I can't see why it is necessary having the @classmethod decorator.

EDIT:

I do it differently, instead of @classmethod I use @staticmethod but I realize we are not talking about the same thing. If someone believes I am wrong please tell me.

Example:

class Bar(object):
    @staticmethod
    def foo(myarg):
        return myarg * 2

Bar.foo(2)

EDIT 2:

I correct myself, @classmethod and @staticmethod represent different things (see here). A static method shouldn't be modifying variables from the class.

Community
  • 1
  • 1
Diego Navarro
  • 9,316
  • 3
  • 26
  • 33
  • I'll come back to that later, at some point of this exercice I was going to use `@staticmethod` instead of `@classmethod` to see what was the difference. – Bentley4 Jun 19 '12 at 13:47
  • 1
    Basically a `@staticmethod` is useful to append a function to your class, `self` is not passed in this case. It acts like a module function. – Diego Navarro Jun 19 '12 at 13:52
0

Forget about global; global has nothing to do with classes and objects.

When you access a property on an object, it will be looked up first on the object and then on the class. That means that if you want an object to have its own copy of a property and not reflect subsequent updates to that property on the class, all you need to do is copy it in __init__:

def __init__(self):
    self.prop = self.prop
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • I am trying to find out the behaviour of `global` on class variables. `global` CAN be used on a class variable. So saying "global has nothing to do with classes and objects" I would not deem correct. It can't be called without an instance or class, so it's behaviour is at least different from global on a 'regular' variable. But it is likely redundant, I haven't even come to evaluating that, merely how it works and if it works. I was trying to assign a class variable to an instance variables. For the time being I don't see any other way to do that unless using `global`. – Bentley4 Jun 19 '12 at 14:03
  • K srry, I understand now from Ned Batchelder's example what you were trying to explain and that the same behaviour can be done without global. I'm sorry I couldn't tell from your example. – Bentley4 Jun 19 '12 at 14:23