Understanding python class attributes - python

I am very new to programming and started learning python. Might look very stupid question, so please pardon my ignorance.
Consider the following snippet of code :
class Test1:
bar = 10
def display(self,foo):
self.foo=foo
print "foo : ",self.foo #80
def display1(self):
print "bar: ", self.bar #10
print "again foo: ", self.foo #80
if __name__ == '__main__':
test1 = Test1()
test1.display(80)
test1.display1()
print test1.bar #10
print test1.foo #80
I want to understand what is the difference between using foo and bar (wrt to where we have defined them) as in scope wise they are equally accessible at all places compared to each other and only difference is that one is inside function and other is inside Class but they both are still "instance" variable.
So which is good practice?
Also, if I slightly modify display function as below :
def display(self,foo):
self.foo=foo
foo = foo
print "self.foo : ",self.foo
print "foo : ",foo
Can someone please explain how python sees this, as in what difference/significance this self keyword is bringing in between two foo.

bar is a class attribute and foo is an instance attribute. The main difference is that bar will be available to all class instances while foo will be available to an instance only if you call display on that instance
>>> ins1 = Test1()
ins1.bar works fine because it is a class attribute and is shared by all instances.
>>> ins1.bar
10
But you can't access foo directly here as it is not defined yet:
>>> ins1.foo
Traceback (most recent call last):
File "<ipython-input-62-9495b4da308f>", line 1, in <module>
ins1.foo
AttributeError: Test1 instance has no attribute 'foo'
>>> ins1.display(12)
foo : 12
>>> ins1.foo
12
If you want to initialize some instance attributes when the instance is created then place them inside the __init__ method.
class A(object):
bar = 10
def __init__(self, foo):
self.foo = foo #this gets initialized when the instance is created
def func(self, x):
self.spam = x #this will be available only when you call func() on the instance
...
>>> a = A(10)
>>> a.bar
10
>>> a.foo
10
>>> a.spam
Traceback (most recent call last):
File "<ipython-input-85-3b4ed07da1b4>", line 1, in <module>
a.spam
AttributeError: 'A' object has no attribute 'spam'
>>> a.func(2)
>>> a.spam
2

bar is a class attribute. Since classes in Python are objects, they too can have attributes. bar just happens to live on that Test object, not an instance thereof.
Because of the way Python resolves attribute lookups, it looks like test1 has a bar attribute, but it doesn't.
foo on the other hand lives on the instance test1 after calling display(80). This means that different instances of Testcan have different values in their respective foo attributes.
Of course, you could use class variables as some kind of "shared default value", which you can then "override" with an instance attribute, but that might get confusing.
Second question
def display(self,foo):
self.foo=foo
foo = foo
print "self.foo : ",self.foo
print "foo : ",foo
Let's just get a detail out of the way: self is not a keyword, it's just convention to call the first argument "self", you could also call it "this" or "that" or "bar" if you liked, but I wouldn't recommend that.
Python will pass the object, on which the method was called as the first argument.
def display(self,foo):
This foo is the name of the first parameter of the display instance-function.
self.foo=foo
This sets the attribute with the name "foo" of the instance, on which you called display() to the value, which you passed as first argument. Using your example test1.display(80), self will be test1, foo is 80 and test1.foo will thus be set to 80.
foo = foo
This does nothing at all. It references the first parameter foo.
The next two lines again reference the instance variable foo and the first parameter foo.

Personally, I don't like defining instance variables inside of methods other than __init__ or as class variables like bar in your example above.
Again, personally, I like to see every member of my class by looking at the top. Whether it is a class variable that you use as an instance variable (something I don't usually do) or an instance variable defined in __init__, it is easy to tell what is and isn't defined in a class by inspecting the first section of the class definition.
If you won't need to access a variable as a class member (ie. you are only defining it there to avoid writing self.variable = val in the __init__ method, then I would steer clear of it. If you might need to access it as a class variable, then doing what you are with bar is ok in my book.
This would be my preferred way of writing you class.
class Test1:
def __init__(self):
self.foo = None
self.bar = 10
def display(self,foo):
self.foo=foo
print "foo : ",self.foo #80
def display1(self):
print "bar: ", self.bar #10
print "again foo: ", self.foo #80

Related

Do instance variables declared outside of `__init__()` have any differences in python?

I'm wondering weather it is necessary to define class instance variable within class declarations.
I tried assigning a new instance variable after the object (class instance) was already created, and looks like there is no difference. Are there any caveats in this approach?
class Context():
def __init__(self, extension):
self.extension = extension
c = Context('extension+')
print(f"before: {c.__dict__}")
c.new_var = 'new_var_content'
print(c.extension + c.new_var)
print(f"after: {c.__dict__}")
printed:
before: {'extension': 'extension+'}
extension+new_var_content
after: {'extension': 'extension+', 'new_var': 'new_var_content'}
There is no difference between declaring self.foo within a def __init__(self, <arguments>): definition, and declaring it after an object has been instantiated.
Both assignments have instance-level scope.
Given -
class Context:
i_am_a_class_variable = 'class_string'
def __init__(self, bar):
self.bar = bar
See -
class attributes can be accessed without instantiating an object.
>>> Context.i_am_a_class_variable
'class_string'
instance attributes can be assigned during instantiation using the __init__(self) function.
>>> Context.bar
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-4-8be0704da5be> in <module>
----> 1 Context.bar
>>> instance = Context('bar')
>>> instance.bar
'bar'
instance attributes can be assigned after instantiation, directly
>>> instance = Context('bar')
>>> instance.foo = 'foo'
>>> instance.foo
'foo'
Speaking in terms of whether you can assign a value to the property or create a new property, there is no difference if you do it within init or anywhere else after the object is created as in both cases it gets added in dict of the object(unless you use slots)
However, if you want your class to be initialized with desired state (i.e, having some mandatory variables with default/preset values) , you should put it in init. Since init is called implicitly as soon as object is created, you object will be having desired state.

Create an object of the same class within the class

I want to have an object of a class within a class, because I need to pass it to a method, similarly to the example below. I would like the example below to print out 1, or fooObj.fooNum, but I keep getting a NameError: name 'foo' is not defined.
class bar:
def fooDef(self, fooObj):
print fooObj.fooNum
class foo:
fooNum = 1
b = bar()
f = foo()
b.fooDef(f)
Please, can you be more specific about what you are trying to do?
The error you see is normal, because the code immediately below class foo will be executed during the definition of foo and therefore the class is not defined yet.
If I understand well you want to define some method foobar of the class foo, which will use a foo instance. The correct procedure would then be
class foo:
def foobar(self,):
f = foo()
...
Again, with more details about what you are trying to do it would be easier to help you.
Although it's unclear what you are asking, but the following changes do what you want to have.
But the code uses the instance of foo() not the class:
class bar:
def fooDef(self, fooObj):
print fooObj.fooNum
class foo:
def __init__(self):
self.fooNum = 1
b = bar()
f = self
b.fooDef(f)
f = foo()
Prints:
1

Adding a new attribute to Python object?

Being from OOPS background, It looks strange to see below code from link
def f():
f.beencalled = True
return 0
My question:
1)
From the above code,
Is f a reference variable pointing to an object f of class 'function'?
2)
We add a new attribute beencalled to an object f, so now 'function' class does not have this attribute beencalled defined and we say that object f is an object of class 'function'? Does it make sense?
1) Yes:
>>> def f():
print(type(f))
>>> f()
>>> <class 'function'>
2) The function class does not have a new attribute, but the object f does. Adding or removing attributes to/from an object does not affect which attributes other objects of that class will have:
>>> class A: pass
>>> a = A()
>>> a.var = 7
>>> b = A()
>>> b.var
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
b.newvar
AttributeError: 'A' object has no attribute 'var'
Classes are much more flexible in python than in Java or C++. Objects can have attributes not defined in their class, or even lack attributes that were defined in their class! Look at this:
>>> class A:
def __init__(self, a):
self.var = a
>>> obj = A(7)
>>> del obj.var #deletes the var attribute from obj, does not change the A class
>>> obj.var
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
obj.var
AttributeError: 'A' object has no attribute 'var'
>>> obj2 = A(6)
>>> obj2.var #obj2 is a new object, so the fact we deleted var from obj doesn't affect it
6
EDIT: after a bit of searching I found an explanation for why this behavior was chosen (source):
To implement user-defined objects, I settled on the simplest possible
design; a scheme where objects were represented by a new kind of
built-in object that stored a class reference pointing to a "class
object" shared by all instances of the same class, and a dictionary,
dubbed the "instance dictionary", that contained the instance
variables.
In this implementation, the instance dictionary would contain the
instance variables of each individual object whereas the class object
would contain stuff shared between all instances of the same class--in
particular, methods. In implementing class objects, I again chose the
simplest possible design; the set of methods of a class were stored in
a dictionary whose keys are the method names. This, I dubbed the class
dictionary. To support inheritance, class objects would additionally
store a reference to the class objects corresponding to the base
classes. At the time, I was fairly naïve about classes, but I knew
about multiple inheritance, which had recently been added to C++. I
decided that as long as I was going to support inheritance, I might as
well support a simple-minded version of multiple inheritance. Thus,
every class object could have one or more base classes.
In this implementation, the underlying mechanics of working with
objects are actually very simple. Whenever changes are made to
instance or class variables, those changes are simply reflected in the
underlying dictionary object. For example, setting an instance
variable on an instance updates its local instance dictionary.
Likewise, when looking up the value of a instance variable of an
object, one merely checks its instance dictionary for the existence of
that variable. If the variable is not found there, things become a
little more interesting. In that case, lookups are performed in the
class dictionary and then in the class dictionaries of each of the
base classes.
On a slightly different note, you can change this behavior for custom classes.
class FooBar(object):
__slots__ = ["foo","bar","baz"]
# if you don't define __slots__, you can add attr to the object as needed
# if you do, the object can only contain those attributes.
def __init__(self,foo=None,bar=None,baz=None):
self.foo = foo
self.bar = bar
self.baz = baz
def __str__(self):
return "I'm a FooBar with id {0} with foo: {1.foo}, bar: {1.bar}, baz: {1.baz}".format(id(self),self)
>>> a = FooBar("a","B","CCC")
>>> print(a)
I'm a FooBar with id 47260256 with foo: a, bar: B, baz: CCC
>>> a.spam = "eggs"
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
a.spam = "eggs"
AttributeError: 'FooBar' object has no attribute 'spam'
Alternately, without defining __slots__:
class BooFar(object):
def __str__(self):
return "I'm a BooFar with the following attributes:\n{}".format(self.__dict__)
>>> b = BooFar()
>>> print(b)
I'm a BooFar with the following attributes:
{}
>>> b.spam = "eggs"
>>> print(b)
I'm a BooFar with the following attributes:
{'spam': 'eggs'}
f() in just an instance of types.FunctionType, and instances can have their own attributes.
Adding an attribute to an instance won't affect its class unless you've overridden the __setattr__ method of that class and doing something evil there.
>>> import types
>>> def func(): pass
>>> isinstance(func, types.FunctionType)
True

How to access a method's attribute

Is it possible to access a method's attribute directly? I tried this and it fails:
class Test1:
def show_text(self):
self.my_text = 'hello'
Which results in:
>>> t = Test1()
>>> t.my_text
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Test1 instance has no attribute 'my_text'
I found that using this made it work:
class Test1:
def __init__(self):
self.my_text = 'hello'
But I'm wondering if it's still possible to access attributes of methods directly? Or am I doing something Very Bad?
Instance variables are created once the object has been instantiated and only after they have been assigned to.
class Example(object):
def doSomething(self):
self.othervariable = 'instance variable'
>> foo = Example()
>> foo.othervariable
AttributeError: 'Example' object has no attribute 'othervariable'
Since othervariable is assigned inside doSomething - and we haven't called it yet -, it does not exist.
Once we call it, though:
>> foo.doSomething()
>> foo.othervariable
'instance variable'
__init__ is a special method that automatically gets invoked whenever class instantiation happens. Which is why when you assign your variable in there, it is accessible right after you create a new instance.
class Example(object):
def __init__(self):
self.othervariable = 'instance variable'
>> foo = Example()
>> foo.othervariable
'instance variable'
my_text attribute doesn't exist until you don't call show_text:
>>> class Test1:
... def show_text(self):
... self.my_text = 'hello'
...
>>> t = Test1()
>>> t.show_text()
>>> t.my_text
'hello'
If you want your attributes to be created during instance creation then place them in __init__ method.
Your first example didn't work: since you never use show_text() method, your object will never have attribute my_text (that will be "added" to your object only when you invoke that method).
Second example is good, because __init__ method is executed as soon as your object is instantiated.
Moreover, is a good practice to access object attribute through getter method on object itself so the best way you can modify your code is
class Test1:
def __init__(self,value):
self.my_text = value
def show_text(self):
return self.my_text
and then use in that way
t = Test1('hello')
t.show_text()
At last, will be also good to have a method like this
def set_text(self,new_text):
self.my_text = new_text

In a Python class, what is the difference between creating a variable with the self syntax, and creating one without ?

What is the difference between creating a variable using the self.variable syntax and creating one without?
I was testing it out and I can still access both from an instance:
class TestClass(object):
j = 10
def __init__(self):
self.i = 20
if __name__ == '__main__':
testInstance = TestClass()
print testInstance.i
print testInstance.j
However, if I swap the location of the self, it results in an error.
class TestClass(object):
self.j = 10
def __init__(self):
i = 20
if __name__ == '__main__':
testInstance = TestClass()
print testInstance.i
print testInstance.j
>>NameError: name 'self' is not defined
So I gather that self has a special role in initialization.. but, I just don't quite get what it is.
self refers to the current instance of the class. If you declare a variable outside of a function body, you're referring to the class itself, not an instance, and thus all instances of the class will share the same value for that attribute.
In addition, variables declared as part of the class (rather than part of an instance) can be accessed as part of the class itself:
class Foo(object):
a = 1
one = Foo()
two = Foo()
Foo.a = 3
Since this value is class-wide, not only can you read it directly from the class:
print Foo.a # prints 3
But it will also change the value for every instance of the class:
print one.a # prints 3
print two.a # prints 3
Note, however, that this is only the case if you don't override a class variable with an instance variable. For instance, if you created the following:
class Bar(object)
a = 1
def __init__(self):
self.a = 2
and then did the following:
one = Bar()
two = Bar()
two.a = 3
Then you'd get the following results:
print Bar.a # prints "1"
print one.a # prints "2"
print two.a # prints "3"
As noted in the comments, assigning to two.a creates an instance-local entry on that instance, which overrides the a from Bar, hence why Bar.a is still 1 but two.a is 3.
j is a class variable as pointed by Amber. Now, if you come from C++ background, self is akin to the this pointer. While python doesn't deal with pointers, self plays the similar role of referring to current instance of the class.
In the python way, explicit is better than implicit. In C++, the availability of this is conventionally assumed for each class. Python, on the other hand, explicitly passes self as first argument to each of your instance methods.
Hence self is available only inside the scope of your instance methods, making it undefined for the place from which you tried using it.
Since you're made to explicitly pass self to instance methods, you could also call it something else if you want to -
>>> class Foo:
... b = 20
... def __init__(them):
... them.beep = "weee"
...
>>> f = Foo()
>>> f.beep
'weee'

Categories