Better way to handle inheritance in constructors - python

I have the following BaseClass
class A(object):
__metaclass__ = abc.ABCMeta
def __init__(self, val1, val2):
self.v1 = val1
self.v2 = val2
and then some extended class:
class B(A):
def __init__(self, *args, **kwargs):
super(self.__class, self).__init(*args[:len(args)-1], **kwargs
self.v3 = args[len(args)]
basically i want to call it in a way such that:
x = B(1, 2, 34)
but this seems that i need to have a specific order, how do implement init the right way so that the base class can initialize its v1,v2 variables and the extended class B can initialize the v3 value (in this case with 34).

You should be explicit about the required arguments for the super class A.__init__. If you provide < 2 args to B then you would get an error.
class B(A):
def __init__(self, val1, val2, *args):
super(self.__class, self).__init__(val1, val2)
self.v3 = args # or args.pop() or whatever

Related

How do I pass the same variable(iterable) between instances of different classes(that inherits from the same parent)

I want to pass a variable(iterable )between instances of different classes. I have a structure similar with the one below.
Each class has its own module(so no globals) and needs to work in python 3 and 2.
class O:
pass
class A(O):
pass
class B(O):
def __init__(self, cache):
self.cache = cache
class B1(B):
def p(self):
self.cache.add_to_cache("32", "something something")
class B2(B):
def p(self):
self.cache.get_from_cache("52", "something else")
For B and its sub-classes I want to create a cache. All instances of this classes(B, B1, B2) to use the same cache.
To keep it simple, let's say that the cache is just a dict.
c = {}
a = A(c)
b1 = B() - needs c
b1.p()
b2 = C() - needs c
b2.p()
print(cache)
Off course the example above, is wrong because the cache is different for each instance.
The chache should be :
{
"32", "something something"
"52": "something else"
}
Another approach to this is using CacheService as an injectable Singleton service, which I consider a better practice.
Read this first for a code/syntax solution to your direct question, or continue reading for a solution with better design.
class O(object):
pass
class CacheService(object):
__instances = {}
#staticmethod
def getinstance(owner_id):
if owner_id not in CacheService.__instances:
CacheService.__instances[owner_id] = CacheService(owner_id)
return CacheService.__instances[owner_id]
def __init__(self, owner_id):
self._owner_id = owner_id
self._owner_query = CacheService.__name__ + self._owner_id
self._cache = {}
def put_in_cache(self, key, value):
self._cache[self._owner_query + str(key)] = value
def get_from_cache(self, key):
return self._cache.get(self._owner_query + str(key), "the_default")
class B(O):
def __init__(self):
self._cache = CacheService.getinstance(B.__name__)
class B1(B):
def __init__(self):
super(B1, self).__init__()
def p(self):
val1 = self._cache.get_from_cache("a")
print(val1)
class B2(B):
def __init__(self):
super(B2, self).__init__()
def p(self):
self._cache.put_in_cache("a", 2)
if __name__ == "__main__":
b1 = B1()
b2 = B2()
b2.p()
b1.p()
out:
2
This still uses a class variable, but hides it from your "everyday code", and moves it to the "infrastructure level".
I see this as cleaner, as now your class hierarchy shouldn't handle its own global variables.
To directly answer the programming question, Use class variables.
As a side note, it would be much better to use some kind of "CacheService" and inject that to the constructor, rather than use inheritance and class variables.
For this, see my other answer.
Code for using class variables follows:
class O(object):
pass
class B(O):
__cache = {} # use your cache class if you want, I am using dict just for show
def __init__(self):
pass
def _get_from_cache(self, key):
return self._cache.get(key, "default1")
def _put_in_cache(self, key, value):
self._cache[key] = value
class B1(B):
def __init__(self):
super(B1, self).__init__()
def p(self):
val1 = self._get_from_cache("a")
print(val1)
class B2(B):
def __init__(self):
super(B2, self).__init__()
def p(self):
self._put_in_cache("a", 2)
if __name__ == "__main__":
b1 = B1()
b2 = B2()
b2.p()
b1.p()
out:
2
Notice _get_from_cache and _put_in_cache are methods, but they can be #staticmethods, as they only ever access class variables, and their self isn't "really" ever being used. __cache could theoretically be accessed directly by children, but the _get_from_cache and _put_in_cache makes __cache private, and gives a protected API to it.

how to call just one argument in the secondary function in python

i have the following code:
class A:
def __init__(self, name,val):
self.name = name
self.val = val
class B(A):
super(A)__init__ // here i want to have just a variable self.name. that means val=0
def outputName(self):
print(f"name = {self.name}")
class C(A):
super(A)__init__ // here i want to habe just a variable self.val. that means name = 0
def outputVal(self):
print(f"val= {self.val}")
n = B('martin')
v = C(25)
outputName(n)
outputVal(v)
My Question: How can i specify that i just want name in class B in val in class C?
an another solution that i know is e.g. n=B('Martin',0) and v =C(0,25) but i think it is nonesense
Any idea how i can right it better?
Here you just need to feed a specific variable to super(). Minor notes: since you have a single inheritance, you don't need to specify which class you're instantiating when calling super(). Also, don't forget the "dot" in super().__init__() and, most importantly, don't forget to define __init__() in subclasses:
class A:
def __init__(self, name, val):
self.name = name
self.val = val
class B(A):
def __init__(self, name):
super().__init__(name=name, val=None) # Only initialise name variable of the base class
def outputName(self):
print(f"name = {self.name}")
class C(A):
def __init__(self, val):
super().__init__(name=None, val=val) # Same here but with val instead.
def outputVal(self):
print(f"val= {self.val}")
Or, alternatively, as Tim has suggested you can make class A have default values in init:
class A:
def __init__(self, val=None, name=None):

Access all variables by their names of specific object in class

I have two python classes:
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
Now I have an instance of B and need to access param1, I can just write b.a.param1. But the goal is to omit the 'a' part, so access this param with b.param1. I could add property to class B, but I am searching for generic solution - when A class has a lot variables. Is it possible? And would it be clean solution?
This is not the most elegant option and probably a bad practice but you can copy all the attributes of a to be attributes of b using getattr and setattr:
import inspect
class A:
def __init__(self, param1):
self.param1 = param1
class B:
def __init__(self, a):
self.a = a
variables = [i for i in dir(a) if not inspect.ismethod(i) and i[:2] != '__']
for var in variables:
setattr(self, var, getattr(a, var))
This way you can access a's attributes directly:
a = A(1)
b = B(a)
b.param1
which will return
1

How to inherit every class in python?

I'm working with classes that have a lot of instance variables, and I want to have classes that inherit every instance variables from them. something like this:
class foo(object):
def __init__(self,thing1,thing2,thing3,thing4,thing5,thingetc):
self.1 = thing1
self.2 = thing2
self.3 = thing3
self.4 = thing4
self.5 = thing5
self.etc = thingetc
class bar(foo):
self.6 = []
a = bar
print a.3
obviously this won't work, but all the documentation that I can find on line is confusing. How do you inherit variables in cases like this?
Currently, your code is invalid syntax as a digit cannot be at the very front of a variable name. However, you can use *args with __dict__:
class foo:
def __init__(self, *args):
self.__dict__ = dict(zip(['var{}'.format(i) for i in range(1, len(args)+1)], args))
f = foo(*range(15))
print(f.var1)
print(f.var14)
Output:
0
13
Use this as a template for your inheritance, emphasis on the super() method:
class Foo:
def __init__(self):
self.name = 'Foo'
class Bar(Foo):
def __init__(self):
super().__init__()
b = Bar()
b.name
# outputs 'Foo'
For your specific type of class (that takes an unknown number of initialization arguments, i.e. *args):
class Foo:
def __init__(self, *args):
self.name = 'Foo'
for i, arg in enumerate(args):
setattr(self, 'thing_' + str(i), arg)
class Bar(Foo):
def __init__(self, *args):
super().__init__(*args)
b = Bar('hello', 'world')
b.name
# outputs 'Foo'
b.thing_0
# outputs 'hello'
b.thing_1
# outputs 'world'
Now I would personally use the **kwargs over *args for specifying unique instance attributes:
class Foo:
def __init__(self, **kwargs):
self.name = 'Foo'
for att in kwargs:
setattr(self, att, kwargs[att])
class Bar(Foo):
def __init__(self, **kwargs):
super().__init__(**kwargs)
b = Bar(value = 4, area = 3.14)
b.name
# outputs 'Foo'
b.value
# outputs 4
b.area
# outputs 3.14

AssertListEqual without changing class __eq__ method

Let's say I have the following code
class MyClass(object):
def __init__(self, val1, val2):
self.attr1 = val1
self.attr2 = val2
class MySecondClass(object):
def __init__(self):
self.my_list = []
def add_to_list(self, val1, val2):
self.my_list.append(MyClass(val1, val2))
and the following unit-testing code
import unittest
def myclass_equality(inst1, inst2, msg=None):
val1_matches = inst1.val1 == inst2.val1
val2_matches = inst1.val2 == inst2.val2
if not val1_matches or not val2_matches:
raise unittest.TestCase.failureException(msg)
class ListTests(unittest.TestCase):
def __init__(self, *args, **kwargs):
super(ListTests, self).__init__(*args, **kwargs)
self.addTypeEqualityFunc(MyClass, myclass_equality)
def setUp(self):
self.second = MySecondClass()
def test_adding_to_list(self):
val1, val2 = 1, 2
self.second.add_to_list(val1, val2)
my_other_list = [(MyClass(val1, val2))]
self.assertListEqual(self.second.my_list, my_other_list)
However, the assertion fails as the assertListEqual method compares objects with '==' as can be seen here and not with the method I have defined, which is called if I call assertEqual on two MyClass instances.
Are there any workarounds? Because I don't need and want to define __eq__ method for MyClass.
And why assertListEqual compares with '==' and not assertEqual?
Can you just do it in a loop or create another loop wrapper for your myclass_equality function? I feel like I am missing something.
E.g.:
def test_adding_to_list(self):
val1, val2 = 1, 2
self.second.add_to_list(val1, val2)
my_other_list = [(MyClass(val1, val2))]
for i, (a, b) in enumerate(zip(self.second.my_list, my_other_list)):
myclass_equality(a, b, msg='Adding to list failed on element {}:{}!={}'.format(i, a, b)

Categories