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

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.

Related

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)

Python: Getting python class has no attribute

Getting below Attribute error while running below code. Class member function is accessed using self still it is giving error.
class A:
def __init__(self):
self.a=1
def f1(self):
self.b=2
def f2(self):
self.c=3
print(self.a,self.b,self.c)
self.f2()
model = A()
model.f1()
Error:
AttributeError: 'A' object has no attribute 'f2'
A does not have an attribute f2, but it does have an attribute f1 that calls a function f2. f2 is not an instance attribute.
In this case c is no longer an instance attribute, or data attribute, it is now just a local variable. This may or may not be what you were
going for.
class D:
def __init__(self):
self.a = 1
def f1(self):
# Instance attribute declared outside of __init__
self.b = 2
def f2():
c = 3
print(self.a, self.b, c)
f2()
Depending on your development environment you may or may not get a warning about instance attributes being declare outside of the __init__ function. It isn't necessary that you declare them this way, however, it improves readability.
class B:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
def f1(self):
def f2():
print(self.a, self.b, self.c)
f2()
This next block is slightly more explicit in what it says about the intent of the code.
class A:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
def f1(self):
def f2():
return '{} {} {}'.format(self.a, self.b, self.c)
return f2()
Perhaps you want f2() to be called using method attributes of the
self argument in which case:
class A2:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
def f2(self):
print(self.a, self.b, self.c)
def f1(self):
"""Do something that requires calling the self.f2()
print function.
"""
self.f2()
You do not need self when calling the nested function and nor does it need to be contained in the function signature:
class A:
def __init__(self):
self.a=1
def f1(self):
self.b=2
def f2():
c=3
print(self.a,self.b, c)
f2()
model = A()
model.f1()
Output:
1, 2, 3
Its not clear to me that you actually need to define a function within a method just to call it.
So, your code could just as easily be this:
class A:
def __init__(self):
self.a = 1
def f1(self):
self.b = 2
self.f2()
def f2(self):
self.c = 3
print(self.a, self.b, self.c)
model = A()
model.f1()
Output:
1 2 3

How the below program is possible in python

I came with a situation where the method of class A to be called from class B.
class A(object):
def __init__(self, a):
self.a = a
def abc(self):
print self.a
class B(A):
def __init__(self):
super(B, self).abc()
def method1():
a = A(2)
method1()
b = B()
Expecting Output: 2
Is it possible to call method 'abc' from class B with changing class A and should not create class A object in class B. If yes, then please let me know the solution.
The above program which I tried is giving error.
And the error I am getting is below
Traceback (most recent call last):
File "a.py", line 12, in <module>
b = B()
File "a.py", line 10, in __init__
super(B, self).abc()
File "a.py", line 6, in abc
print self.a
AttributeError: 'B' object has no attribute 'a'
Your B class __init__ method is not taking any argument, while the __init__ of class A require you to pass one (named "a"), and yet, you are not providing it. Neither in your B class or by passing it to A.
However, this would work.
class A(object):
def __init__(self, a):
self.a = a
def abc(self):
print self.a
class B(A):
def __init__(self):
self.a = 10
super(B, self).abc()
Or:
class B(A):
def __init__(self):
super(B, self).__init__(10)
inst = B()
inst.abc() # 10
Here:
class B(A):
def __init__(self):
super(B, self).abc()
The constructor of A is never called, so the initialization done in A.__init__ is missing. It fails in print self.a, because there is no a.
The super constructor should be called.
Furthermore, super(B, self).abc() is the same as self.abc().
If there was a method named abc defined in B, then self.abc() would call the method from B, whereas super(B, self).abc() would call the method from the superclass.
So, since those are the same, I would not use the ugly one. It just makes the code less readable.
With those two fixes:
class B(A):
def __init__(self):
super(B, self).__init__(1000) # whatever default value
self.abc()

Troubles managing attributes in python's class

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

Python: nested class with static method fails

What is wrong with the following code?
class A:
def A_M(self): pass
class B:
#staticmethod
def C(): super(B).A_M()
error (Python 2.7.3):
>>> a = A()
>>> a.B.C()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "..x.py", line 36, in C
def C(): super(B).A_M()
NameError: global name 'B' is not defined
Edit:
the solution was simple as this:
class A:
def A_M(self): pass
class B:
#staticmethod
def C(): A().A_M() #use of A() instead of supper, etc.
Important Note that there is an issue with this solution. If you change the name of super class (i.e. A) then you will have to update all uses inside itself as A :)).
class A(object):
def foo(self):
print('foo')
#staticmethod
def bar():
print('bar')
class B(object):
#staticmethod
def bar(obj):
# A.foo is not staticmethod, you can't use A.foo(),
# you need an instance.
# You also can't use super here to get A,
# because B is not subclass of A.
obj.foo()
A.foo(obj) # the same as obj.foo()
# A.bar is static, you can use it without an object.
A.bar()
class B(A):
def foo(self):
# Again, B.foo shouldn't be a staticmethod, because A.foo isn't.
super(B, self).foo()
#staticmethod
def bar():
# You have to use super(type, type) if you don't have an instance.
super(B, B).bar()
a, b = A(), B()
a.B.bar(a)
b.foo()
B.bar()
See this for details on super(B, B).
You need to use a fully-qualified name. Also, in python 2.7, you need to use (object), else super(A.B) will give TypeError: must be type, not classobj
class A(object):
def A_M(self):
pass
class B(object):
#staticmethod
def C():
super(A.B).A_M()
Finally, super(A.B) is essentially object here. Did you mean for B to inherit from A? Or were you simply looking for A.A_M()?
A latecommer, to just encapsulate B in A the easy way is this:
class A:
def A_M(self):
return "hi"
class B:
#staticmethod
def C():
return A().A_M()
a = A()
print a.B().C()
Not sure this is what you need, but the question was still unsolved, so I guessed.

Categories