Create a python Class inheriting from a Class created by a function - python-3.x

What works :
I am using the module recordtype to store global parameters for the different functions of my program.
Each parameter is a class instance of :
class Parameter(object):
def __init__(self, name, value, info):
self.name = name
self.value = value
self.info = info
Then the global structure is defined like this :
The individual parameters :
parameter_1 = Parameter('param_1', 10, 'Value for the parameter 1, usage...')
parameter_2 = Parameter('param_2', 20, 'Value for the parameter 2, usage...')
...
parameter_m = Parameter('param_n', 50, 'Value for the parameter m, usage...')
parameter_n = Parameter('param_n', 100, 'Value for the parameter n, usage...')
Parameter sub sets :
parameter_set_1 = recordtype('parameter_set_1', [(parameter_1.name, parameter_1),
(parameter_2.name, parameter_2), ...])
...
parameter_set_n = recordtype('parameter_set_n', [(parameter_m.name, parameter_m),
(parameter_n.name, parameter_n)]
The global parameter structure is then :
GlobalParametersFunction = recordtype('GlobalParameters', [('parameter_set_1', parameter_set_1()),
('parameter_set_2', parameter_set_2()), ('parameter_set_n', parameter_set_n())])
Which needs to be instantiated :
GlobalParameters = GlobalParameterFunction()
This all works well, the GlobalParameterFunction creates a class where I can access individual parameters and change their values, Ex:
GlobalParameters.parameter_set_1.parameter_1.value = 20
From the GlobalParameters class instance I can then make a function that prints the values and their names :
def print_parameter_info(GP):
for field, val in zip(GP._asdict(), GP._asdict().values()):
print(field, ':')
for key, entry in zip(val._asdict(), val._asdict().values()):
print('\t', entry.name, ':', entry.value)
Which does a nice print for the user :
>>> print_parameter_info(GlobalParameters)
parameter_set_1 :
parameter_1 : 10
parameter_2 : 20
parameter_set_n :
parameter_m : 50
parameter_n : 100
I would also like to create a function such that :
change(GlobalParameters, 'name', new_value)
does :
GlobalParameters.parameter_set_1.name.value = new_value
Which seems easily doable with the class created by recordtype
The problem :
I would like to create a class methods for the GlobalParameters class instance, from the print_parameter_info() function such that :
GlobalParameters.print_parameter_info()
and
GlobalParameters.change(name, new_value)
works
because GlobalParameters is a class instance of recordtype, I tried :
class GlobalParameterClass(recordtype):
def __init__(self):
self = GlobalParameterFunction()
But because recordtype is a function creating a class ?
I get the following error :
TypeError: function() argument 'code' must be code, not str
I found this question (2231427)
But after trying to import the right thing and looking at the source code of recordtype I think I understood that recordtype does not define a class clearly and creates it by parsing a string of code ?
Thus I do not understand how to create a class inheriting from the class created by recordtype
I also tried
class GlobalParameterClass(object):
def __init__(self, *args):
self = GlobalParameterFunction(*args)
This does not raise any errors but the class instance created is empty.
TLDR/Conclusion
How can I add custom methods to the class created by the recordtype function from the recordtype module ?
Or
Perhaps, there is there a better way to manage the GlobalParameters object ?
Thank you !

I found a solution without using the recordtype object, by creating a custom classes with the desired behavior :
class Parameter(object):
"""
A class to store the individual sound parameters
"""
def __init__(self, name, value, info):
self.name = name
self.value = value
self.info = info
class ParameterSet(object):
"""
A class to store multiple parameters as a set
"""
def __init__(self, name, *parameters):
self.name = name
for parameter in parameters:
setattr(self, parameter.name, parameter)
class GlobalParameters(object):
"""
A class to store the parameter sets and to be used to assign parameter values to the different functions
"""
def __init__(self, *parameter_sets):
for parameter_set in parameter_sets:
setattr(self, parameter_set.name, parameter_set)
The methods of the GlobalParameters classes go as follow :
def info(self):
for parameter_set in self.__dict__.values():
print(parameter_set.name)
parameters = [parameter for parameter in parameter_set_1.__dict__.values() if type(parameter) != str]
for parameter in parameters:
print('\t', parameter.name, ':', parameter.value)
def change(self, name, value):
for parameter_set in self.__dict__.values():
parameters = [parameter for parameter in parameter_set_1.__dict__.values() if type(parameter) != str]
for parameter in parameters:
if parameter.name == name:
parameter.value = value
With this example code :
# Individual parameters
parameter_1 = Parameter('parameter_1', 10, 'Value for the parameter 1, usage...')
parameter_2 = Parameter('parameter_2', 20, 'Value for the parameter 2, usage...')
parameter_n = Parameter('parameter_n', 50, 'Value for the parameter n, usage...')
parameter_m = Parameter('parameter_m', 100, 'Value for the parameter m, usage...')
# Parameter sets
parameter_set_1 = ParameterSet('parameter_set_1', parameter_1, parameter_2)
parameter_set_2 = ParameterSet('parameter_set_n', parameter_n, parameter_m)
# Global parameter set
GP = GlobalParameters(parameter_set_1, parameter_set_2)
I get the desired behavior :
>>> GP.info()
parameter_set_1
parameter_1 : 10
parameter_2 : 20
parameter_set_n
parameter_1 : 10
parameter_2 : 20
As well as with the .change method :
>>> GP.parameter_set_1.parameter_1.value
10
GP.change('parameter_1', 15)
>>> GP.parameter_set_1.parameter_1.value
15

Related

AttributeError Problem with Multiple inheritance in python

I wanted to calculate the Total and Average Marks of a student with multiple inheritances in python. But whenever I create an object for my child class it gives me access to all the methods of parent classes but shows an AttributeError when I call the method of the child class. I tried to use the super() function but the result is the same.
I just paste my code below. Can you suggest to me a solution to that?
class Student_Marks:
def __init__(self):
# initializing variables
self.__roll: int
self.__marks1: int
self.__marks2: int
def getMarks(self):
self.__roll = int(input("Enter Roll No: "))
self.__marks1, self.__marks2 = map(int, input("Enter Marks: ").split())
return self.__roll, self.__marks1, self.__marks2
class Cocurricular_Marks:
def __init__(self):
self.__activemarks: int
def getActiveMarks(self):
self.__activemarks = int(input("Enter Co Curricular Activities Marks: "))
return self.__activemarks
class Result(Student_Marks, Cocurricular_Marks):
def __init__(self):
super().getMarks()
super().getActiveMarks()
def display(self):
total = self.__marks1 + self.__marks2 + self.__activemarks
avg = total / 3
print("Roll No: ", self.__roll)
print("Total Marks: ", total)
print("Average Marks: ", avg )
# creating Objects
res = Result()
res.getMarks()
res.getActiveMarks()
res.display() # I got problem here
You're prepending the attributes with two underscores in the classes, this mangles the name of the attribute (see the documentation on Private Variables).
For instance, Student_Marks's __roll will be mangled to _Student_Marks__roll as you exit getMarks.
Hence Result.display() not being able to access self.__marks1, but it can access it as self._Student_Marks__marks1.
See the following minimal example.
class K:
__a = 1
class KK(K):
pass
k = K()
k.__a # AttributeError: 'K' object has no attribute '__a'
k._K__a # 1
kk = KK()
kk._K__a # 1

Can I store a function rather than its result to a dictionary value so that it is dynamically updated when the value is called?

I have a dictionary:
mydict = {'x':val1,'x2':val2}
I would like to do something like
mydict['x3'] = some_func(mydict['x1'])
Where if I later change mydict['x1'] and call mydict['x3'], I return the output of some_func() with the new value of x1.
I realize this is something class property decorators handle well. I'd like to also be able to change the function stored at the 'x3' key for that specific object. This is something class properties cannot do to my knowledge (having the underlying function definition changed on an instance of the class).
You can just set 'x3' to a function in your dict. Then whenever you want to use it, you can call it. Something like this:
mydict = {'x1': val1,'x2': val2, 'x3': lambda: some_func(mydict['x1'])}
Now, mydict['x3'] is a function.
x3 = mydict['x3']()
print(x3)
Here's a quick demo
At this point, you should define a class rather than using a dict directly.
class MyClass:
def __init__(self, x1, x2):
self.x1 = x1
self.x2 = x2
#property
def x3(self):
return some_func(self.x1)
mydict = {'x':val1,'x2':val2}
m = MyClass(mydict['x'], mydict['x2'])
assert m.x3 == some_func(m.x1)
Based on primarily on Rocket's answer, I came up with this test solution.
class Test:
def __init__(self, x, y):
self.x = x
self.y = y
def set_prop(self, name, func):
setattr(Test, name, property(fget=func))
test = Test(1,2)
### Print list of class attributes in __dict__
for key, val in test.__dict__.items():
print(f"{key} = {val}")
### Create new property, xy defined by self.x * self.y
test.set_prop(name='xy', func=lambda self: self.x*self.y)
print(f"test.x ({test.x}) * test.y ({test.y}) = {test.xy}")
### Test if it is properly linked to changes in x
test.x = 2
print(f"test.x set to {test.x}")
print(f"test.x ({test.x}) * test.y ({test.y}) = {test.xy}")
### Test changing the underlying property formula
test.set_prop('xy',lambda self: self.x/self.y)
print(f"test.x ({test.x}) / test.y ({test.y}) = {test.xy}")

Accessing Class attribute from a called function?

Let say I have something like this :
--module1
def called():
if caller.class.attrX == 1 : ...
--module2
class ABC:
attrX = 1
def method():
called()
I want to access caller Class-attribute ?
I know I have to use inspect somehow but can figure how exactly.
python3
Passing a variable to the function is the best (and only?) option.
--module1
def called(attrX):
if attrX == 1 : ...
--module2
class ABC:
self.attrX = 1
def method():
called(self.attrX)
This seems to work for object variable :
/if I can make it work for class-var it will be better/
import inspect
def say(*args, **kwargs) :
obj = inspect.currentframe().f_back.f_locals['self']
if hasattr(obj,'aaa') : print('hasit')
else : print("no")
class ABC:
aaa = 2
def test(self):
say(123)
i.e. if I dont have 'aaa' set in advance :
In [8]: a.test()
no
In [9]: ABC.aaa = 2
In [10]: a.test()
no
In [12]: a.aaa = 3
In [13]: a.test()
hasit

Can't change instance attribute of my class?

I'm starting out in python and I can't quite figure out why I'm unable to change the data stored in one of my instance attributes. I have the following code:
class Bank:
def __init__(self, bank = 'Unnamed'):
self.bank = bank
self.clients = []
self.status = {'bank': self.bank, 'clients': self.clients}
self.c_counter=0
def deposit(self, name = None, amount = 200):
self.name = name
self.amount = amount
self.c_counter += 1
if self.name is None:
self.name = print("client" + str(self.c_counter));
self.clients.append((self.name, self.amount))
else:
self.clients.append((self.name, self.amount))
This produces the following output:
bb = Bank("bb")
bb.deposit(amount = 2000)
bb.status
out: {'bank': bb, 'clients': [(None, 2000)]}
While the desired output is:
out: {'bank': bb, 'clients': [('client1', 2000)]}
As you can see, what I'm trying to do is to set the client name to "clientx" if client name is not specified during a deposit; the x is just a number to distinguish each generic client from other generic clients without a specific name.
If a specific name is not provided when calling the deposit attribute of the bank the client name is set to None by default. I check this with the if condition and change the client name accordingly, but for some reason the client name literally gets added as "None" to the list of clients in the dictionary (list of tuples). What is wrong here?
print prints a string to sys.stdout and returns None. you should change the line
self.name = print("client" + str(self.c_counter));
to
self.name = "client" + str(self.c_counter)
or maybe self.name = f"client{self.c_counter}" for python >= 3.6.
print is a NoneType, it's None and you can prove it:
>>> type(print())
<class 'NoneType'>
>>>
And:
>>> print(print())
None
>>>
So print isn't be used for assigning, print is used for outputting stuff, whereas in this case you're assigning stuff, that which isn't meant for print to handle, so change:
self.name = print("client" + str(self.c_counter));
To:
self.name = "client" + str(self.c_counter)
Or:
self.name = "client%s" % self.c_counter
Or:
self.name = "client{}".format(self.c_counter)
Or if your python version is bigger or equal to 3.6, you can use:
self.name = f"client{self.c_counter}"

Python3 Class Syntax Error: non-default argument follows default argument

I'm messing around with Class' and I have a python3 file with the following code:
class playerstats(object):
def __init__(self, name, baselevel=1, hp, mana, basedmg=0, atkdmg, basedef=0, chardef):
self.name = name
self.baselevel = baselevel
self.hp = hp
self.mana = mana
self.basedmg = basedmg
self.atkdmg = atkdmg
self.basedef = basedef
self.chardef = chardef
return self.name, self.baselevel, self.basedmg, self.basedef
def selectedclass(self, chosenclass):
if chosenclass == 'W' or chosenclass == 'w':
self.hp = 100
self.mana = 50
elif chosenclass == 'M' or chosenclass == 'm':
self.hp = 75
self.mana = 100
else:
print('Error')
return self.hp, self.mana
charcreation = playerstats('Tom', baselevel, self.chosenclass, self.chosenclass, basedmg, 0, basedef, 0)
self.chosenclass = 'w'
print(playerstats.hp)
When I run it, I get this Error:
File "..\Playground\", line 2
def init(self, name, baselevel=1, hp, mana, basedmg=0, atkdmg, basedef=0, chardef):
^
SyntaxError: non-default argument follows default argument
Can someone help me understand why?
You need to change the order of parameters in __init__ function.
Generally, non-default argument should not follow default argument , it means you can't define (a="b",c) in function.
The order of defining parameter in function are :
positional parameter or non-default parameter i.e (a,b,c)
keyword parameter or default parameter i.e (a="bcd",r="jef")
keyword-only parameter i.e (*args)
var-keyword parameter i.e (**kwargs)

Resources