OOP: Init method questions - python

I have two questions regarding the code below.
What is the difference between self.a=self.test1() and a=self.test1()? One is class field and other one is object field?
Why cannot I define result = self.a+self.b? How to correct it?
class Test():
def __init__(self):
self.a=self.test1()
a=self.test1()
self.b=Test.test2()
result = self.a+self.b
def test1(self):
a=100
return a
#classmethod
def test2(cls):
b=200
return b
#staticmethod
def test3():
print("Testing3 is calling ")
c=500
return c

self.a = self.test1() creates an instance attribute named a. The attribute will be accessible from the object anywhere you have a reference to the object.
a = self.test1() defines a local variable named a. It will go out of scope once __init__ returns.
result = self.a + self.b doesn't work because it is in a context where self is not defined. self is just the (conventional) name of the first parameter of an instance method. It's not defined in the namespace of the class statement itself.

self.a is a property in this class. It will remain accessible throughout functions in the Test() class. a = self.test1(), however, goes away once __init__(self) finishes, because a is local to __init__(self).
For result = self.a + self.b, I assume you want a variable called result calculated after self.a and self.b is defined? At that indentation level a statement like this is usually not allowed (I could be wrong), usually a declaration of a property of a class happens here.

Related

How to access a variable from constructor outside of method in python

class A():
B: str = "no"
class test(A):
a = None
  def __init__(self, val):
      self.a = val
  if self.a == "test":
B = "yes"
t = test("test")
print(t.B)
NameError: name 'self' is not defined
self.a shows an error so how can I access a which was assigned a value in the constractor out side of a method inside the class?
Because you are making class object u need to define the object name used
class test():
def __init__(self):
You want to access your attribute, you cant access outside of method so
class test():
def __init__(self):
self.a = "test"
if self.a == "test":
pass
or you can create another method that process that
class test():
def __init__(self):
self.a = "test"
self.access_a()
def access_a(self):
if self.a == "test":
print("do something")
pass
What you are asking for (given that your example defines an instance attribute) is quite frankly not possible, you can't access an instance attribute without referencing that instance and you can't reference an instance of a class in its body.
What could be done is changing the class attribute from the constructor but that would be pointless because all code in the body of the class gets executed first so such a check (as in your provided sample) would be pointless anyways.

how to know from which class instance a function is called to access the instance attributes

I want to access an attribute of the class instance that called a function :
for example:
class A:
def a(self):
return B.q
class B:
q=0
def b(self):
M=A()
return M.a()
c=B()
c.q = 6
print(c.b())
the output will be 0 but I want it to print the q attribute of the instance c of the class B which has the value 6
Pass the instance as a parameter.
class A:
def a(self, b):
return b.q
class B:
q=0
def b(self):
M=A()
return M.a(self)
c=B()
c.q = 6
print(c.b())
This appears to be very bad program design. What are you trying to accomplish with this?
You have a class attribute and an instance attribute -- in that class -- of the same name, q. This makes your code difficult to follow and to maintain.
You have method B.b instantiate an instance of class A. You immediately call A.a, which has been assigned the questionable task of returning an instance attribute from and object of class B.
Clean up your design.
Use init appropriately for each class.
Design your class methods to work appropriately with the characteristics of instances of that class. Your question strongly suggests that your design is not yet clean in your mind, nor in code.
define an init method so that you can work with the instance attributes instead of the class variable
class A:
def a(self):
return B.q
class B:
def __init__(self):
self.q = 0
def b(self):
M=A()
return M.a()
c=B()
c.q = 6
print(c.b())

Calling a variable from a classmethod function to a normal method

I would like to call a variable from a classmethod to a different method inside the same class:
class A():
#classmethod
def b(cls):
cls.g = 5
def c(self):
if self.g < 1:
print("TestA")
else:
print("TestB")
When doing:
x = A()
x.c()
I get:
AttributeError: 'A' object has no attribute 'g'
I've read and searched for a similar case but haven't found one. Most deal with calling variables from the init method and that doesn't apply here.
If you don't run .b() beforehand, your .g doesn't exist,...at all.
Add an __init__ function to your class and declare .g there to make sure it exists at least.
You did not define g as a class attribute of the class A. This could be done this way:
class A():
g = 7
but then in your code you are treating g as instance (self.g) and class variable (cls.g) at the same time. While this works (self.g will refer to cls.g) it may be confusing.

Python - method of a class with an optional argument and default value a class member

I have something like this (I know this code doesn't work, but it's the closer to what I want to achieve):
class A:
def __init__(self):
self.a = 'a'
def method(self, a=self.a):
print a
myClass = A()
myClass.method('b') # print b
myClass.method() # print a
What I've done so far, but I do not like it, is:
class A:
def __init__(self):
self.a = 'a'
def method(self, a=None):
if a is None:
a = self.a
print a
myClass = A()
myClass.method('b') # print b
myClass.method() # print a
Default arguments are evaluated at definition time. By the time the class and method are defined self.a is not.
Your working code example is actually the only clean way of achieving this behavior.
The default is evaluated at method definition time, i.e. when the interpreter executes the class body, which usually happens only once. Assigning a dynamic value as default can only happen within the method body, and the approach you use is perfectly fine.

Confused about behaviour of base class

This follows a question I asked a few hours ago.
I have this code:
class A(object):
def __init__(self, a):
print 'A called.'
self.a = a
class B(A):
def __init__(self, b, a):
print 'B called.'
x = B(1, 2)
print x.a
This gives the error: AttributeError: 'B' object has no attribute 'a', as expected. I can fix this by calling super(B, self).__init__(a).
However, I have this code:
class A(object):
def __init__(self, a):
print 'A called.'
self.a = a
class B(A):
def __init__(self, b, a):
print 'B called.'
print a
x = B(1, 2)
Whose output is:
B called.
2
Why does this work? And more importantly, how does it work when I have not initialized the base class? Also, notice that it does not call the initializer of A. Is it because when I do a:
def __init__(self, b, a)
I am declaring b to be an attribute of B? If yes, how can I check b is an attribute of which class - the subclass or the superclass?
The way you defined it B does not have any attributes. When you do print a the a refers to the local variable a in the __init__ method, not to any attribute.
If you replace print a with print self.a you will get the same error message as before.
In your second code, calling print a works because you are printing the variable passed as a parameter to B's __init__ function, not the self.a variable (which is not initialized because A.__init__ was not called). Once you leave the scope of the __init__ function you will loose access to the a.
The short answer is that __init__ isn't a constructor in the sense of other languages, like C++. It's really just a function that runs on an object after the "bare" object is created. B's __init__ function is conventionally responsible for calling A's __init__, but it doesn't have to - if A.__init__ never gets called, then then A's attribute a doesn't get added to the object, and so you get an error when you try to access it.
To fix this problem, you'll need to use
class B(A):
def __init__(self, b, a):
super(A, self).__init__(a)
Make sure you declare the classes as new style objects, something you're already doing
[edit: clarification]

Categories