1

Edit: I am not asking about what are classmethod and staticmethod or difference between them. Just asking the question to clarify what does state of class mean.

I just started python. While going through tutorials on @classmethod and @staticmethod I found a statement similar to below one in multiple websites.

Mentioned in geekforgeeks

A class method can access or modify class state while a static method can’t access or modify it.

Tutorialspoint

Class method can access and modify the class state. Static Method cannot access or modify the class state.


What does the class state mean? Does it mean that there is a way of modifying the values of all objects at go by using class method,because when a class state change it should affect all the objects created from that class? I could find only factory method creation with @classmethods and I don't think it is some class state change.

I am an advanced C++ programmer. Some related explanation would be good , if possible.

Edit: The question which marked this as duplicate of it doesn't mention the class states. I read both that questions and its duplicate before asking this.

One example I tried:

class MyClass:
 myvar = 100
 def __init__(self,age):
  self.myvar = age
 def instMethod(self):
  print("Inst method")

 @classmethod
 def classMethod(cls,age):
  cls.myvar = age

obj1 = MyClass(10)
obj2 = MyClass(20)
obj3 = MyClass(30)

print(obj1.myvar)
print(obj2.myvar)
print(obj3.myvar)

print("after class method")
MyClass.classMethod(45)

print(obj1.myvar)
print(obj2.myvar)
print(obj3.myvar)

output:

10

20

30

after class method

10

20

30

But my expectation was

10

20

30

after class method

45

45

45

Sreeraj Chundayil
  • 5,548
  • 3
  • 29
  • 68
  • Does [this](https://stackoverflow.com/questions/12179271/meaning-of-classmethod-and-staticmethod-for-beginner) answer you question? – LeoE Nov 26 '19 at 19:23
  • @LeoE: No. I already read that and its duplicate questions. It doesn't mention class states. – Sreeraj Chundayil Nov 26 '19 at 19:24
  • I thought the last paragraph of the accepted answer should have cleared that up: "So, as we can see from usage of staticmethod, we don't have any access to what the class is---it's basically just a function, called syntactically like a method, but without access to the object and its internals (fields and another methods), while classmethod does." -Rostyslav Dzinko – LeoE Nov 26 '19 at 19:29
  • @LeoE: I am not asking what's static method and class methods. – Sreeraj Chundayil Nov 26 '19 at 19:31
  • 1
    The statement `cls.myvar = age` changes the state of the class, not the state of the instances. In your example, each instance has its own `myvar` attribute, so `obj1.myvar` is that attribute. If the instances didn't have their own `myvar` attributes then `obj1.myvar` would fall back to the class's attribute, which has the value 45. – kaya3 Nov 26 '19 at 19:38

4 Answers4

5

Both of the explanations you have quoted are wrong. It is quite possible for a static method to modify the class's state:

class A:
    x = 1

    @staticmethod
    def change_static():
        A.x = 2

    @classmethod
    def change_class(cls):
        cls.x = 3

Proof:

>>> A.x
1
>>> A.change_static()
>>> A.x
2
>>> A.change_class()
>>> A.x
3

The correct statement is that a class method takes an argument for the class it is called on, named cls in this example. This allows the class method to access the class it is called on (which may in general be a subclass of A), much like an instance method takes an argument usually named self in order to access the instance it is called on.

A static method takes no such argument, but can still access the class by name.


For the second half of your question, you need to understand how accessing an attribute on an instance works:

  • If the instance has its own attribute of that name, then you will get the value of the instance's own attribute.
  • Otherwise, if the instance's class has its own attribute of that name, then you'll get that value instead.
  • Otherwise, you'll get the attribute belonging to the nearest superclass that has one of that name.
  • Otherwise, an AttributeError is raised.

Note that this applies only to getting the value of an attribute; if you set a.x = 23 then you will always be setting the attribute belonging to a itself, even if it didn't have such an attribute before. For example:

>>> a = A()
>>> a.x
3              # a has no x attribute, so this gets the value from the class
>>> A.x = 4
>>> a.x
4              # gets the updated value from the class
>>> a.x = 5
>>> A.x = 6
>>> a.x
5              # a has its own x attribute now, so this doesn't go to the class

In your code, the __init__ method sets the self.myvar attribute, so every instance has this attribute. Therefore, a lookup like obj1.myvar will never fall back to the class's attribute.

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • The static method is accessing *an* attribute of something; it's not necessarily accessing the same class that defined the method. That's the distinction people mean when they say the static method doesn't have access to class attributes. (Simple example: after defining `A`, set `C = A` and define a new class named `A`. Now `C.change_static()` no longer changes `C.x`, because it is hardcoded to refer to whatever object is bound to the name `A`.) – chepner Nov 26 '19 at 19:50
  • Your comment is correct but does not change the fact that a static method **can** change the state of the class, as my example demonstrates. The two quotes in the question are wrong because they say that a static method **cannot** do this. One counter-example is sufficient. – kaya3 Nov 26 '19 at 19:54
  • Nobody has ever said that a static method is magically prevented from changing an attribute of an object it is given a reference to. You are taking the quote far too literally. – chepner Nov 26 '19 at 19:59
  • I am taking the quotes literally because to take them the way you are taking them, the reader must already understand the thing which is being explained. They are wrong explanations. In languages like C++ or Java, a statement like "method X cannot access class Y" is absolute, independent of the way the method tries to access the class; so people may well expect that from such a statement, and not think there is anything magic involved. – kaya3 Nov 26 '19 at 20:02
  • The static method's ability to affect the state of its class is dependent on the global (or at least a nonlocal) namespace; the class method's is not. The reader *should* understand how name lookup works if they want to truly understand how virtually *everything* in Python works. – chepner Nov 26 '19 at 20:08
  • OK, and if the explanations quoted had said that then they would be correct. But they didn't say that, and they are not correct, as my demonstration shows. – kaya3 Nov 26 '19 at 20:08
2

Class state would include things like class attributes:

class A:
    x = 3

    @classmethod
    def change_x(cls, new_x):
       cls.x = new_x

Then

>>> A.x
3
>>> A.change_x(9)
>>> A.x
9

The distinction between a class method and a static method is that a class method is automatically provided with an instance of the class that invokes it, as its first argument; a static method is not.

chepner
  • 497,756
  • 71
  • 530
  • 681
1

In C++ terms, a static method is not bound by the object nor the object instance. A static method will not be able to access instance bound nor object / class bound variables.

A class method is very similar to a static method, in the sense that you can call a class method the same way you would call a static method: by the class rather than the instance of that class.

The major difference is that, the class method is bound by the object / class, and thus can access class state variables.

alex067
  • 3,159
  • 1
  • 11
  • 17
  • 1
    *"A static method will not be able to access ... class bound variables."* This is not true in Python; see my answer for an example. – kaya3 Nov 26 '19 at 19:28
  • 1
    @kaya3 The static method isn't *given* access to the class invoking it; your example hard codes *a* class reference in the body; whether that name refers to the class you expect it to depends on the current value of that global name. – chepner Nov 26 '19 at 19:45
  • "Given access to" is not a very clear thing to say; unlike languages like C++ or Java, Python doesn't have access control. The class `A` has access to itself the same way any other code does, and doesn't need to be given access to itself through an argument. Of course, if the global name `A` changes then the static method which returns `A.x` won't work, but neither will you be able to call `A.change_class()` for the class method either. Yes, there are edge cases where a reference to the class still exists through a different name than `A`, but this isn't an issue of access control. – kaya3 Nov 26 '19 at 19:51
  • See my comment on your answer. When we say "a static method can't access class attributes", we're really saying we aren't providing a reference to the class as an argument (the way `self` and `cls` are automatically provided to instance and class methods, respectively). – chepner Nov 26 '19 at 19:51
  • I don't think a reasonable reader would infer that meaning from the statement "can't access class attributes". If the statement were qualified as "can't access class attributes *through a reference to the class provided as a special argument to the method*" then that would be correct, but it is simpler to just say that no such special argument is provided to the method. – kaya3 Nov 26 '19 at 19:59
1

Here's an example, where calling a class method alters the class attribute, so that it is different for a new instance of the class:

class Foo:
    i = 1

    @classmethod
    def increase(cls):
        cls.i += 1


instance1 = Foo()
print(instance1.i)
>>> 1

instance1.increase()
instance2 = Foo()
print(instance2.i)
>>> 2
Toby Petty
  • 4,431
  • 1
  • 17
  • 29
  • Ok, this cleared my doubt. So already existing objects doesn't get affected at all. – Sreeraj Chundayil Nov 26 '19 at 19:41
  • 1
    It's not that existing objects aren't affected. Both `instance1.i` and `instance2.i` evaluate now to 2, because both expressions evaluate to the value of `Foo.i` (because neither instance has an *instance* attribute named `i`). – chepner Nov 26 '19 at 19:44
  • @chepner: Hmm, so i and self.i are entirely different. C++ to Python huge difference. i and this->i are same there. – Sreeraj Chundayil Nov 26 '19 at 19:50
  • 2
    Yes, Python is not C++. – chepner Nov 26 '19 at 19:53
  • 1
    @InQusitive do yourself a favor a read [the following documentation on python classes](https://docs.python.org/3.8/tutorial/classes.html). C++ and Python are pretty different (albeit both belong to very similar paradigms). You would probably do best by forgetting most of what you know about writing C++ class definitions and learning Python's approach on it's own terms. – juanpa.arrivillaga Nov 26 '19 at 19:59