Correct Way to Define Class Variables in Python

correct way to define class variables in Python

Neither way is necessarily correct or incorrect, they are just two different kinds of class elements:

  • Elements outside the __init__ method are static elements; they belong to the class.
  • Elements inside the __init__ method are elements of the object (self); they don't belong to the class.

You'll see it more clearly with some code:

class MyClass:
static_elem = 123

def __init__(self):
self.object_elem = 456

c1 = MyClass()
c2 = MyClass()

# Initial values of both elements
>>> print c1.static_elem, c1.object_elem
123 456
>>> print c2.static_elem, c2.object_elem
123 456

# Nothing new so far ...

# Let's try changing the static element
MyClass.static_elem = 999

>>> print c1.static_elem, c1.object_elem
999 456
>>> print c2.static_elem, c2.object_elem
999 456

# Now, let's try changing the object element
c1.object_elem = 888

>>> print c1.static_elem, c1.object_elem
999 888
>>> print c2.static_elem, c2.object_elem
999 456

As you can see, when we changed the class element, it changed for both objects. But, when we changed the object element, the other object remained unchanged.

Class (static) variables and methods

Variables declared inside the class definition, but not inside a method are class or static variables:

>>> class MyClass:
... i = 3
...
>>> MyClass.i
3

As @millerdev points out, this creates a class-level i variable, but this is distinct from any instance-level i variable, so you could have

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

This is different from C++ and Java, but not so different from C#, where a static member can't be accessed using a reference to an instance.

See what the Python tutorial has to say on the subject of classes and class objects.

@Steve Johnson has already answered regarding static methods, also documented under "Built-in Functions" in the Python Library Reference.

class C:
@staticmethod
def f(arg1, arg2, ...): ...

@beidy recommends classmethods over staticmethod, as the method then receives the class type as the first argument.

Python, how come we can create class variables that were not defined in class creation?

A class object is just an instance of yet another type, usually type (though you can change this using the metaclass parameter to the class statement).

Like most other instances, you can add arbitrary instance attributes to a class object at any time.

Class attributes and instance attributes are wholly separate; the former are stored on the class object, the latter on instances of the class.

There's nothing particularly special about __init__; it's just another method that, among other things, can attached new attributes to an object. What is special is that __init__ is called automatically when you create a new instance of the class by calling the class. foo = MyClass(2) is equivalent to

foo = MyClass.__new__(MyClass, 2)
foo.__init__(2)

The class statement

class MyClass(object):
class_var = 1

def __init__(self, i_var):
self.i_var = i_var

is roughly equivalent to

def my_class_init(self, i_var):
self.i_var = i_var

MyClass = type('MyClass', (object,), {'class_var': 1, '__init__: my_class_init})

The 3-argument form of type lets you pass a dict that creates class attributes when you first create the class, but you can always assign attributes after the fact as well:

MyClass = type('MyClass', (object,), {})
MyClass.class_var = 1
MyClass.__init__ = my_class_init

Just to blow your mind a little bit more, the call to type can be though of as

MyClass = type.__new__(type, 'MyClass', (object,), {...})
MyClass.__init__('MyClass', (object,), {...})

though unless you define a custom metaclass (by subclassing type), you never have to think about type itself having __new__ and __init__ methods.

How to properly access and set class variables in a derived class?

Is it because c.set_formats uses the class method with cls being an instance?

Yes, you can check via print calls showing the ids of the involved objects.

Does Python have “private” variables in classes?

It's cultural. In Python, you don't write to other classes' instance or class variables. In Java, nothing prevents you from doing the same if you really want to - after all, you can always edit the source of the class itself to achieve the same effect. Python drops that pretence of security and encourages programmers to be responsible. In practice, this works very nicely.

If you want to emulate private variables for some reason, you can always use the __ prefix from PEP 8. Python mangles the names of variables like __foo so that they're not easily visible to code outside the namespace that contains them (although you can get around it if you're determined enough, just like you can get around Java's protections if you work at it).

By the same convention, the _ prefix means _variable should be used internally in the class (or module) only, even if you're not technically prevented from accessing it from somewhere else. You don't play around with another class's variables that look like __foo or _bar.

Different ways to declare internal class variables in Python. Which is the best one?

Your first example doesn't work. You can define a static variable like this:

class ApplePie:

type = "apple"

def __init__(self):
print(f"I'm an {ApplePie.type} pie!")

This is a class property (i.e. it's shared among all instances of this class), as opposed to an instance property, accessed through self, like in your second example. These can differ among multiple instances of a class.

Your third example is

Useful for expensive computed properties of instances that are otherwise effectively immutable.

as described in the official documentation.

Proper way to create class variable in Data Class

To create a class variable, annotate the field as a typing.ClassVar or not at all.

from typing import ClassVar
from dataclasses import dataclass

@dataclass
class Foo:
ivar: float = 0.5
cvar: ClassVar[float] = 0.5
nvar = 0.5

foo = Foo()
Foo.ivar, Foo.cvar, Foo.nvar = 1, 1, 1
print(Foo().ivar, Foo().cvar, Foo().nvar) # 0.5 1 1
print(foo.ivar, foo.cvar, foo.nvar) # 0.5 1 1
print(Foo(), Foo(12)) # Foo(ivar=0.5) Foo(ivar=12)

There is a subtle difference in that the unannotated field is completely ignored by @dataclass, whereas the ClassVar field is stored but not converted to an attribute.



dataclasses — Data Classes

The member variables [...] are defined using PEP 526 type annotations.

Class variables

One of two places where dataclass() actually inspects the type of a
field is to determine if a field is a class variable as defined in PEP
526. It does this by checking if the type of the field is typing.ClassVar. If a field is a ClassVar, it is excluded from
consideration as a field and is ignored by the dataclass mechanisms.

Such ClassVar pseudo-fields are not returned by the module-level
fields() function.

How do I access Class member variables?

The answer, in a few words

In your example, itsProblem is a local variable.

Your must use self to set and get instance variables. You can set it in the __init__ method. Then your code would be:

class Example(object):
def __init__(self):
self.itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)

But if you want a true class variable, then use the class name directly:

class Example(object):
itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)
print (Example.itsProblem)

But be careful with this one, as theExample.itsProblem is automatically set to be equal to Example.itsProblem, but is not the same variable at all and can be changed independently.

Some explanations

In Python, variables can be created dynamically. Therefore, you can do the following:

class Example(object):
pass

Example.itsProblem = "problem"

e = Example()
e.itsSecondProblem = "problem"

print Example.itsProblem == e.itsSecondProblem

prints

True

Therefore, that's exactly what you do with the previous examples.

Indeed, in Python we use self as this, but it's a bit more than that. self is the the first argument to any object method because the first argument is always the object reference. This is automatic, whether you call it self or not.

Which means you can do:

class Example(object):
def __init__(self):
self.itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)

or:

class Example(object):
def __init__(my_super_self):
my_super_self.itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)

It's exactly the same. The first argument of ANY object method is the current object, we only call it self as a convention. And you add just a variable to this object, the same way you would do it from outside.

Now, about the class variables.

When you do:

class Example(object):
itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)

You'll notice we first set a class variable, then we access an object (instance) variable. We never set this object variable but it works, how is that possible?

Well, Python tries to get first the object variable, but if it can't find it, will give you the class variable. Warning: the class variable is shared among instances, and the object variable is not.

As a conclusion, never use class variables to set default values to object variables. Use __init__ for that.

Eventually, you will learn that Python classes are instances and therefore objects themselves, which gives new insight to understanding the above. Come back and read this again later, once you realize that.



Related Topics



Leave a reply



Submit