Troubles managing attributes in python's class - python

I am a python beginner and it is my first time dealing with OOP. Could you help me to figure out what I am doing wrong whit the code below:
class Example(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.c = self.a + self.b/2
def property1(self):
self.obs1 = self.a - self.c
def property2(self):
self.obs2 = self.b - self.c
def get_obs1(self):
return self.obs1
def get_obs2(self):
return self.obs2
test = Example(2,5)
test.get_obs1()
I obtain the following exceptions:
Traceback (most recent call last):
File "/home/panadestein/Programming_Games/Python/buggy.py", line 22, in <module>
test.get_obs1()
File "/home/panadestein/Programming_Games/Python/buggy.py", line 16, in get_obs1
return self.obs1
AttributeError: 'Example' object has no attribute 'obs1'

As at when get_obs1 was called the attribute obs1 was not yet defined and not yet bounded to your class instance. Your question already gives a hint at using property, so use this instead and define your private variables in __init__:
class Example(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.c = self.a + self.b/2
self._obs1 = self.a - self.c
self._obs2 = self.b - self.c
#property
def obs1(self):
return self._obs1
#property
def obs2(self):
return self._obs2
test = Example(2,5)
test.obs1
Read more on #property

Related

How to instantiate multiple classes in one class: Python OOP

I've written a code with 3 different classes - those classes initializes functions and connected between each other (class B calls function a_calc() from A class; class C calls function b_calc() from B class). I want to create Calculator() function that instantiates all of those classes (A, B, C), uses class functions in one line of code, and returns the result of execution (to be able to execute the program creating only one object Main). Here the snippet of code (simplified):
class A(object):
def __init__(self):
self.a = 10
def a_calc(self):
a = self.a + 1
return a
class B(object):
def __init__(self, A):
self.A = A
def b_calc(self):
b = self.A.a_calc() + 2
return b
class C(object):
def __init__(self, B):
self.B = B
def c_calc(self):
c = self.B().b_calc + 3
return c
class Calculator(A, B, C):
def __init__(self):
result = A.a_calc() + B.b_calc() + C.c_calc()
print(result)
calc = Calculator()
Yet, I'm getting an error:
Traceback (most recent call last):
File "/home/taras/PycharmProjects/ResSysPymage/test#2.py", line 31, in <module>
calc = Calculator()
File "/home/taras/PycharmProjects/ResSysPymage/test#2.py", line 27, in __init__
result = A.a_calc() + B.b_calc() + C.c_calc()
TypeError: a_calc() missing 1 required positional argument: 'self'
How can I make my idea come true? I'd be grateful for ideas:)
You seem to be confusing classes, instances of classes and composition vs. multiple inheritance.
Perhaps you're looking for something like this:
class A:
def __init__(self):
self.a = 10
def a_calc(self):
a = self.a + 1
return a
class B:
def __init__(self, a: A):
self.a = a
def b_calc(self):
b = self.a.a_calc() + 2
return b
class C:
def __init__(self, b: B):
self.b = b
def c_calc(self):
c = self.b.b_calc() + 3
return c
def calculator():
a = A()
b = B(a)
c = C(b)
return a.a_calc() + b.b_calc() + c.c_calc()
print(calculator())

python class B inherit class A with all its methods but change one method only

Let's say class A has 10 methods. Some of the methods are private and it has private attributes as well. I want to create class B so I can change last method only without duplicating the code for the rest of the methods. My example is below. At the moment I am unable to achieve it with such inheritance as I get AttributeError: 'B' object has no attribute '_B__c'
class A:
def __init__(self, a=1, b=2):
self.a = a
self.b = b
self.__foo()
def __foo(self):
self.__c = self.a + self.b
def get_data(self):
return self.__c
class B(A):
def __init__(self, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
self.__c = self.__modify_data()
def __modify_data(self):
self.__c += 10000
def get_data(self):
return self.__c
b = B(a=5, b=10).get_data()
Question 2:
Can I achieve it with use of *args so I do not have to repeat all the arguments?
EDIT:
Please see my updated code above.
I believe private attributes causes the problem.
Can I solve it with still using private?
class A(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
self.__foo()
def __foo(self):
self._c = self.a + self.b
def get_data(self):
return self._c
class B(A):
def __init__(self, *args, **kwargs):
super(B, self).__init__(*args, **kwargs)
self.__modify_data()
def __modify_data(self):
self._c += 10000
b = B(a=5, b=10).get_data()
print(b)
Output:
10015
Changing _c to __c gives AttributeError: 'B' object has no attribute '_B__c'
Yes, the __ is causing the trouble by making variable c inaccessible in children, which is good because the private variable of parents should not be allowed to edit by the children class.

Class with read-only attributes

I want to create a class with attributes that can be __setattr__-ed by its methods internally, so an attempt like self.attr = value would raise an AttributeError. This is what I have so far:
class MyClass():
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def __repr__(self):
return '%r class with a=%s, b=%s, c=%s' % (self, self.a, self.b, self.c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
self.attr = value
>>> obj = MyClass(1,2,3)
>>> obj.setattr_(a,4) # obj.a = 4
AttributeError: 'obj' is read-only # __setattr__ method also applies internally
This is a use case for properties. Properties without a setter are read-only. In the following, a and b are read-only, while c is not.
class MyClass:
def __init__(self, a, b, c):
self._a = a
self.b = b
self._c = c
# a is a read-only property
#property
def a(self):
return self._a
# b is an ordinary attribute
# c is a property you can set
#property
def c(self):
return self._c
#c.setter
def c(self, value):
self._c = value
Since you have defined only getters for the a, attempts to
change its value will fail. Attempts to change b will succeed as expected. Attempts to change c will succeed as
if it were a regular attribute.
>>> obj = MyClass(1,2,3)
>>> obj.a = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> obj.b = 5
>>> obj.c = 6
>>> obj.c
6
You can use properties in Python for this type of tasks. First, you make your attribute 'private' by adding two underscores, then you create a getter method with the #property decorator:
class MyClass:
def __init__(self, a, b, c):
self.__a, self.__b, self.__c = a, b, c
#property
def a(self):
return self.__a
#property
def b(self):
return self.__b
#property
def c(self):
return self.__c
Now, you can use your class like this:
>>> my_object = MyClass('foo', 'bar', 'bar')
>>> print(my_object.b)
bar
>>> my_object.b = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Note
I wrote 'private' because you can still access it if you really want:
>>> my_object._MyClass__b = 42
>>> print(my_object.b)
42
This has to do with the Zen of Python: "We’re all consenting adults here".
Please use the properties.
Anyway, it is good to understand the internals, here is a working code based on your question. Just to play with.
Once you redefine __setattr__ to fail, there is no way to set an attribute in that class. But there is still a working __setattr__ left in the parent class.
class MyClass():
def __init__(self, a, b, c):
self.setattr_('a', a)
self.setattr_('b', b)
self.setattr_('c', c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
super().__setattr__(attr, value)
obj = MyClass(1,2,3)
obj.setattr_('a',4) # note that a is a name (string)

encapsulating class calls/modifies things in the dir of original class

I have a class A encapsulating a class B instance and additional stuff. The following is a toy example.
class B(object):
def __init__(self):
self.b = 2
def square(self):
return self.b * self.b
class A(object):
def __init__(self, x):
self.b = B()
a = A(1)
print(a.b.square())
Any time an A instance wants to call a method in B, I always need to do things like 'a.b'. My hope is to get rid of '.b' for user convenience. The following codes do the job.
class B(object):
def __init__(self):
self.b = 2
def square(self):
return self.b * self.b
class A(object):
def __init__(self, x):
self.b = B()
def square(self):
return self.b.square()
a = A(1)
print(a.square())
The problem is that class B is from outside library and there are lots of and different types of things in the dir. I couldn't do it one by one manually like above. Any magical ways to handle that?
Any magical ways to handle that?
It's python, of course there are! You can use __getattr__ function to proxy unknown calls to b:
class B(object):
def shadowed(self):
print('B.shadowed')
def unshadowed(self):
print('B.unshadowed')
class A(object):
def __init__(self):
self._b = B()
def shadowed(self):
print('A.shadowed')
def __getattr__(self, name):
return getattr(self._b, name)
test = A()
test.shadowed()
test.unshadowed()
test.unknown()
Result:
A.shadowed
B.unshadowed
Traceback (most recent call last):
File "/Users/Andrew/Desktop/test.py", line 23, in <module>
test.unknown()
File "/Users/Andrew/Desktop/test.py", line 17, in __getattr__
return getattr(self._b, name)
AttributeError: 'B' object has no attribute 'unknown'
__getattr__ is called when the object doesn't have attribute that's being asked for.

Multiple Inheritance in python with super()

My code:
class A():
def __init__(self, a = 100):
self.a = a
class B():
def __init__(self, b = 200):
self.b = b
class C(A,B):
def __init__(self, a, b, c = 300):
super().__init__(a)
super().__init__(b)
self.c = c
def output(self):
print(self.a)
print(self.b)
print(self.c)
def main():
c = C(1,2,3)`enter code here`
c.output()
main()
Error:
2
Traceback (most recent call last):
File "inheritance.py", line 25, in <module>
main()
File "inheritance.py", line 23, in main
c.output()
File "inheritance.py", line 17, in output
print(self.b)
AttributeError: 'C' object has no attribute 'b'
Why it cannot inheritance the b?
what's wrong with this code???
And how to modify this code?
If I replace the supper() with A. or B. , it can run normally.
So what cause this problem?
If I do not use super(), what method can I use?
Inherit from object + fixed your "super" call
class A(object):
def __init__(self, a = 100):
self.a = a
class B(object):
def __init__(self, b = 200):
self.b = b
class C(A,B):
def __init__(self, a, b, c = 300):
A.__init__(self, a=a)
B.__init__(self, b=b)
self.c = c
def output(self):
print(self.a)
print(self.b)
print(self.c)

Categories