13

Or can they be declared otherwise?

The code below does not work:

class BinaryNode():
    self.parent = None
    self.left_child = None

Do they need to be declared in __init__?

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
aerain
  • 1,149
  • 3
  • 12
  • 17
  • 8
    What you are attempting to do appears at first glance to misguided. Perhaps you could explain what problem you are trying to solve? – Oddthinking Sep 05 '12 at 03:42
  • +1 on the above comment. What is the problem you are trying to solve? @Oddthinking: is correct. You are likely going about solving the wrong problem, or at least, in the wrong way. – sberry Sep 05 '12 at 03:57

4 Answers4

13

They do not have to be declared in __init__, but in order to set an instance variable using self, there needs to be a reference to self, and the place you are defining the variables does not.

However,

class BinaryNode():
    parent = None
    left_child = None

    def run(self):
        self.parent = "Foo"
        print self.parent
        print self.left_child

The output will be

Foo
None

To answer your question in the comment, yes. You can, in my example say:

bn = BinaryNode()
bn.new_variable = "Bar"

Or, as I showed, you can set a class level variable. All new instances of the class will get a copy of the class level variables at instantiation.

Perhaps you are not aware that you can pass arguments to the constructor:

class BinaryNode(object):

    def __init__(self, parent=None, left_child=None):
        self.parent = parent
        self.left_child = left_child



bn = BinaryNode(node_parent, node_to_the_left)
sberry
  • 128,281
  • 18
  • 138
  • 165
  • Is there a way to set an instance variable without using self? – aerain Sep 05 '12 at 03:41
  • But the class level variable will be shared among all instances of the class correct? – aerain Sep 05 '12 at 03:44
  • I can't quite imagine how such a `parent` member shared between all instances of a class would be useful. – Greg Hewgill Sep 05 '12 at 03:46
  • @GregHewgill: To be clear, I am not suggesting that a `parent` member be shared across all instances. I was just using the same names as given by the OP. – sberry Sep 05 '12 at 03:48
  • once you call `self.xxx = yy` instead of `SomeClass.xxx = yyy`you are turning it into an instance variable... (or at least its value is unique to that value) at least i think so in my py2.6 – Joran Beasley Sep 05 '12 at 03:57
3

Must all Python instance variables be declared in def __init__? Short answer: no. But it really depends on how/where to use them.

Or can they be declared otherwise? Yes they can be declared otherwise (not an instance variable). Since you're referring to variables inside a class, yes you can declare a local variable (for example) inside of it if you don't want to use __init__ constructor function that uses the keyword self.

The code below does not work:

class BinaryNode():
    self.parent = None
    self.left_child = None

What do you mean by not working? if you're referring to how most developers use a class, then you are right, it does not work in the sense that you can not create a class object (it's also called class instance) and then call one of its instance variables assuming they are declared properly (ie: declared INSIDE the init method that plays the role of the constructor method.)

In other words: if you declare a variable outside the constructor method init then it's called a class variable and not an instance variable. To initiate an instance variable, it needs to be inside the init method that will actually construct (instantiate) the class instance (class object) along with its associated instance variables (instance attributes.)

for example:

if you have parent = None instead of self.parent = None you just created a local variable inside your class.

So the word self is there to hold the place for when you call the class instance is called, it's passed-in in lieu of self. Therefor the word self is just there as a parameter to hold a place for passing whatever instance variable we want to call in its place. So the word self is just doing a favor service here.

Similarly , when creating a class object and say you want to create an instance variable, you must use the attribute word self (it does not have to be self but since most python developers use it, using it became a convention and a good practice in python) that will work as a reference (a holder) to complete the work of the constructor function __init__ which will assign the argument value passed-in the class instance (object) created to be the new value of the instance variable copy that belongs to the class instance created.

Now, let's try another scenario to better solidify the concept:

What if we use the __init__ function but not the keyword self? if you create a class object named object_x and then you want to call one of its instance variables named instance_y from the , then you execute the program, it will throw an error saying that the class object created does not have an attribute (instance variable).

It will be just a local variable with whatever value you assigned to it when you defined it.

The error occurs because it doesn't recognize it as one of the instance variables defined inside the class when it's missing the reference word self.

Fouad Boukredine
  • 1,495
  • 14
  • 18
2

Nope. I love the @property variable for just this thing:

class Data(object):
    """give me some data, and I'll give you more"""
    def __init__(self, some, others):
        self.some   = some
        self.others = others

    @property
    def more(self):
        """you don't instantiate this with __init__, per say..."""
        return zip(self.some, self.others)


>>> mydata = Data([1, 2, 3], ['a', 'b', 'c'])
>>> mydata.more
[(1, 'a'), (2, 'b'), (3, 'c')]
yurisich
  • 6,991
  • 7
  • 42
  • 63
  • You are missing a `self` in your `__init__`. – sberry Sep 05 '12 at 03:49
  • `@property` is a "decorator". Properties are outside of the scope of the original question, though, as they don't have anything to do with declaring of attributes. Effectively, it's the same as `more = property(fget=lambda self: zip(self.some, self.others)`; that is, it's a standard attribute assignment that just happens to hold a wrapper for specialised behaviour. – Matthew Trevor Sep 05 '12 at 04:14
  • @MatthewTrevor as far as a user of the Data class is concerned, `more` will not appear as a function that returns a "getter", but as a simple property (with validation, assignment, exceptions, and the like). I'm not saying you aren't correct... – yurisich Sep 05 '12 at 04:17
  • But we're not talking appearances here, we're talking actuality of assignment. It just confuses the issue a little as it's effectively irrelevant. – Matthew Trevor Sep 05 '12 at 04:29
0

Also, you can have class level variables, but I call them class constants.

class Connection(object):
    """helps you connect to the server at work"""
    YOUR_IP = '10.0.9.99'

    def __init__(self, username, password):
        self.ip = Connection.YOUR_IP
        self.un = username
        self.pw = password

    #...and so on
yurisich
  • 6,991
  • 7
  • 42
  • 63