Python parent class modifiable by child class or dynamic attribute? - python

So I am not even sure if what I want to do is possible but I thought I would ask and find out.
I want to build a chef "databag" via python. This is pretty much just a python dictionary. There are other things that need to happen with this databag that are encapsulated in the Databag class.
Now for the meat of the question...
I want to add key/values to this dictionary but need to build it in a way that is easily extensible. NOTE: the autodict is a class that makes it so you can build a dictionary using dot notation.
Here is what I am trying to do:
databag = Databag(
LogGroup=Sub("xva-${environment}-${uniqueid}-mygroup"),
RunList=[
"mysetup::default",
"consul::client"
]
)
databag.Consul() <-- Trying to add consul key/values to databag
print(databag.to_dict())
print(databag.to_string_list())
So you can see how I add the "consul" key values to the already existing databag object.
Here are the class definitions. I know this is wrong which is why I am here to see if this is even possible.
Databag Class
class Databag(object):
def __init__(self,uniqueid=Ref("uniqueid"),environment=Ref("environment"),LogGroup=None,RunList=[]):
self.databag = autodict()
self.databag.uniqueid = uniqueid
self.databag.environment = environment
self.databag.log.group = LogGroup
self.runlist=RunList
def to_string_list(self):
return self.convert_databag_to_string(self.databag)
def to_dict(self):
return self.databag
def get_runlist(self):
return self.convert_to_runlist_string(self.runlist)
Consul Class
class Consul(Databag):
def __init__(self, LogGroup=None):
if LogGroup == None:
Databag.consul.log.group = Databag.log.group
else:
Databag.consul.log.group = LogGroup
As you can see the Consul class is supposed to access the databag dictionary of the Databag class and add the "consul" variables, almost like an attribute. However, I don't want to add a new function to the databag class every time otherwise that class will end up being very, very large.

I was able to get something like this to work with the following method. Although I am up for an suggestions to get this to work. I just read the help posted on this link:
http://www.qtrac.eu/pyclassmulti.html
EDIT: This method is a lot easier:
Note: This uses the exact same implementation of the old method.
consul.py
from classes.databag.utils import *
class Consul:
def Consul(self, LogGroup=None):
if LogGroup == None:
self.databag.consul.log.group = self.databag.log.group
else:
self.databag.consul.log.group = LogGroup
databag.py
from classes.databag.utils import autodict
from classes.databag import consul
class Databag(consul.Consul):
def __init__(self,uniqueid=Ref("uniqueid"),environment=Ref("environment"),LogGroup=None,RunList=[]):
self.databag = autodict()
self.databag.uniqueid = uniqueid
...
...
Folder Structure
/classes/
databag/
utils.py
databag.py
consul.py
testing.py
---- OLD METHOD -----
How I implemented it
from classes.databag.databag import *
databag = Databag(
LogGroup=Sub("xva-${environment}-${uniqueid}-traefik"),
RunList=[
"mysetup::default",
"consul::client"
]
)
databag.Consul()
print(databag.to_dict())
print(databag.to_string_list())
lib.py
def add_methods_from(*modules):
def decorator(Class):
for module in modules:
for method in getattr(module, "__methods__"):
setattr(Class, method.__name__, method)
return Class
return decorator
def register_method(methods):
def register_method(method):
methods.append(method)
return method
return register_method
databay.py
from classes.databag import lib, consul
#lib.add_methods_from(consul)
class Databag(object):
def __init__(self,uniqueid=Ref("uniqueid"),environment=Ref("environment"),LogGroup=None,RunList=[]):
self.databag = autodict()
self.databag.uniqueid = uniqueid
....
....
consul.py
from classes.databag import lib
__methods__ = []
register_method = lib.register_method(__methods__)
#register_method
def Consul(self, LogGroup=None):
if LogGroup == None:
self.databag.consul.log.group = self.databag.log.group
else:
self.databag.consul.log.group = LogGroup
Folder Structure
/classes/
/databag
lib.py
databag.py
consul.py
utils.py
/testing.py

Related

divide classes into sub functions/classes python

First time poster and python newbie here, this question is probably asked before, but I am not able to find any answer.
I have a Class that reads robot status data, this works fine and I am able to dive my data into methods that are working fine when i call them. But I would like to divide my class even more, so the data is structured better. for example
I have some methods the reads and return target_data
def target_joint_positions(self):
t_j_p = self.read_data()[1:7]
return t_j_p
def target_joint_velocities(self):
t_j_v = self.read_data()[7:13]
return t_j_v
def target_joint_currents(self):
t_j_c = self.read_data()[19:25]
return t_j_c
And similar methods returning actual_data:
def actual_joint_positions(self):
a_j_p = self.read_data()[31:37]
return a_j_p
def actual_joint_velocities(self):
a_j_v = self.read_data()[37:43]
return a_j_v
def actual_joint_currents(self):
a_j_c = self.read_data()[43:49]
return a_j_c
So what I would like to accomplish is that when i make a instance of my class, instead of getting all the methods i would like something like this:
inst = Class_Name()
inst.target. (list of target methods)
inst.actual. (list of actual methods)
I have looked into nested classes and inheritance but I have not been successful in achieving my goal. Thanks for any pointers.
Welcome!
You can do the following:
class TargetMetrics:
def __init__(self, data):
self.data = data
def joint_positions(self):
return self.data[1:7]
...
class ActualMetrics:
def __init__(self, data):
self.data = data
def joint_positions(self):
return self.data[31:37]
...
class RobotMetrics:
def __init__(self):
data = read_data()
self.actual = ActualMetrics(data)
self.target = TargetMetrics(data)
...

Python 3 Scope between classes in separate files?

I have been researching for ages and cannot find this specific question being asked (so perhaps I am missing something simple!) but I have had trouble separating classes into different .py files.
Scenario:
Main class imports a Settings class file and a Work class file..Settings class populates a list with objects instantiated from an Object class file...
Work class wants to cycle through that list and change values within each of those objects. <-- here is where I come unstuck.
I have tried it by making the values class variables rather than instance. Still I have to import the settings class in the work class in order to write the code to access the value to change. But it wont change the instance of that class within the main class where all these classes are called!
I read an article on Properties. The examples they gave were still examples of different classes within the same file.
Any advice as to what I should be looking at would be greatly appreciated!
This is what I was doing to test it out:
Main File where all will be run from:
import Set_Test
import Test_Code
sting = Set_Test.Settings()
tc = Test_Code.Testy()
ID = sting._settingsID
print(f'Settings ID is: {ID}')
tc.changeVal()
ID = sting._settingsID
print(f'Settings ID is: {ID}')
Set_Test.py:
class Settings:
def __init__(self):
self._settingsID = 1
#property
def settingsID(self):
return self._settingsID
#settingsID.setter
def settingsID(self, value):
self.settingsID = value
Test_Code.py:
import Set_Test
class Testy:
def changeVal(self):
Set_Test.Settings.settingsID = 8
Thanks to stovfl who provided the answer in comments. I managed to decipher what stovfl meant eventually :D
I think!
Well the below code works for anyone who wants to know:
Main:
import Set_Test
import Test_Code
sting = Set_Test.Settings()
tc = Test_Code.Testy()
ID = sting._settingsID
print(f'Settings ID is: {ID}')
tc.changeVal(sting)
ID = sting._settingsID
print(f'Settings ID is: {ID}')
Set_Test.py:
class Settings:
def __init__(self):
self._settingsID = 1
#property
def settingsID(self):
return self._settingsID
#settingsID.setter
def settingsID(self, value):
self._settingsID = value
Test_Code.py
import Set_Test
sting = Set_Test.Settings()
class Testy():
def changeVal(self, sting):
print(sting.settingsID)
sting.settingsID = 8
print(sting.settingsID)

How to make nested enum also have value

Consider the following code example:
from enum import Enum
class Location(Enum):
Outside = 'outside'
Inside = 'inside'
class Inside(Enum): # TypeError for conflicting names
Downstairs = 'downstairs'
Upstairs = 'upstairs'
How do I make Inside have the value 'inside' whilst also being a nested enum for accessing Downstairs and Upstairs?
Desired input:
print(Location.Inside)
print(Location.Inside.value)
print(Location.Inside.Downstairs)
print(Location.Inside.Downstairs.value)
Desired output:
Location.Inside
inside
Location.Inside.Downstairs
downstairs
UPDATE 1:
Some more context to my specific problem:
class Location(Enum):
Outside = 'outside'
Inside = 'inside'
class Inside(Enum): # TypeError for conflicting names
Downstairs = 'downstairs'
Upstairs = 'upstairs'
class Human:
def __init__(self, location):
self.location = location
def getLocationFromAPI():
# this function returns either 'inside' or 'outside'
# make calls to external API
return location # return location from api in str
def whereInside(human):
if human.location != Location.Inside:
return None
# here goes logic that determines if human is downstairs or upstairs
return locationInside # return either Location.Downstairs or Location.Upstairs
location_str = getLocationFromAPI() # will return 'inside' or 'outside'
location = Location(location_str) # make Enum
human = Human(location) # create human with basic location
if human.location == Location.Inside:
where_inside = whereInside(human)
human.location = where_inside # update location to be more precise
The problem is when I create the Human object I only know of a basic location, as in 'inside' or 'outside'. Only after that can I update the location to be more precise.
You can accomplish this by embedding an enum.Enum inside another like so: (just watch out for names conflicting)
from enum import Enum
class _Inside(Enum):
Downstairs = 'downstairs'
Upstairs = 'upstairs'
class Location(Enum):
Outside = 'outside'
Inside = _Inside
print(Location.Inside.value.Downstairs.value)
downstairs
it may be a bit late and the one who asked the question is no longer necessary, but I leave it here in case someone wants to take a look at it, and even if it has already been validated as one, although the same comment that it is not completely complete .
But I have been thinking about it and in the end I have solved it by looking at the same documentation XD.
You cannot extend classes of Enums, but you can extend methods, I have followed this way and the only thing I have done has been to override the new and init methods, the use case can be modified, this is only to nest enumerators.
from enum import Enum
class SuperNestedEnum(Enum):
def __new__(cls, *args):
obj = object.__new__(cls)
value = None
# Normal Enumerator definition
if len(args) == 1:
value = args[0]
# Have a tuple of values, first de value and next the nested enum (I will set in __init__ method)
if len(args) == 2:
value = args[0]
if value:
obj._value_ = value
return obj
def __init__(self, name, nested=None):
# At this point you can set any attribute what you want
if nested:
# Check if is an Enumerator you can comment this if. if you want another object
if isinstance(nested, EnumMeta):
for enm in nested:
self.__setattr__(enm.name, enm)
class Homework(Enum):
Task = "5"
class Subjects(SuperNestedEnum):
Maths = "maths"
English = "english"
Physics = "nested", Homework
class School(SuperNestedEnum):
Name = "2"
Subjects = "subjects", Subjects
Ignore the use case because it doesn't make sense, it's just an example
>>> School.Name
<School.Name: '2'>
>>> School.Subjects
<School.Subjects: 'subjects'>
>>> School.Subjects.value
'subjects'
>>> School.Subjects.Maths
<Subjects.Maths: 'maths'>
>>> School.Subjects.Physics.value
'nested'
>>> School.Subjects.Physics.Task
<Homework.Task: '5'>
>>> School.Subjects.Physics.Task.value
'5'
If anyone has similar issues and just wants a simple solution for the topic without patching any functions or additional imports for enums containing strings, follow these steps:
Create the value enums, in your lower hierarchy, like:
class __private_enum1__(str, enum.Enum):
VAL11 = "abc"
VAL12 = "def"
class enum2(str, enum.Enum):
VAL21 = "123"
VAL22 = "456"
Create a base class (a container) for these enums. Where you can either import the enums classes or simply directly acccess the enums.
class myValues:
VAL11 = __private_enum1__.VAL11
VAL12 = __private_enum1__.VAL12
VALS2X = enum2
Then you can access your values by:
print(myValues.VAL11.value)
print(myValues.VAL2X.VAL21.value)
.value is not necessary here but it shows that you both access the string inside the enum for passing it to other functions but also the enum itself, which is pretty neat. So basically, first create the values, then the structure. That way you have a class but it provides you the basic functionality of enums and you can nest them as deep as you want to without further imports.

Looking for a short way to generate subclass (that would be created into a new file), with parent class' methods included in it (Python)

Will use following example to explain.
Existing python file (a.py) contains one class:
class A:
def method1(self, par1, par2='e'):
# some code here
pass
def method2(self, parA):
# some code here
pass
def method3(self, a, b, c):
# lots of code here
pass
def anothermethod(self):
pass
if __name__ == '__main__':
A().anothermethod()
Now, there is a need to create another py file (b.py), which would contain subclass (class B) of class A.
And there is a need to have all the methods included (all inherited from parent class), but without
implementation in it. Result might look like:
class B(A):
def method1(self, par1, par2='e'):
# empty here; ready to override
pass
def method2(self, parA):
# empty here; ready to override
pass
def method3(self, a, b, c):
# empty here; ready to override
pass
def anothermethod(self):
# empty here; ready to override
pass
if __name__ == '__main__':
B().anothermethod()
Having described the example, the question is: how could one generate last mentioned (skeleton-like) py file? So that after generating you can just open generated file and start right away with filling specific implementation.
There must be a shorter way, 1-2 line solution. Maybe it is already solvable by some existing functionality within modules already provided by Python (Python 3)?
Edit (2018 Mar 14). Thank you https://stackoverflow.com/a/49152537/4958287 (though was looking for short and already existing solution here). Will have to settle with longer solution for now -- will include its rough version here, maybe it would be helpful to someone else:
import inspect
from a import A
def construct_skeleton_subclass_from_parent(subcl_name, parent_cl_obj):
"""
subcl_name : str
Name for subclass and
file to be generated.
parent_cl_obj : obj (of any class to create subclass for)
Object of parent class.
"""
lines = []
subcl_name = subcl_name.capitalize()
parent_cl_module_name = parent_cl_obj.__class__.__module__
parent_cl_name = parent_cl_obj.__class__.__name__
lines.append('from {} import {}'.format(parent_cl_module_name, parent_cl_name))
lines.append('')
lines.append('class {}({}):'.format(subcl_name, parent_cl_name))
for name, method in inspect.getmembers(parent_cl_obj, predicate=inspect.ismethod):
args = inspect.signature(method)
args_others = str(args).strip('()').strip()
if len(args_others) == 0:
lines.append(' def {}(self):'.format(name))
else:
lines.append(' def {}(self, {}):'.format(name, str(args).strip('()')))
lines.append(' pass')
lines.append('')
#...
#lines.append('if __name__ == \'__main__\':')
#lines.append(' ' + subcl_name + '().anothermethod()')
#...
with open(subcl_name.lower() + '.py', 'w') as f:
for c in lines:
f.write(c + '\n')
a_obj = A()
construct_skeleton_subclass_from_parent('B', a_obj)
Get the list of methods and each of their signatures using the inspect module:
import a
import inspect
for name, method in inspect.getmembers(a.A, predicate=inspect.ismethod):
args = inspect.signature(method)
print(" def {}({}):".format(name, args))
print(" pass")
print()

Python: How to enable the following API using class magic

Objective:
Given something like:
stackoverflow.users['55562'].questions.unanswered()
I want it converted into the following:
http://api.stackoverflow.com/1.1/users/55562/questions/unanswered
I have been able to achieve that, using the following class:
class SO(object):
def __init__(self,**kwargs):
self.base_url = kwargs.pop('base_url',[]) or 'http://api.stackoverflow.com/1.1'
self.uriparts = kwargs.pop('uriparts',[])
for k,v in kwargs.items():
setattr(self,k,v)
def __getattr__(self,key):
self.uriparts.append(key)
return self.__class__(**self.__dict__)
def __getitem__(self,key):
return self.__getattr__(key)
def __call__(self,**kwargs):
return "%s/%s"%(self.base_url,"/".join(self.uriparts))
if __name__ == '__main__':
print SO().abc.mno.ghi.jkl()
print SO().abc.mno['ghi'].jkl()
#prints the following
http://api.stackoverflow.com/1.1/abc/mno/ghi/jkl
http://api.stackoverflow.com/1.1/abc/mno/ghi/jkl
Now my problem is I can't do something like:
stackoverflow = SO()
user1 = stackoverflow.users['55562']
user2 = stackoverflow.users['55462']
print user1.questions.unanswered
print user2.questions.unanswered
#prints the following
http://api.stackoverflow.com/1.1/users/55562/users/55462/questions/unanswered
http://api.stackoverflow.com/1.1/users/55562/users/55462/questions/unanswered/questions/unanswered
Essentially, the user1 and user2 refer to the same SO object, so it can't represent different users.
I have been thinking any pointers to do that would be helpful, because this additional level of functionality would make the API far more interesting.
IMHO, when you recreate a new stackoverflow object, you need to separate the arguments from old instance attributes with a deep copy
import copy
........
def __getattr__(self,key):
dict = copy.deepcopy(self.__dict__)
dict['uriparts'].append(key)
return self.__class__(**dict)
....
If you want more flexibility on the URI parts, an abstraction is needed for a cleaner design. For example:
class SOURIParts(object):
def __init__(self, so, uriparts, **kwargs):
self.so = so
self.uriparts = uriparts
for k,v in kwargs.items():
setattr(self,k,v)
def __getattr__(self,key):
return SOURIParts(self.so, self.uriparts+[key])
def __getitem__(self,key):
return self.__getattr__(key)
def __call__(self,**kwargs):
return "%s/%s"%(self.so.base_url,"/".join(self.uriparts))
class SO(object):
def __init__(self, base_url='http://api.stackoverflow.com/1.1'):
self.base_url = base_url
def __getattr__(self,key):
return SOURIParts(self, [])
def __getitem__(self,key):
return self.__getattr__(key)
I hope this helps.
You could override __getslice__(Python 2.7), or getitem()(Python3.x) and use a memorizing decorator so that if the slice you request (the userid) has already been looked up it would use cached results -- otherwise it could retrieve the results and populate the existing SO instance object.
However, I think a more OO way to solve the problem is make SO a pure lookup module that returns stack overflow user objects which would then have the deeper-digging lookups for profile details. But thats just me.

Categories

Resources