Dynamic Python Dataclass - python-3.x

I need to minimize the response footprint of an API. One way we are doing that is eliminating attributes that are null. Which introduces the challenge to create a dataclass that I can build dynamically.
I have managed to get the dynamic class created and it contains the data I need (viewing in log). But, I have not been able to get it inserted into the Parent Class. ParentClass portion is Emtpy with this logic.
#dataclass
class VR: pass
#dataclass
class ResultParent:
validationResult: VR
matchCandidate: List[matchCandidates] = field(default_factory=list)
#create and pop dynamic class
vr = VR()
if valres.matchStatusCode is not None:
vr.matchStatusCode = valres.matchStatusCode
if valres.matchStatusReason is not None:
vr.matchStatusReason = valres.matchStatusReason
...
#This pprint works and the attr/values are what we want
pprint(vars(vr))
#Create ParentClass
obj = ResultParent([vr],[])
# The result of the above command is empty
#Code then continues on to populate matchCandidates and then the ParentClass - no issues there

I was able to accomplish this. I added a setattr that only added the attr if it was not NONE. Found it in this post. Which was the desired result.
#dataclass
class VR:
def __setattr__(self, name, value):
if value is not None:
self.__dict__[name] = value
#dataclass
class ResultParent:
validationResult: VR
matchCandidate: List[matchCandidates] = field(default_factory=list)
vr = VR()
vr.matchStatusCode = p.get('ValAddrStatus')
vr.matchStatusReason = p.get('ValAddrStatusCode')
...
obj = ResultParent([vars(vr)],[])

Related

How to inherit an updated attribute in Python

I'm trying to define two clases, A and B, with B being the child, as in the following code
class A:
def __init__(self, att_A=False):
self.att_A = att_A
def call_B(self):
b = B()
class B(A):
def __init__(self):
super().__init__()
print(f'{self.att_A=}')
a = A()
a.att_A = True
a.call_B()
B does properly inherit the methods and attributes at the time of definition but I want it to also access the values of self.att_A even when they were updated after being initiated.
Is it possible to do that or is there any workaround, like forwarding the attribute as a method parameter?
I have tried deffining att_A as a class attribute but still B cannot access the updated value

How to share variables across Python modules when getter and setter methods are required

How can I share variables across different modules of my Python project if I need these variables to have setter and getter methods.
The reason I need setter\getter methods is because when getting and setting the variables I need to have backwards compatibility with code that stored these variable as environment variables. So I need to write and read using os.environ too.
Usually all I need to do is create a class with class-level variables, import the class in each Module and access the module as follows:
datastore.py/
class DataStore:
target_server_ip: str = '10.10.10.100'
consumer.py/
from project.datastore import DataStore
def print_target_server_ip():
print(DataStore.target_server_ip)
This doesn't work (at least not in Python 3.6.5) if the variables require property getter and setter methods.
The reason is that I cannot define a class level method as a property. The following code just isn't possible:
datastore.py/
class DataStore:
target_server_ip: str = '10.10.10.100'
#classmethod
#property
def target_server_ip(cls):
return cls.target_server_ip
#classmethod
#target_server_ip.setter
def target_server_ip(cls, value):
cls.target_server_ip = value
To solve this issue I propose the following code section. It is split into two classes.
The first class is working at the class level and maintains a 2 level nested dictionary that contains the name of the datastore and the variable name.
The second class is the datastore itself. It has the minimum required code to keep it visually simple.
This specific implementation has one known error prone limitation. If you declare two or more variables with the same name in different datastore classes, i.d. you define class FrameworkDatastore and another class SecondDatastore with the same variable in both, the environment will have only one of them.
import inspect
import logging
import os
from typing import Any, Dict, Type
logger = logging.getLogger(__name__)
class _BaseDataStoreWithEnvironSupport:
"""
The class support global storing of variables in a class level dictionary, allowing all instances of the
datastore to access the same values.
This class is backward compatible to store the global variables as os.environ, but also
"""
_members: Dict[str, Dict[str, Any]] = {} # holds all the members of the datastore
#classmethod
def get_value(cls) -> Any:
datastore_name: str = cls.__name__
member_name: str = inspect.stack()[1][3]
env_value: str = os.environ.get(member_name)
ds_value: Any = cls._members[datastore_name][member_name]
if env_value:
type_ds_value: Type = type(ds_value)
if type_ds_value is bool:
value: bool = (env_value == True.__str__())
else:
value: Any = type(ds_value)(env_value)
if value != ds_value:
logger.warning('Environment stored value is different from Datastore value. Check your implementation')
else:
value: Any = ds_value
return value
#classmethod
def set_value(cls, value: Any) -> None:
datastore_name: str = cls.__name__
name: str = inspect.stack()[1][3]
if datastore_name not in cls._members.keys():
cls._members[datastore_name] = {}
cls._members[datastore_name][name] = value
os.environ[name] = str(value)
def validate_datastore(self):
members = set([attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("_")])
if members.__len__() == 0:
raise RuntimeError(f'There are no members in the datastore or the validation runs at the start of __init__')
datastore_name: str = self.__class__.__name__
dict_keys: set = set(self._members[datastore_name].keys())
if members != dict_keys:
missing_members: set = members - dict_keys
raise NotImplementedError(f'Datastore is missing get and set methods for members: {missing_members}')
class FrameworkDatastore(_BaseDataStoreWithEnvironSupport):
"""
This class is storing all variables that are currently saved as global or os.environ variables
If the data stored here becomes irrelevant after the code change or is seldom used, remove it and merge its
functionality into other sections
"""
def __init__(self):
"""
predefine all the members of the datastore.
Members which dont implement get/set methods will be flagged by the validate_datastore check
"""
self.run_traffic_validations: bool = True # Should Ixia traffic validations run in the current suite
# The validation of the datastore must come at the end of the __init__ method
self.validate_datastore()
#property
def run_traffic_validations(self):
return self.get_value()
#run_traffic_validations.setter
def run_traffic_validations(self, value: Any):
self.set_value(value)
if __name__ == '__main__':
# This tests the datastore code
fd1 = FrameworkDatastore()
fd2 = FrameworkDatastore()
print(fd1.run_traffic_validations)
print(fd2.run_traffic_validations)
fd1.run_traffic_validations = False
print(fd1.run_traffic_validations)
print(fd2.run_traffic_validations)
fd2.run_traffic_validations = True
print(fd1.run_traffic_validations)
print(fd2.run_traffic_validations)

Python / Attributes between methods of a class

I'm new in Python and I'm trying to get my head around how are managed attributes between methods of a class.
In the following example, I'm trying to modify a list in the method "regex" and use it afterwards in another method "printsc".
The "regex" part works without issues, but the attribute "self.mylist" is not updated so when I call "printsc" the result is "None".
class MyClass():
def __init__(self):
self.mylist = None
def regex(self, items):
self.mylist = []
for item in items:
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", item):
self.mylist.append("IP:" + item)
else:
self.mylist.append("DNS:" + item)
return self.mylist
def printsc(self):
print(self.mylist)
items = ['192.168.0.1', 'hostname1', '10.0.1.15', 'server.local.fr']
MyClass().regex(items)
MyClass().printsc()
What am I missing ? What is the best way to achieve this goal ?
Thank you for your answers!
When you do MyClass(), it returns you an object.. And you are calling your methods on the object. Since you are doing it twice, each time a new object is created and regex and printsc are called on different objects.
what you should do is
myObj = MyClass()
myObj.regex(items)
myObj.printsc()
The problem is that when you do:
MyClass().regex(items)
MyClass().printsc()
You are creating 2 separate instances of MyClass, each of which will have a different .mylist attribute.
Either mylist is an instance attribute, and then this will work:
instance = MyClass()
instance.regex(items)
instance.printsc()
Or, if you want to share .mylist across instances, it should be
a class attribute:
class MyClass():
class_list = None
def __init__(self):
pass
def regex(self, items):
cls = self.__class__
if cls.class_list is None:
cls.class_list = []
for item in items:
if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", item):
cls.class_list.append("IP:" + item)
else:
cls.class_list.append("DNS:" + item)
return cls.class_list
def printsc(self):
# Going throuhgh `.__class__.` is actually optional for
# reading an attribute - if it is not in the instance
# Python will fetch it from the class instead.
# i.e. , the line bellow would work with `self.class_list`
print(self.__class__.class_list)
This way, the list persists across different instances of the class, as you try to do in your example.
You should create an object of the class:
a = MyClass()
a.regex(items)
a.printsc()
>>> ['IP:192.168.0.1', 'DNS:hostname1', 'IP:10.0.1.15', 'DNS:server.local.fr']

How to print class variables in a list

So I am very new to coding and started with python, I am trying to build a class in a program that puts together a DnD party by randomising their attributes. So far I can get the program to initialise instances of the party members and just give the user a prompt on how many of the hero's to choose from they would like in their party. My issue is that after setting the lists up and getting everything in place. I am unable to print any of the attributes of the individual heros. Regardless of whether I am calling them from within the lists or if I am directly trying to print them. I have tried using __str__ to create strings of the attributes but I am clearly missing something. Any help would be greatly appreciated.
import random
class Party:
def __init__(self, name="", race="", alignment="", class_=""):
self.name = name
while name == "":
name = random.choice(names)
# print(name)
self.race = race
while race == "":
race = random.choice(races)
# print(race)
self.alignment = alignment
while alignment == "":
alignment = random.choice(alignments)
# print(alignment)
self.class_ = class_
while class_ == "":
class_ = random.choice(classes)
# print(class_)
def character_stats(self):
return "{} - {} - {} - {}".format(self.name, self.race, self.class_, self.alignment)
Each attribute pulls a random value from a list. My format statement is the latest attempt to get the values of the attributes to print rather than the object/attributes instead.
I apologise if any of the terminology is wrong, very very new to this
You are not assigning anything else but the input, (in this case being an empty string "" to the attribuytes. In your minimal example you have this constructor:
class Party:
def __init__(self, name=""):
self.name = name
while name == "":
name = random.choice(names)
After you randomly assign a new name from names, you should assign it to self, otherwise the local variable just goes out of scope when the __init__ method finishes. This code snippet should work:
class Party:
def __init__(self, name=""):
while name == "":
name = random.choice(names)
# Now we assign the local variable as
# an attribute
self.name = name

How to add members to a class dynamically using options as coonstructor args in python?

I want to create my Python class which will dynamically build its members in the constructor based on the options.
Check the following sample code that I am trying to build.
options = {
'param1' = 'v1'
'param2' = 2
'param3' = bool
'param4' = {}
}
class MyCustomObject:
def __init__(self, options):
self.props = {}
for key in options.keys():
self.props[key] = options[key]
a1 = MyCustomObject(options)
# I want to access the attributes of the props using dot
a1.param1 = 10
print(a1.param1)
If I had known what options I need to add to the my object when defining the class, the I could have added following to the class definition:
#property
def param1(self):
return self.options['param1']
#param1.setter(self, value)
self.options['param1'] = value
How to achieve the same behavior dynamically i.e. providing the options during object instantiation?
You can make use of setattr method of Python.
setattr(object, name, value)
The setattr() function sets the value of the attribute of an object.
options = {'param1' :'v1','param2' : 2,'param3' : "bool",'param4' : {}}
class MyCustomObject:
def __init__(self, options):
for key in options.keys():
setattr(self, key, options[key])
a1 = MyCustomObject(options)

Resources