Class variables behave differently for list and int? - python

The class shared variables are shared with all the instances of the classes as far as I know. But I am having trouble getting my head around this.
class c():
a=[1]
b=1
def __init__(self):
pass
x=c()
x.a.append(1)
x.b+=1 #or x.b=2
print x.a #[1,1]
print x.b #2
y=c()
print y.a #[1,1] :As Expected
print y.b #1 :why not 2?
y.a resonates with the x.a but
y.b doesn't.
hope someone can clarify.
EDIT: And how can the same functionality be created for ints.

x.a.append(1)
changes the class attribute c.a, a list, by calling its append method, which modifies the list in-place.
x.b += 1
is actually a shorthand for
x.b = x.b + 1
because integers in Python are immutable, so they don't have an __iadd__ (in-place add) method. The result of this assignment is to set an attribute b on the instance x, with value 2 (the result of evaluating the right-hand side of the assignment). This new instance attribute shadows the class attribute.
To see the difference between an in-place operation and an assignment, try
x.a += [1]
and
x.a = x.a + [1]
These will have different behavior.
EDIT The same functionality can be obtained for integers by boxing them:
class HasABoxedInt(object):
boxed_int = [0] # int boxed in a singleton list
a = HasABoxedInt()
a.boxed_int[0] += 1
b = HasABoxedInt()
print(b.boxed_int[0]) # prints 1, not zero
or
class BoxedInt(object):
def __init__(self, value):
self.value = value
def __iadd__(self, i):
self.value += i

larsmans' answer is excellent, but it might provide additional insight if we look at id of x.b before and after the assignment.
class c():
a=[1]
b=1
def __init__(self):
pass
x=c()
print "initial a : {} at {}".format(x.a, id(x.a))
print "initial b : {} at {}".format(x.b, id(x.b))
x.a.append(1)
x.b+=1 # x.b = x.b + 1, created a new object
# we created an instance variable x.b and it
# is shadowing the class variable b.
print "after change a : {} at {}".format(x.a, id(x.a))
print "after change b : {} at {}".format(x.b, id(x.b))
y=c()
# We can already see from the class object that
# b has not changed value
print "in class c b : {} at {}".format(c.b, id(c.b))
print "in instance y a : {} at {}".format(y.a, id(y.a))
print "in instance y b : {} at {}".format(y.b, id(y.b))
Result:
initial a : [1] at 50359040
initial b : 1 at 40974280
after change a : [1, 1] at 50359040
after change b : 2 at 40974256 # Shows id of instance variable x.b; hence it is
# different
in class c b : 1 at 40974280
in instance y a : [1, 1] at 50359040
in instance y b : 1 at 40974280
If you want to use an int as a class variable, this should work:
class MyClass(object):
b=1
def increase_b(self, n):
MyClass.b += n
Result:
>>> mc_1 = MyClass()
>>> mc_1.b
1
>>> mc_1.increase_b(5)
>>> mc_1.b
6
>>> mc_2 = MyClass()
>>> mc_2.b
6
>>> mc_2.increase_b(10)
>>> MyClass.b
16
>>> mc_2.b
16
>>> mc_1.b
16

Related

OOP - Understanding Inheritance

I'm beginning my journey into OOP by trying to learn inheritance. I saw this code in an online quiz about the topic and I was hoping someone could explain it to me since it makes little sense to me.
class A(object) :
def __init__(self, x) :
self._x = 2 * x
def m1(self, x) :
return self.m2(x) + 2
def m2(self, x) :
return x - 1
class B(A) :
def m2(self, y) :
self._y = y
return self._x + self._y
For the following, if I was to say a = A(1), what would be the expected return? The initialization multiplies the 1 with 2, so now the instance of x has value 2. What happens with the method m1? it receives this instance of X but in the return it refers to m2? so is that x=2 passed into m2 first and then the return of 1 is passed to m1? which adds 2 to it?
As for Class B, I see it changes the inherited m2 method from Class A, but the x value that is added to the y, is that an inherited x value from Class A?
Sorry about the endless questions but I'm just beginning and it seems hard to wrap my head around.
The easiest way to figure out what the code does is to run it.
>>> a = A(1)
>>> a.m1(10)
11
An important thing to note here (which from your question it sounded like you might be confused on) is that the value of x you pass to A.__init__ does not get used by A.m1! So A.m1(x) just returns x + 1 no matter how you initialized the A instance.
>>> a = A(1000)
>>> a.m1(1)
2
>>> a.m1(10)
11
>>> a.m1(100)
101
Okay, what if we make a B the same way?
>>> b = B(1)
>>> b.m1(10)
14
Now it's different, because B.m2 is different from A.m2. We can just run those on their own to see:
>>> a.m2(10)
9
>>> b.m2(10)
12
a.m2(10) is 9 because a.m2(x) is always just x - 1 regardless of what a._x is.
But b.m2(10) returns b._x + 10, which in this case is 12 -- and so b.m1(10) returns that plus 2. The results will be different if we change what b._x is by initializing our B with a different value:
>>> b = B(2)
>>> b.m2(10)
14
>>> b.m1(10)
16
You can always track what happens:
...
class B(A) :
def m2(self, y) :
self._y = y
return self._x + self._y
class B then looks like Bx as m2 is overridden:
class Bx:
def __init__(self, x) :
self._x = 2 * x
def m1(self, x) :
return self.m2(x) + 2
def m2(self, y) :
self._y = y
return self._x + self._y
So this is how class B looks like-ish when it has inherited A
I'll try answering your questions in order:
If you were to say a = A(1), __init__ would set variable self._x to 2. This does not change, as you do not ever edit the self._x variable after this.
The functions m1 and m2 could return anything based on x that you pass in. Did you mean self._x? And yes, m2 would execute when you call m1, m1 for any number x would just return x + 1.
a = A(1) is just instantiating class A, the methods would work with just A(x).m1(y), whereas if you include a = A(1) you would execute a.m1(y).
Instantiating class B with any number (B(x)) runs the __init__() function of A (super().init(args)) with the x passed (Try running it yourself with something like b = B(2); print(b._x)!). Thus, self._x is 2 * x that you passed for B!
You have overwritten method m2() in class B, so m2() from A is not used in B.
If you have any other questions comment and I'll attempt to answer them!

Changing Method called without new instance of Python Class

I'm new to Python OOP and for the purpose of this question I have simplified my problem to this:
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
# some arbitrary change
return self.a + self.b
def subtract(self):
# some arbitrary change
return self.a - self.b
a = Foo(a=1, b=2).add()
b = Foo(a=1, b=3).subtract()
So I have an object, which has 2 methods which do different things, in order for me to get some output, I have created 2 separate instances of Foo as the value b has changed.
Is there a way for me to just dynamically set b and the obj.method() without just listing them one after the other? I.E: some sort of generic class that I can use to dynamically set the attributes and the methods that are present in the object? or is there anything built in I can use...
Edit
Here is another example:
class Foo:
def __init__(self, a, b):
self.a = list(a)
self.b = list(b)
def method1(self):.
# some arbitrary change in data
return self.a * 2
def method2(self):
return self.b + [5, 6, 4]
a = Foo(a=[1, 2, 3], b=[]).method1()
b = Foo(b=[1, 2, 3], a=[]).method2()
print(a)
print(b)
So here, the input list changes based on the method called, is there a way for me to package this up so I could feed just one instance some data and then it 'knows' that list a is for method1(), list b is for method2() - I want to use the word reflection but I feel like that might not be accurate.
Again I'm new to OOP so any advice is appreciated
class Foo:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
fo = Foo()
a = fo.add(1,2)
b = fo.subtract(1,3)
you don't need 2 instances of Foo to achieve this.
Just do something like this:
foo = Foo(a = 1, b = 2)
# Perform addition (now 'a' is 1 and 'b' is 2)
a = foo.add()
# Change 'b'
foo.b = 3
# Now perform subtraction (now 'a' is 1 and 'b' is 3)
b = foo.subtract()

Class variable vs instance variable

While learning python through python docs, i came across the following wherein its explained that class variable is common to the class and that any object can change it:
Sample Code 1:
class Dog:
tricks = [] # mistaken use of a class variable
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
Output:
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']
Question => If so, then why doesn't y in the following example get affected when x changes its tricks attribute to 5?
Sample Code 2:
class Complex:
tricks = 3
def __init__(self,var1):
self.tricks=var1
def add_tricks(self,var1):
self.tricks=var1
x = Complex(11)
y = Complex(12)
print (x.tricks)
print (y.tricks)
x.add_tricks(5)
print (x.tricks)
print (y.tricks) -->Remains unchanged
Output:
11
12
5
12 -->Remains unchanged
And what exactly is the difference when i remove the self in the following program:
Sample Code 3:
class Complex:
tricks = 3
def __init__(self,var1):
self.tricks=var1
def add_tricks(self,var1):
tricks=var1
x = Complex(11)
y = Complex(12)
print (x.tricks)
print (y.tricks)
x.add_tricks(5) -->This change is not reflected anywhere
print (x.tricks)
print (y.tricks)
print(Complex.tricks)
Output:
11
12
11
12
3
This example may be illustrative. Given the following class (I've dropped the initialiser from your example because it doesn't let us demonstrate the behaviour):
class Complex:
tricks = 3
def add_tricks(self, value):
self.tricks = value
We can see, upon creation, the value of their tricks attribute is both 3:
>>> a = Complex()
>>> b = Complex()
>>>
>>> a.tricks
3
>>> b.tricks
3
Let's take a second and look at the names defined on those objects:
>>> a.__dict__
{}
>>> b.__dict__
{}
They're both objects with no attributes themselves. Let's see what happens after we call add_tricks on b:
>>> b.add_tricks(5)
>>>
>>> a.tricks
3
>>> b.tricks
5
Okay. So, this looks like the shared value hasn't been affected. Let's take a look at their names again:
>>> a.__dict__
{}
>>> b.__dict__
{'tricks': 5}
And there it is. Assigning to self.tricks creates an attribute local to that object with name tricks, which when accessed via the object (or self) is the one that we'll use from that point forward.
The shared value is still there and unchanged:
>>> a.__class__.tricks
3
>>> b.__class__.tricks
3
It's just on the class, not on the object.

python object as argument of a function

I know that python pass object by reference, but why the second output of codes below is 3 other than 10?
class a():
def __init__(self, value):
self.value = value
def test(b):
b = a(10)
b = a(3)
print(b.value)
test(b)
print(b.value)
Python objects are passed by value, where the value is a reference. The line b = a(3) creates a new object and puts the label b on it. b is not the object, it's just a label which happens to be on the object. When you call test(b), you copy the label b and pass it into the function, making the function's local b (which shadows the global b) also a label on the same object. The two b labels are not tied to each other in any way - they simply happen to both be currently on the same object. So the line b = a(10) inside the function simply creates a new object and places the local b label onto it, leaving the global b exactly as it was.
You did not return a value from the function to put into the class. This means the 'b' is irrelevant and does nothing. The only connection is the name 'b'
You need to reassign the value 'b' to be able to call the class.
class a():
def __init__(self, value):
self.value = value
def test(b):
b = a(10)
return b
b = a(3)
print(b.value)
b = test(3)
print(b.value)

Attempting to create a read-only property attribute - getter returns initialized value, direct access returns changed value

Although I may be very confused as to what the property() function does, I'm trying to use it to create an attribute which is read-only. Ideally, I'd like to be able to refer to the attribute directly but not be allowed to assign to it. When experimenting, I got this very curious behavior:
>>> class Boo(object):
... def __init__(self, x):
... self.__x = x
... def getx(self):
... return self.__x
... x = property(getx)
...
>>> b = Boo(1)
>>> b.__x = 2
>>> b.getx()
1
>>> b.__x
2
I'd like to add that when I used x and _x as the attribute names, reassigning the attribute caused the getter to return the changed value, i.e. both b.getx() and b.x/b._x gave me 2.
I realize that I'm using x as the property name, though, but when I tried the following I got an AttributeError in my __init__():
>>> class Boo(object):
... def __init__(self, x):
... self.__x = x
... def getx(self):
... return self.__x
... __x = property(getx)
...
>>> b = Boo(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
AttributeError: can't set attribute
The problem here has nothing to do with property, but with double-underscore attributes, which are subject to what's called "Private name mangling".
>>> b = Boo(1)
>>> '__x' in dir(b)
False
>>> '_Boo__x' in dir(b)
True
So, when you do this:
>>> b.__x = 2
You're not changing the value of the attribute the getx function is looking at, you're creating a new attribute.
If you just use a name for the attribute that doesn't start with two underscores—such as _x—everything works as you intended.
As a general rule, use a single underscore for "advisory private"—as in, "users of this object probably shouldn't care about this value", and a double underscore only when you actually need mangling (because of complex inheritance issues that rarely come up).
What if you want "real private", like C++ or Java? You can't have it. If you hide or protect the attribute well enough, someone will just monkeypatch the getx method or the x property. So, Python doesn't give a way to hide attributes.
Your problem is that using double underscore attribute names mangles the name. So when you are dealing with __x inside of your class definition, outside of the class it actually looks like _Boo__x. That is,
_ + (class name) + (double underscore attribute name)
To demonstrate,
>>> b = Boo(1)
>>> b.__x = 2
>>> b.getx()
1
>>> b.x # NOTE: same as calling getx
1
>>> b.__x # why didn't x return 2 if we changed it?
2
>>> b._Boo__x # because it's actually saved in this attribute
1
>>> b._Boo__x = 3 # setting it here then works
>>> b.x
3
>>> b.getx()
3
Really just wanted to comment (rather than answer) your question. I think you will find the following informative:
>>> b = Boo(1)
>>> print b.__dict__
{'_Boo__X': 1}
>>> b.__x = 2
>>> print b.__dict__
{'__x': 2, '_Boo__X': 1}
Might provide a hint as to the behavior (which I do not understand sufficiently well to explain).
It's because you don't have a setx function defined.
#!/usr/bin/python
class Boo(object):
def __init__(self, initialize_x):
self.x = initialize_x
def getx(self):
print
print '\t\tgetx: returning the value of x', self.__x
print
return self.__x
def setx(self, new_x):
print
print '\t\tsetx: setting x to new value',new_x
print
self.__x = new_x
x = property(getx, setx)
print '1 ########'
print
print '\tinitializing Boo object with a default x of 20'
print
o = Boo(20)
print '\treading the value of x through property o.x'
t = o.x
print
print '2 ########'
print
print '\tsetting x\'s value through the property o.x'
o.x = 100
print
print '3 ########'
print
print '\treading the value of x through the property o.x'
t = o.x
When run produces:
1 ########
initializing Boo object with a default x of 20
setx: setting x to new value 20
reading the value of x through property o.x
getx: returning the value of x 20
2 ########
setting x's value through the property o.x
setx: setting x to new value 100
3 ########
reading the value of x through the property o.x
getx: returning the value of x 100
I use property as decorator. It convenient for calculating data.
May be for make read-only attribute better use magic function as __set__ and __get__?

Categories

Resources