Something like setter and getter for a python dictionary - python

I have the following problem:
A class contains a dict of dicts of dicts ... e.g.
class Try():
def __init__(self):
self._tryDict = {'first':{'b':{'bla':'x'},'c':1},'second':{'b':15,'c':1}}
#getter
def tryDict....
#tryDict.setter
def tryDict....
I would like to have now something like setter and getter to change a variable at a certain level of the dict and retrun the corresponding dict a view levels above (and only if i set a new value)
e.g.
try = Try()
try.tryDict['first']['b']['bla']
returs: 'x'
try.tryDict['first']['b']['bla']='z'
changes 'x' to 'z' and returns the content of try.tryDict['first'] ({'b':{'bla':'x'},'c':1})
To understand why I need this:
The dict is actually a list of file-praser-functions and the content is also stored as a dict.
i.e.
dict = {'file-1':dict-1, 'file-1':dict-1, 'file-1':dict-1, ...}
with dict['file-1']['content-3']=1 I set the new content
and with dict['file-1'].write(), the content of the file is updated in the file. The dict is a class instance and I would like to immediately wirte the change to the file.
many thx for your help!
BR, maths

OK so what I implemented it now this way:
Each file got setter and getter function
class dicts():
.....
# to show the content
#getter
def tryDict(self):
return tryDict.file
# to sett some content
#tryDict.setter
def tryDict(self, entry):
if len(entry)==2:
name, newEntry = entry
tryDict.file[name]= newEntry
if len(entry)==3:
....
if i whan to get the entry:
tryDicis = dicts()
tryDicis.trydict # shows the dictionary
i can now set in different levels like:
tryDicis = dicts()
tryDicis.trydict = (name-level-1, name-level-2,..., newEntry)
This is maybe not the most elegant way, however it is easy to implement and good enough in my case.
BR, maths

Related

How to create a new object in the .connect function with pyqt?

I want to create a new object of the Class Lamp with the name of the clicked item.
self.listWidget_lamps.clicked.connect(Hue = Lamp(name_item))
Since this is not working, I would like to now what the right way looks like and how I need to inherit the Lamp class to the class Ui_MainWindow(object):.
Here is my code (issue line 52):
https://pastebin.com/rjg96kuJ
Here is the init of the class Lamps:
class Lamp:
"""Class to control phillips hue lamps/groups"""
def __init__(self, name: str):
self.name = name
with open("data.json", "r") as self.file:
self.data = json.load(self.file)
self.brightness = self.data["lamps"][self.name]["brightness"]
self.xy = self.data["lamps"][self.name]["color"]
The input into ...cliked.connect() has to be a function - something that can be called with a pair of brackets. So Hue = ... will not work. Instead, use a lambda function or a function you have defined. Also note that to get the name of an item you should use ...itemClicked.connect() not ...clicked.connect() which passes the item clicked as a parameter to the function. This is (I believe) the shortest way of doing it, although very unreadable and not recommended:
self.listWidget_lamps.itemClicked.connect(lambda item: globals().update({"Hue": Lamp(item.text())}))
This is the recommended way:
Hue = None
def new_lamp(item):
global Hue
Hue = Lamp(item.text())
self.listWidget_lamps.itemClicked.connect(new_lamp)

Can dynamically created class methods know their 'created' name at runtime?

I have a class which I want to use to extract data from a text file (already parsed) and I want do so using dynamically created class methods, because otherwise there would be a lot of repetitive code. Each created class method shall be asociated with a specific line of the text file, e.g. '.get_name()' --> read a part of 0th line of text file.
My idea was to use a dictionary for the 'to-be-created' method names and corresponding line.
import sys
import inspect
test_file = [['Name=Jon Hancock'],
['Date=16.08.2020'],
['Author=Donald Duck']]
# intented method names
fn_names = {'get_name': 0, 'get_date': 1, 'get_author': 2}
class Filer():
def __init__(self, file):
self.file = file
def __get_line(cls):
name = sys._getframe().f_code.co_name
line = fn_names[name] # <-- causes error because __get_line is not in fn_names
print(sys._getframe().f_code.co_name) # <-- '__get_line'
print(inspect.currentframe().f_code.co_name) # <-- '__get_line'
return print(cls.file[line][0].split('=')[1])
for key, val in fn_names.items():
setattr(Filer, key, __get_line)
f = Filer(test_file)
f.get_author()
f.get_date()
When I try to access the method name to link the method to the designated line in the text file, I do get an error because the method name is always '__get_line' instead of e.g. 'get_author' (what I had hoped for).
Another way how I thought to solve this was to make '__get_line' accepting an additional argument (line) and set it by passing the val during 'the setattr()' as shown below:
def __get_line(cls, line):
return print(cls.file[line][0].split('=')[1])
and
for key, val in fn_names.items():
setattr(Filer, key, __get_line(val))
however, then Python complains that 1 argument (line) is missing.
Any ideas how to solve that?
I would propose a much simpler solution, based on some assumptions. Your file appears to consist of key-value pairs. You are choosing to map the line number to a function that processes the right hand side of the line past the = symbol. Python does not conventionally use getters. Attributes are much nicer and easier to use. You can have getter-like functionality by using property objects, but you really don't need that here.
class Filer():
def __init__(self, file):
self.file = file
for line in file:
name, value = line[0].split('=', 1)
setattr(self, name.lower(), value)
That's all you need. Now you can use the result:
>>> f = Filer(test_file)
>>> f.author
'Donald Duck'
If you want to have callable methods exactly like the one you propose for each attribute, I would one-up your proposal and not even have a method to begin with. You can actually generate the methods on the fly in __getattr__:
class Filer():
def __init__(self, file):
self.file = file
def __getattr__(self, name):
if name in fn_names:
index = fn_names[name]
def func(self):
print(self.file[index][0].split('=', 1)[1])
func.__name__ = func.__qualname__ = name
return func.__get__(self, type(self))
return super().__getattr__(name)
Calling __get__ is an extra step that makes the function behave as if it were a method of the class all along. It binds the function object to the instance, even through the function is not part of the class.
For example:
>>> f = Filer(test_file)
>>> f.get_author
<bound method get_author of <__main__.Filer object at 0x0000023E7A247748>>
>>> f.get_author()
'Donald Duck'
Consider closing over your keys and values -- note that you can see the below code running at https://ideone.com/qmoZCJ:
import sys
import inspect
test_file = [['Name=Jon Hancock'],
['Date=16.08.2020'],
['Author=Donald Duck']]
# intented method names
fn_names = {'get_name': 0, 'get_date': 1, 'get_author': 2}
class Filer():
def __init__(self, file):
self.file = file
def getter(key, val):
def _get_line(self):
return self.file[val][0].split('=')[1]
return _get_line
for key, val in fn_names.items():
setattr(Filer, key, getter(key, val))
f = Filer(test_file)
print("Author: ", f.get_author())
print("Date: ", f.get_date())

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.

Python - Assigning attributes to a class instance using a for loop and a list

Hi folks I am experimenting with Python (I found pygame.org and wanted to play around) and I am trying to read some settings from a configuration file. I want to be able to change stats on the fly. (So if I wanted to change how hard a fighter hits or how fast a wizard runs then I'd be able to do that.) I was hoping to be able to read from a list and create an attribute for each instance in the list basically this:
for stat in Character.stats:
self.stat = parser.get(self.char_class, stat)
What ends up happening is there is an object with an attribute names 'stat' that contains the last value assigned. What I would LIKE to happen is to have an attribute created for each item in the list, and then get assigned the related value from the config file.
here is more code for context:
class Character(object):
stats = ["level_mod",
"power",
"speed",
"hit",
"evade",
"magic",
"stamina",
"magic_defense",
"intelligence"]
def __init__(self, name, rpg_id):
self.name = name
self.rpg_id = rpg_id
self.__setStats()
def __setStats(self):
parser = SafeConfigParser()
parser.read('char_config.cfg')
for stat in Character.stats:
self.stat = parser.get(self.char_class, stat)
Thanks for your time!
You can use, setattr:
for stat in Character.stats:
setattr(self, stat, parser.get(self.char_class, stat))
Or manually access dict
for stat in Character.stats:
self.__dict__[stat] = parser.get(self.char_class, stat))
You want setattr(obj, attrname, value)
You better re-design that part of the game by adding a Stats class.
class Stats:
STATS = ["level_mod",
"power",
"speed",
"hit",
"evade",
"magic",
"stamina",
"magic_defense",
"intelligence"]
def __init__(self, conf_file=None):
self.__stats = {}
if conf_file is not None:
self.loads_stats_from_file(conf_file)
def load_stats_from_file(self, conf_file):
"""
Here add the pairs <stat_name>:<value>
to the self.__stats dict. For that just parse the config
file like before.
"""
pass
def get_stat(self, stat_name):
return self.__stats[stat_name]
def set_stat(self, stat_name, value):
self.__stats[stat_name] = value
Then you can add a Stats instance to your Character.
class Character(object):
def __init__(self, name, rpg_id):
self.stats = Stats("char_config.cfg")
self.name = name
self.rpg_id = rpg_id
This way you improve usability and decouple the Stats and Character logics. And besides, your problem is reduced from "Adding attributes to an object" to "Adding items to a dictionary".

python: Using the choice box to get data besides getString()

I have defined an object that has several attribute..
class thing(object):
def __init__(self, type, name, attrA, attrB, attrC):
self.type = type
self.name = name
self.attrA = attrA
self.attrB = attrB
self.attrC = attrC
lets say then I have a list of things
self.things=[thing('car','fred',1,2,3),
thing('car','george',a,b,c),
thing('truck','bob',6,7,8),
thing('truck','tom',x,y,z)
]
I then populate a choice box with SOME of the items from that list
for each in self.things:
if each.type == 'car':
self.choiceCar.Append(item=each.name)
When the user selects Bob from the dropdown I have an event for that
def EvtChoice(self,event):
self.Name = event.GetString()
This captures the name of the selection, but how do I get the other attributes? What I am currently doing is
for each in self.items:
if self.Name == each.name
#Get other things here
My thought is that if my list grows large then this loop through my entire list will become very inefficient and really unneeded since the user has already selected the specific item I want. What I think I should be able to do is to get the index of the selected item, but im not sure how to do that, or even if that is the correct way to go about it.
Associating data or objects with wx.Choice or wx.ComboBox is pretty easy. You can see an example using the latter here:
http://www.blog.pythonlibrary.org/2010/12/16/wxpython-storing-object-in-combobox-or-listbox-widgets/
The basic idea is to pass an empty list to the control's constructor and then iterate over the objects and Append them to the control. So something like this:
for obj in self.things:
self.choiceCar.Append(obj.name, obj)
Then in the event handler for the widget, you can get the object back by doing this:
obj = self.choiceCar.GetClientData(self.choiceCar.GetSelection())

Categories

Resources