11

So I understand how to use init when defining a class in Python. However, I am confused as to what to put inside the init function, or if I should use it at all. For example:

Here I make the user define concept when the class is instantiated:

class Fruit(object):
    def __init__(self, concept):
        self.concept = concept

But I could also do this, and the user can just change the concept attribute themselves:

class Fruit(object):
    def __init__(self):
        self.concept = "I am a fruit"

Or I could just abandon the init function altogether:

class Fruit(object):
    concept = "I am a fruit"

In each of these examples, I can change the concept attribute as such

test = Fruit()
test.concept = 'Whatever I want'

So I'm not sure why I use the init function in the first place. It seems to only be convenient for defining all the attributes up front.

Moreover, how do I know what to put inside an init function and what to just define outside of the init?

Community
  • 1
  • 1
Daniel
  • 363
  • 3
  • 11
  • 4
    The line `concept = "I am a fruit"` defines a *class* variable, not an instance variable. – Jared Goguen Jan 28 '17 at 17:32
  • `test = Fruit('Whatever I want', "other", "Another")` looks better then `test = Fruit() ; test.concept = 'Whatever I want' ; test.concept_2 = "other" ; test.concept_3 = "Another"` – furas Jan 28 '17 at 17:34
  • Start off *putting everything in* ```__init__``` till you come accross a need to to do something different. – wwii Jan 28 '17 at 17:36
  • 1
    `__init__` should initialize an object, so that it is in a useful state. – Daniel Jan 28 '17 at 17:37
  • @furas So it's more stylistic/user friendly than it is functional? – Daniel Jan 28 '17 at 17:40

2 Answers2

8
class Fruit(object):
    def __init__(self, concept):
        self.concept = concept

is the best approach because it allows you, as the maintainer of the class, flexibility in changing the inner workings. In addition, you may have data inside the class that should not be accessed by the client (in Python, it's not entirely possible to protect data, but there are some conventions that exist).

For example:

class Fruit(object):
    def __init__(self, concept):
        self.concept = concept
        self._super_secret_key = get_secret_key(concept)

You might not want _super_secret_key to be accessible by a client (again, it still is) but by prefixing the instance variable with _, you are conveying that it should be private.

In addition, the purpose of __init__ is to initialize the instance, so any initialization (such as setting variables) should be done there. You may want to read over Why do we use __init__ in python classes?.


class Fruit(object):
    def __init__(self):
        self.concept = "I am a fruit"

In each of these examples, I can change the concept attribute as such

test = Fruit()
test.concept = 'Whatever I want'

That type of code becomes messy very quickly. What if a year from now, you change the variable name of concept, or abandon that usage altogether? All of the clients of that class (i.e. any time you use the Fruit class) are now non-functional.


class Fruit(object):
    concept = "I am a fruit"

This serves an entirely different purpose, as concept is now a class variable, not an instance variable. As such, it is shared by all instances and can be modified in similar fashion:

In [1]: class Fruit(object):
   ...:     concept = "I am a fruit"
   ...:     

In [2]: a = Fruit()

In [3]: a.concept
Out[3]: 'I am a fruit'

In [4]: Fruit.concept = "I am NOT a fruit"

In [5]: a.concept # a.concept has changed just by changing Fruit.concept
Out[5]: 'I am NOT a fruit'
Rushy Panchal
  • 16,979
  • 16
  • 61
  • 94
  • 1
    Thanks @Rushy, this is very helpful. I did read over the answer you linked (and linked it in my question), but it didn't completely answer my question. Your answer does answer my question, though. Thank you so much, this is very clear and helpful. – Daniel Jan 28 '17 at 17:45
1

After calling the class, the instance should be ready to go and not require additional attribute settings.

You left out the usual answer for when there is a sensible non-mutable default.

class Fruit(object):
    def __init__(self, concept='I am a fruit'):
        self.concept = concept
Terry Jan Reedy
  • 18,414
  • 3
  • 40
  • 52