Need help walking through logic of this code - python

I'm pretty new with Python and programming in general, so excuse the lack of "fu". :)
I'm having trouble understanding this class call:
snippet
class bar:
def __init__(self, a):
self.a = a
def __add__(self, b):
s = self.a + b.a
return s
end snippet
So, from the interpreter, if I issue:
x = bar(10)
y = bar(20)
z = x + y
print(z)
I get '30' back. That's correct.
I see how self.a is created, but I don't understand how b.a is getting created to do the addition.
Any guidance is appreciated.

When you call x + y it is actually translated to:
x.__add__(y)
Therefore, this method is called:
__add__(self, b) # __add__(x, y)
Which results in:
s = x.a + y.a # 30
return 30

In this code, b.a isn't being created, it is being accessed. You're basically passing in y as the argument b, which already has an a attribute associated with it since it is an object of type bar. If you want to step through your code go to http://www.pythontutor.com

x = bar(a) creates an object of the class bar with an a value of 'a'. Each bar object has a property/variable named a.
In x + y, the function add of x is called using y as the parameter.
So b = y, meaning b.a = y.a = 20.

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!

Using lambda to implement Singleton pattern

I'm trying to figure out why this code works, producing only a unique instance of class A.
def singleton_function (c):
print("singleton_function")
x = c()
return lambda : x
#singleton_function
class A:
pass
def test2 () :
x = A()
y = A()
assert x == y
test2()
Output:
python3 test.py
singleton_function
Why is singleton_function only accessed once? A is of type function, so my guess is that when calling A, A(), it returns x, which is the instance of class A, so, it should have printed two times "singleton_function", since I am accessing twice.
What am I thinking wrong?
Consider the decorator syntax:
def singleton_function (c):
print("singleton_function")
x = c()
return lambda : x
#singleton_function
class A:
pass
This is essentially saying that:
A = singleton_function(A)
And singleton_function() creates one instance of A and then returns a function that points to that instance.
So we get
A = lambda: x
Where x is an instance of the original class A. Calling A() returns x which is still that same instance that singleton_function() first created.

How to use inputs within the class?

I am new to python and object language. I am learning class in python now. but the code below confused me.
class math:
def __init__(self, a, b):
self.a = a
self.b = b
math.add = self.a+self.b
def sum(self):
math.sum = self.a+self.b
s= math(2,1)
x = math.add
y = math.sum
the results look not right. x is 3 but y is not 3. where is my problem?
thanks
I'm guessing what you wanted to do was this.
class math:
def __init__(self, a, b):
self.a = a
self.b = b
self.add = self.a+self.b
def sum(self):
return self.a+self.b
s= math(2,1)
x = s.add
y = s.sum()
print(x)
print(y)
The errors were as follows
You were not calling your instance of math. As these are instance methods, they must reference an instance x = math.add should be x = s.add.
You created a method sum but did not call it y = s.sum simply creates a reference to the function and does not call it, instead you should use y = s.sum()
Your function sum doesn't return anything, so assigning a value to its return value will yield a value of None. To resolve this return a value from sum i.e. return self.a + self.b.
self.add is also quite strange, as it named like a function, but its actually a value, this will not be expected by others looking at your code. A better name for it might be total or remove it since it provides the same functionality as sum.

Sharing scope in Python between called and calling functions

Is there a way in Python to manage scope so that variables in calling functions are visible within called functions? I want something like the following
z = 1
def a():
z = z * 2
print z
def b():
z = z + 1
print z
a()
print z
b()
I would like to get the following output
2
4
2
The real solution to this problem is just to pass z as a variable. I don't want to do this.
I have a large and complex codebase with users of various levels of expertise. They are currently trained to pass one variable through and now I have to add another. I do not trust them to consistently pass the second variable through all function calls so I'm looking for an alternative that maintains the interface. There is a decent chance that this isn't possible.
I think this is what you're looking for. If you control the inner functions, and the variable you're looking for is already in scope somewhere in the call chain, but you don't want your users to have to include the variable in the call itself. You clearly don't want the global keyword, because you don't want inner assignments to affect the outer scope.
Using the inspect module gets you access to the calling context. (But, be warned that it's somewhat fragile. You should probably use only for CPython unthreaded.)
import inspect
def calling_scope_variable(name):
frame = inspect.stack()[1][0]
while name not in frame.f_locals:
frame = frame.f_back
if frame is None:
return None
return frame.f_locals[name]
z = 1
def a():
z = calling_scope_variable('z')
z = z * 2
print z
def b():
z = calling_scope_variable('z')
z = z + 1
print z
a()
print z
b()
This does give the right answer of:
2
4
2
This might be the right place for a class:
class MyClass(object):
def __init__(self, z):
self.z = 1
def a(self):
self.z = self.z * 2
print self.z
def b():
self.z = self.z + 1
print self.z
self.a()
print self.z
You can get the same effect with globals but I'd suggest creating your own separate namespace (such as with a class) and using that for your scope.
This is a bit ugly, but it avoids using globals, and I tested it and it works. Using the code from the selected answer found here, the function getSetZ() has a "static" variable that can be used to store a value that is passed to it, and then retrieved when the function is called with None as the parameter. Certain restrictions are that it assumes None is not a possible value for z, and that you don't use threads. You just have to remember to call getSetZ() right before each call to a function that you want the calling function's z to be available in, and to get the value out of getSetZ() and put it in a local variable in that function.
def getSetZ(newZ):
if newZ is not None:
getSetZ.z = newZ
else:
return getSetZ.z
def a():
z = getSetZ(None)
z = z * 2
print z
def b():
z = getSetZ(None)
z = z + 1
print z
getSetZ(z)
a()
print z
getSetZ.z = 0
getSetZ(1)
b()
I hope this helps.
I don't quite understand what you're trying to achieve, but this prints 2, 4, 2:
z = 1
def a():
global z
y = z * 2
print y
def b():
global z
z = z + 1
print z
a()
print z
b()
In general, the best solution is always just to pass the values around - this will be much more predictable and natural, easier to work with and debug:
def a(z):
z = z * 2
print z
def b():
global z
z = z + 1
print z
a(z)
print z
b()
You could define a() in b() and do something similar:
def b():
global z
z = z + 1
print z
def a():
z = z * 2
print z
a()
print z
However, this isn't really optimal.

unbound method when using type builtin to create a dynamic object

I have this code:
class X(object):
x = 10
def test_x(self):
return self.x
class Y(X):
def test_y(self):
return self.x
y = Y()
y.test_y() # works fine
But when I construct a new object z based on X using type :
z = type('Z', (X,), dict(z=1))
z.x # works fine
z.test_x() # gives a TypeError :
unbound method test_x() must be called with Z instance as first argument (got nothing instead).
How can I solve it.
UPDATE
With the help and understanding of Martijn, this is how I solved it:
z = type('Z', (X,), dict(z=1))()
z.test_x()
z is a class, not an instance. Create an instance instead:
z().test_x()
What you did was the equivalent of:
class Z(X):
z = 1
z = Z
z.test_x()

Categories

Resources