Creating a child class from a parent method in python - python-3.x

I am trying to make a class that has a bunch of children that all have their own respective methods but share common methods through the parent. The problem is I need to create an instance of the child class in the parent method but am not sure how to go about it
my code so far looks like this
def filterAttribute(self, attribute, value):
newlist = []
for thing in self._things:
if thing._attributes[attribute] == value:
newlist.append(thing)
return self.__init__(newlist)
the class constructor takes in a list as its sole argument. Does anyone know if there is a standard way of doing this because my code is returning a NoneType object
Here are a few examples of classes I have made
This is the parent class:
class _DataGroup(object):
def __init__(self, things=None):
self._things=things
def __iter__(self):
for x in self._things:
yield x
def __getitem__(self, key):
return self._things[key]
def __len__(self):
return len(self._things)
def extend(self, datagroup):
if(isinstance(datagroup, self.__class__)):
self._things.extend(datagroup._things)
self._things = list(set(self._things))
def filterAttribute(self, attribute, value):
newlist = []
for thing in self._things:
if thing._attributes[attribute] == value:
newlist.append(thing)
#return self.__init__(newlist)
return self.__init__(newlist)
this is one of the child classes
class _AuthorGroup(_DataGroup):
def __init__(self, things=None):
self._things = things
def getIDs(self):
return [x.id for x in self._things]
def getNames(self):
return [x.name for x in self._things]
def getWDs(self):
return [x.wd for x in self._things]
def getUrns(self):
return [x.urn for x in self._things]
def filterNames(self, names, incl_none=False):
newlist = []
for thing in self._things:
if((thing is not None or (thing is None and incl_none)) and thing.name in names):
newlist.append(thing)
return _AuthorGroup(newlist)
The functionality I am looking for is that I can use the parent class's with the child classes and create instances of the child classes instead of the overall DataGroup parent class

So if I correctly understand what you are trying to accomplish:
You want a Base Class 'DataGroup' which has a set of defined attributes and methods;
You want one or mpore child classes with the ability to inherit both methods and attributes from the base class as well as have the ability to over-ride base class methjods if necessary: and
You want to invoke the child class without also having to manually invoke the base class.
If this in fact is your problem, this is how I would proceed:
Note: I have modified several functions, since I think you have several other issues with your code, for example in the base class self._things is set up as a list, but in the functions get_item and filterAttribute you are assuming self._things is a dictionary structure. I have modified the functions so all assume a dict structure for self._things
class _DataGroup:
def __init__(self, things=None):
if things == None:
self._things = dict() #Sets up default empty dict
else:
self._things=things
def __iter__(self):
for x in self._things.keys():
yield x
def __len__(self):
return len(self._things)
def extend(self, datagroup):
for k, v in datagroup.items():
nv = self._things.pop(k, [])
nv.append(v)
self._things[k] = nv
# This class utilizes the methods and attributes of DataGroup
# and adds new methods, unique to the child class
class AttributeGroup(_DataGroup):
def __init__(self, things=None):
super.__init__(things)
def getIDs(self):
return [x for x in self._things]
def getNames(self):
return [x.name for x in self._things]
def getWDs(self):
return [x.wd for x in self._things]
def getUrns(self):
return [x.urn for x in self._things]
# This class over-rides a DataGroup method and adds new attribute
class NewChild(_DataGroup):
def __init__(self, newAttrib, things = None):
self._newattrib = newAttrib
super.__init__(self, things)
def __len__(self):
return max(len(self._newattrib), len(self._things))
These examples are simplified, since I am not absolutely sure of what you really want.

Related

How to overload python sets to accept duplicates?

I had taken a python coding test, which asked to create a class that overloads the builtin set(all the methods of sets must work). The only change between the set I was asked to create and the builtin sets is that my custom sets SHOULD store duplicates, and 2 more custom methods.
Here is what I could come up with:
import builtins
class Multiset(builtins.set):
def __init__(self):
super().__init__()
self.my_set = builtins.set()
def add(self, val):
self.my_set.add(val)
def remove(self, val):
# removes one occurrence of val from the multiset, if any
self.my_set.discard(val)
def __contains__(self, val):
# returns True when val is in the multiset, else returns False
return val in self.my_set
def __len__(self):
# returns the number of elements in the multiset
return len(self.my_set)
I have tried overriding multiple methods, but to no avail. I also couldn't find a method that defined this non-duplicate criteria for sets. So, how do I do this?
EDIT 1:
Here is the problem description, if you want to see it.
You can use a dictionary which maps objects to a list of all objects that are equal to themselves. The advantage is that dict keys are already set-like.
from collections import defaultdict
class MultiSet:
def __init__(self):
self._items = defaultdict(list)
def add(self, item):
self._items[item].append(item)
def remove(self, item):
try:
self._items[item].remove(item)
except ValueError:
pass
def __contains__(self, item):
return item in self._items
def __len__(self):
return sum(len(v) for v in self._items.values())

Is it posible to use super() with all the parents in Python3 multiple inheritance?

If a class has 2 or more parents, how can I use super(), or any equivalent, to make reference to each of them? For example here:
class A:
def __init__(self, x): self.a = x
class B:
def __init__(self, y): self.b = y
class C(A,B):
def __init__(self, x, y):
super().__init__(x)
B.__init__(self,y) # I would to like to use super() here too
ObjetoC = C(4,3);
print (ObjetoC.a, ObjetoC.b) # It works fine
PD: I understand the MROrder. I just wonder if there is a way to reach a non-priority parent with super() or equivalent. Or if there is another elegant way to do that I have already done by using B.__init__(self,y)

Create a list that can be accessed from multiple classes

I'm trying to create a list that is populated in one class and read in another class. I have a number of things, right now I'm getting the error shown below. I show only one class where the list is read, but there will be others.
How do I do this?
ReadDatabase.py
class ReadDatabase(object):
f_type_list = ReadDatabase.getFTypes() ## 'ReadDatabase' not defined
#staticmethod
def getFTypes():
<reads from database>
return my_list
MyTreeView.py
from ReadDatabase import *
class MyTreeView (ttk.Treeview):
def __init__(self, frame=None, list=[], column_headers=[]):
for f_type in ReadDatabase.f_type_list:
<do stuff>
You can separate it into two different classes in two different ways.
Example 1: Using two classes, one class (A) creates & updates the list and the other class (B) creates an instance of A and controls it:
class A:
"""
Create and updates the list
"""
def __init__(self):
self.my_list = []
self._read_from_database()
def _read_from_database(self):
# some database update logic
self.my_list.append(3)
class B:
"""
Creates an instance of A and can read from it.
"""
def __init__(self):
self.a = A()
def print_list(self):
for index, element in enumerate(self.a.my_list):
print(f"Index: {index} Element: {element}")
b_object = B()
b_object.print_list() # Prints: Index: 0 Element: 3
or
Example 2: You can just create a method in class B and just pass it the lst from class A:
class A:
"""
Create and updates the list
"""
def __init__(self):
self.my_list = []
self._read_from_database()
def _read_from_database(self):
# some database update logic
self.my_list.append(3)
class B:
def __init__(self):
pass
def print_list(self, lst):
for index, element in enumerate(lst):
print(f"Index: {index} Element: {element}")
a_object = A()
b_object = B()
b_object.print_list(a_object.my_list)
You can also pass the entire instance of A to B for it to use if you wanted to do it that way.

Python overriding default attribute assignment

for a specific framework i work with, i need to define object attributes as special classes, for example, instead of writing this:
class A:
def __init__(self):
self.some_int = 2
i would need to write:
class A:
def __init__(self):
self.some_int = SpecialIntWrapper(name = "some_int", value = 2)
I would like to somehow override operators/methods so that typing the first code (self.some_int = 2) will call SpecialIntWrapper behind the scenes, with the attribute name and value.
is this possible?
Basically there are two ways - via a #property decorator (preferable unless you want to affect arbitrary names)
class MyClass:
def __init__(self):
self.some_int = 2
# if you know the name of the property define it as a property - a getter
#property
def some_int(self):
return self._some_int
# and a setter
#some_int.setter
def some_int(self, value):
self._some_int = SpecialIntWrapper("some_int", value)
or overloading the __setattr__ magic method
class MyClass:
def __init__(self):
self.some_int = 2
def __setattr__(self, name, value):
# in general if you dont know the names of the properties
# beforehand you can somehow filter them here
if name == "some_int":
super().__setattr__(name, SpecialIntWrapper(name=name, value=value))
else:
# to use the setattr in a default way, just call it via super(Python 3)
super().__setattr__(name, value)
either way the some_int will be initialized to the SpecialIntWrapper instance
>>>print(MyClass().some_int)
<__main__.SpecialIntWrapper object at 0x03721810>
Something like this
class SpecialIntWrapper:
def __init__(self, name, value):
pass
class MyClass:
def __init__(self):
self.some_int = 3
def __setattr__(self, key, value):
if key == 'some_int':
self.__dict__[key] = SpecialIntWrapper(key, value)
else:
self.__dict__[key] = value
print(MyClass().some_int)
# >>> <__main__.SpecialIntWrapper object at 0x1076f1748>

dynamic class inheritance using super

I'm trying to dynamically create a class using type() and assign an __init__ constructor which calls super().__init__(...); however, when super() gets called I receive the following error:
TypeError: super(type, obj): obj must be an instance or subtype of type
Here is my code:
class Item():
def __init__(self, name, description, cost, **kwargs):
self.name = name
self.description = description
self.cost = cost
self.kwargs = kwargs
class ItemBase(Item):
def __init__(self, name, description, cost):
super().__init__(name, description, cost)
def __constructor__(self, n, d, c):
super().__init__(name=n, description=d, cost=c)
item = type('Item1', (ItemBase,), {'__init__':__constructor__})
item_instance = item('MyName', 'MyDescription', 'MyCost')
Why is super() inside the __constructor__ method not understanding the object parameter; and how do I fix it?
Solution 1: Using cls = type('ClassName', ...)
Note the solution of sadmicrowave creates an infinite loop if the dynamically-created class gets inherited as self.__class__ will correspond to the child class.
An alternative way which do not have this issue is to assigns __init__ after creating the class, such as the class can be linked explicitly through closure. Example:
# Base class
class A():
def __init__(self):
print('A')
# Dynamically created class
B = type('B', (A,), {})
def __init__(self):
print('B')
super(B, self).__init__()
B.__init__ = __init__
# Child class
class C(B):
def __init__(self):
print('C')
super().__init__()
C() # print C, B, A
Solution 2: Using MyClass.__name__ = 'ClassName'
An alternative way to dynamically create class is to define a class inside the function, then reassign the __name__ and __qualname__ attributes:
class A:
def __init__(self):
print(A.__name__)
def make_class(name, base):
class Child(base):
def __init__(self):
print(Child.__name__)
super().__init__()
Child.__name__ = name
Child.__qualname__ = name
return Child
B = make_class('B', A)
class C(B):
def __init__(self):
print(C.__name__)
super().__init__()
C() # Display C B A
Here is how I solved the issue. I reference the type() method to dynamically instantiate a class with variable references as such:
def __constructor__(self, n, d, c, h):
# initialize super of class type
super(self.__class__, self).__init__(name=n, description=d, cost=c, hp=h)
# create the object class dynamically, utilizing __constructor__ for __init__ method
item = type(item_name, (eval("{}.{}".format(name,row[1].value)),), {'__init__':__constructor__})
# add new object to the global _objects object to be used throughout the world
self._objects[ item_name ] = item(row[0].value, row[2].value, row[3].value, row[4].value)
There may be a better way to accomplish this, but I needed a fix and this is what I came up with... use it if you can.

Resources