0

Can you please tell me what is a difference between calling Class_name.class_variable and self.class_variable inside method. Here is the example:

class Employee:

   raise_amount = 1.04

   def __init__(self, first, last, pay):
       self.first = first
       self.last = last
       self.pay = pay

   def apply_raise(self):
       self.pay = int(self.pay * Employee.raise_amount)

So I used an Employee.raise_amount, but i can also write this method like that:

def apply_raise(self):
    self.pay = int(self.pay * self.raise_amount)

I tested that with:

emp_1 = Employee('James', 'Amb', 10000)
emp_2 = Employee('Test', 'User', 20000)

print("Original value")
print("emp_1.raise_amount", emp_1.raise_amount)
print("emp_2.raise_amount", emp_2.raise_amount)

emp_1.raise_amount = 1.1
print("emp_1.raise_amount = 1.1")
print("emp_1.raise_amount", emp_1.raise_amount)
print("emp_2.raise_amount", emp_2.raise_amount)

Employee.raise_amount = 1.2
print("Employee.raise_amount = 1.2")
print("emp_1.raise_amount", emp_1.raise_amount)
print("emp_2.raise_amount", emp_2.raise_amount)

I run the program using Employee.raise_amount and then self.raise_amount. In both situation OUTPUT is the same:

Original value
emp_1.raise_amount 1.04
emp_2.raise_amount 1.04
emp_1.raise_amount = 1.1
emp_1.raise_amount 1.1
emp_2.raise_amount 1.04
Employee.raise_amount = 1.2
emp_1.raise_amount 1.1
emp_2.raise_amount 1.2

So what is a difference and when should I use Class_name.class_variable and self.class_variable

1
  • Class_name.class_variable is called class variable which is owned by the class and self.class_variable is an instance variable, owned by an instance of a class. Because a class variable is shared by instances of a class, sometimes used to reduce repetition within code, helpful to adheres your code to the DRY principle. Commented May 19, 2022 at 15:53

2 Answers 2

3

Python attribute reading, with self.attribute works like this:

  1. Python checks if the attribute is a data descriptor in the class or ancestor class, if so, its __get__ method is called (not the case, I will get back here later)
  2. Then checks the instance (self) __dict__ attribute, and sees if it contains attribute, if so, its fetched
  3. checks if there is a "non data descriptor" (a descriptor without the __set__ method, such as a function or method)
  4. Then checks if it is a plain (non descriptor) attribute in the class __dict__ <- you are here!
  5. Checks for a plain attribute in any of the ancestor classes
  6. Calls __gettattr__ method on the class if it exists
  7. Raises attribute error.

This set of rules is actually rather natural when one is making use of the language. But you have to take care if at any point you are writting back to the attribute - if you do something like self.attribute = value, then the attribute is set in the instance, not on the class, and if in other method you are retrieving it by doing ClassName.attribute it will see the orignal value, set on the class, not the value set on self, even if it is the same instance.

All things considered, the natural design of always using self.attribute will work better for read-only class attributes. If you need a special value for a single instance, you can set a new value for that instance only, and everything keeps working. For an example using your case, let's say that for most employees, the raise_ammount is 1.04, but one of them is special cased to be 1.06, any method or even external code can set the new attribute on the instance, and methods reading the value from self. will peek the updated number.

As for "descriptors" - they are special objects bound to the class that feature one of __get__, __set__ or __delete__ methods, and override attribute access on the instance and class. They are the mechanism used by the @property decorator, and by methods and classmethods themselves (so that the language can insert the self parameter in method calls).

Oh, and all these steps are encoded in the language in object.__getattribute__ method (not the same as __getattr__). Any class that overrides __getattribute__ can rewrite these rules at will (usually one will only tweak access to certain attributes, in order to create a proxy object, or something similar, not come out with a full hierarchical set of rules nearly as complex)

Sign up to request clarification or add additional context in comments.

Comments

1

One plays nice with inheritance, the other doesn't.

class Base:
    a = 1

    def f(self):
        print(Base.a)


class Foo(Base):
    a = 2

Base().f()
Foo().f()

outputs

1
1

Changing print(Base.a) to print(self.a) changes the output to

1
2

because now self is an instance of Foo (so it refers to Foo.a) where Base.a unsurprisingly refers to Base.a, regardless of the current instance we use to call f.

1 Comment

The disadvantage of self.a is that it is ambiguous. It sometimes refers to the instance variable and sometimes to the class variable (if the instance variable is not set). In most cases the ambiguity is ok. It is a feature. However, if you want to be access the class variable SPECIFICALLY in a way that plays nice with inheritance, you can use self.__class__.a

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.