Is there any difference at all between these classes besides the name?

class WithClass ():
    def __init__(self):
        self.value = "Bob"
    def my_func(self):
        print(self.value)

class WithoutClass ():
    value = "Bob"

    def my_func(self):
        print(self.value)

Does it make any difference if I use or don’t use the __init__ method for declaring the variable value?

My main worry is that I’ll be using it one way, when that’ll cause me further problems down the road.

Variable set outside __init__ belong to the class. They’re shared by all instances.

Variables created inside __init__ (and all other method functions) and prefaced with self. belong to the object instance.

Without Self

Create some objects:

class foo(object):
    x = 'original class'

c1, c2 = foo(), foo()

I can change the c1 instance, and it will not affect the c2 instance:

c1.x = 'changed instance'
c2.x
>>> 'original class'

But if I change the foo class, all instances of that class will be changed as well:

foo.x = 'changed class'
c2.x
>>> 'changed class'

Please note how Python scoping works here:

c1.x
>>> 'changed instance'

With Self

Changing the class does not affect the instances:

class foo(object):
    def __init__(self):
        self.x = 'original self'

c1 = foo()
foo.x = 'changed class'
c1.x
>>> 'original self'

I would like to add something to the responses that I read in this thread and this thread (which references this one).

Disclaimer: this remarks come from the experiments I ran

Variables outside __init__:

These are, in fact, static class variables and are, therefore, accesible to all instances of the class.

Variables inside __init__:

The value of these instance variables are only accesible to the instance at hand (through the self reference)

My contribution:

One thing that programmers must consider when using static class variables is that they can be shadowed by instance variables (if you are accessing the static class variables through the self reference).

Explanation:

Previously, I thought that both ways of declaring the variables were exactly the same (silly me), and that was partly because I could access both kind of variables through the self reference. It was now, when I ran into trouble, that I researched the topic and cleared it up.

The problem with accessing static class variables through the
self reference is that it only references the static class variable if there is no instance variable with the same name, and to make things worse, trying to redefine a static class variable through the self reference does not work because an instance variable is created which then shadows the previously-accesible static class variable.

To get around this problem, you should always reference static class variables through the name of the class.

Example:

#!/usr/bin/env python

class Foo:
    static_var="every instance has access"

    def __init__(self,name):
        self.instance_var="I am %s" % name

    def printAll(self):
        print 'self.instance_var = %s' % self.instance_var
        print 'self.static_var = %s' % self.static_var
        print 'Foo.static_var = %s' % Foo.static_var

f1 = Foo('f1')

f1.printAll()

f1.static_var="Shadowing static_var"

f1.printAll()

f2 = Foo('f2')

f2.printAll()

Foo.static_var="modified class"

f1.printAll()
f2.printAll()

Output:

self.instance_var = I am f1
self.static_var = every instance has access
Foo.static_var = every instance has access
self.instance_var = I am f1
self.static_var = Shadowing static_var
Foo.static_var = every instance has access
self.instance_var = I am f2
self.static_var = every instance has access
Foo.static_var = every instance has access
self.instance_var = I am f1
self.static_var = Shadowing static_var
Foo.static_var = modified class
self.instance_var = I am f2
self.static_var = modified class
Foo.static_var = modified class

I hope this is helpful to someone

further to S.Lott’s reply, class variables get passed to metaclass new method and can be accessed through the dictionary when a metaclass is defined. So, class variables can be accessed even before classes are created and instantiated.

for example:

class meta(type):
    def __new__(cls,name,bases,dicto):
          # two chars missing in original of next line ...
          if dicto['class_var'] == 'A':
             print 'There'
class proxyclass(object):
      class_var="A"
      __metaclass__ = meta
      ...
      ...

class User(object):
    email="none"
    firstname="none"
    lastname="none"

    def __init__(self, email=None, firstname=None, lastname=None):
        self.email = email
        self.firstname = firstname
        self.lastname = lastname

    @classmethod
    def print_var(cls, obj):
        print ("obj.email obj.firstname obj.lastname")
        print(obj.email, obj.firstname, obj.lastname)
        print("cls.email cls.firstname cls.lastname")
        print(cls.email, cls.firstname, cls.lastname)

u1 = User(email="[email protected]", firstname="first", lastname="last")
User.print_var(u1)

In the above code, the User class has 3 global variables, each with value ‘none’. u1 is the object created by instantiating this class. The method print_var prints the value of class variables of class User and object variables of object u1. In the output shown below, each of the class variables User.email, User.firstname and User.lastname has value 'none', while the object variables u1.email, u1.firstname and u1.lastname have values '[email protected]', 'first' and 'last'.

obj.email obj.firstname obj.lastname
('[email protected]', 'first', 'last')
cls.email cls.firstname cls.lastname
('none', 'none', 'none')

In Python, a class comes with member functions (methods), class variables, attributes/instance variables (and probably class methods too):

class Employee:

    # Class Variable
    company = "mycompany.com"

    def __init__(self, first_name, last_name, position):
        # Instance Variables
        self._first_name = first_name
        self._last_name = last_name
        self._position = position

    # Member function
    def get_full_name(self):
        return f"{self._first_name} {self._last_name}"

By creating an instance of the object

my_employee = Employee("John", "Wood", "Software Engineer")

we essentially trigger __init__ that is going to initialise the instance variables of the newly created Employee. This means that _first_name, _last_name and _position are explicit parameters of the specific my_employee instance.

Likewise, member functions return information or change the state of a specific instance.


Now any variable defined outside the constructor __init__ are considered to be class variables. Those variables are shared amongst all the instances of the class.

john = Employee("John", "Wood", "Software Engineer")
bob = Employee("Bob", "Smith", "DevOps Engineer0")

print(john.get_full_name())
print(bob.get_full_name())
print(john.company)
print(bob.company)

>>> John Wood
>>> Bob Smith
>>> mycompany.com
>>> mycompany.com

You can also use class methods in order to change the class variable for all the instances of the class. For example:

@classmethod
def change_my_companys_name(cls, name):
    cls.company = name

and now change_my_companys_name()

bob.change_my_companys_name("mynewcompany.com")

will have effect on all the instances of class Employee:

print(bob.company)
print(john.company)

>>> mynewcompany.com
>>> mynewcompany.com

Example code:

class inside:
    def __init__(self):
        self.l = []

    def insert(self, element):
        self.l.append(element)


class outside:
    l = []             # static variable - the same for all instances

    def insert(self, element):
        self.l.append(element)


def main():
    x = inside()
    x.insert(8)
    print(x.l)      # [8]
    y = inside()
    print(y.l)      # []
    # ----------------------------
    x = outside()
    x.insert(8)
    print(x.l)      # [8]
    y = outside()
    print(y.l)      # [8]           # here is the difference


if __name__ == '__main__':
    main()

This is very easy to understand if you track class and instance dictionaries.

class C:
   one = 42
   def __init__(self,val):
        self.two=val
ci=C(50)
print(ci.__dict__)
print(C.__dict__)

The result will be like this:

{'two': 50}
{'__module__': '__main__', 'one': 42, '__init__': <function C.__init__ at 0x00000213069BF6A8>, '__dict__': <attribute '__dict__' of 'C' objects>, '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None}

Note I set the full results in here but what is important that the instance ci dict will be just {'two': 50}, and class dictionary will have the 'one': 42 key value pair inside.

This is all you should know about that specific variables.

classes are like blueprints to create objects. Let’s make a metaphor with building a house. You have the blueprint of the house so you can build a house. You can build as many houses as your resources allow.

In this metaphor, the blueprint is the class and the house is the instantiation of the class, creating an object.

The houses have common attributes like having a roof, living room, etc. This is where you init method goes. It constructs the object (house) with the attributes you want.

Lets suppose you have:

`class house:`
`roof = True`
`def __init__(self, color):`
`self.wallcolor = color`

>> create little goldlock's house:

>> goldlock = house() #() invoke's class house, not function

>> goldlock.roof

>> True

all house's have roofs, now let's define goldlock's wall color to white:

>> goldlock.wallcolor="white"
>>goldlock.wallcolor
>> 'white'

class foo(object):
    mStatic = 12

    def __init__(self):
        self.x = "OBj"

Considering that foo has no access to x at all (FACT)

the conflict now is in accessing mStatic by an instance or directly by the class .

think of it in the terms of Python’s memory management :

12 value is on the memory and the name mStatic (which accessible from the class)

points to it .

c1, c2 = foo(), foo() 

this line makes two instances , which includes the name mStatic that points to the value 12 (till now) .

foo.mStatic = 99 

this makes mStatic name pointing to a new place in the memory which has the value 99 inside it .

and because the (babies) c1 , c2 are still following (daddy) foo , they has the same name (c1.mStatic & c2.mStatic ) pointing to the same new value .

but once each baby decides to walk alone , things differs :

c1.mStatic ="c1 Control"
c2.mStatic ="c2 Control"

from now and later , each one in that family (c1,c2,foo) has its mStatica pointing to different value .

[Please, try use id() function for all of(c1,c2,foo) in different sates that we talked about , i think it will make things better ]

and this is how our real life goes . sons inherit some beliefs from their father and these beliefs still identical to father’s ones until sons decide to change it .

HOPE IT WILL HELP

As noted by S.Lott,

Variable set outside init belong to the class. They’re shared by
all instances.

Variables created inside init (and all other method functions) and
prefaced with self. belong to the object instance.

However,
Note that class variables can be accessed via self.<var> until they are masked by an object variable with a similar name This means that reading self.<var> before assigning it a value will return the value of Class.<var> but afterwards it will return obj.<var> . Here is an example

In [20]: class MyClass: 
    ...:     elem = 123 
    ...:  
    ...:     def update(self,i): 
    ...:         self.elem=i  
    ...:     def print(self): 
    ...:         print (MyClass.elem, self.elem) 
    ...:  
    ...: c1 = MyClass()  
    ...: c2 = MyClass() 
    ...: c1.print() 
    ...: c2.print()                                                                                                                                                                                                                                                               
123 123 
123 123 
In [21]: c1.update(1) 
    ...: c2.update(42) 
    ...: c1.print() 
    ...: c2.print()                                                                                                                                                                                                                                                               
123 1 
123 42
In [22]: MyClass.elem=22 
    ...: c1.print()  
    ...: c2.print()                                                                                                                                                                                                                                                               
22 1 
22 42

Second note: Consider slots. They may offer a better way to implement object variables.

Try this and check the difference

class test:
    f = 3

    def __init__(s, f):
        s.__class__.f = f
        s.f = s.__class__.f
        print(f'def __init__(s, {f})')
        print(f's.__class__.f = {f}')
        print(f's.f={s.__class__.f}')
        print(f'f={f}')
        print('===============init over===========')

    def setinstancetoOne(s, f):
        print(f'def setinstancetoOne(s, {f})')
        s.f = f

    print(f'class var f = {f}')

    def useClassname(test):
        print(f'>>>>def useClassname({test})')
        print(f'test.f {test.f}')

    def p_method(s):
        print(f'>>>>def p_method({s})')
        print(f's.f {s.f}')
        print(f'test.f {test.f}')
        print(f's.__class__.f {s.__class__.f}')

    print(f'class var f={f}')


# test.__init__.f = 19
t = test(2)
t.useClassname()
t.p_method()
print(f'Outside class t.f {t.f}')
print(f'Outside class test.f {test.f}')

print('______difference__________')
t = test(2)
t.setinstancetoOne(1)
t.useClass()
t.p_method()
print(f'Outside class instance variable(2) {t.f}')
print(f'Outside class class variable(3) {test.f}')