How to pass a reference to a value inside a dictionary? [duplicate] - python

This question already has answers here:
Passing an integer by reference in Python
(13 answers)
Closed 8 months ago.
Suppose we have a dictionary:
some_dict = {
"first": 11111,
"second": {
"second_1": 22222,
"second_2": 232323,
"second_3": 99999
}
}
if I create a new variable a with value some_dict["second"], then it will be a reference:
ref = some_dict["second"]
Accordingly, if I change the value of the nested dictionary second in some_dict, then it will also change in ref:
some_dict["second"]["second_1"] = "not a second_1"
print(ref)
Output:
{'second_1': 'not a second_1', 'second_2': 232323, 'second_3': 99999}
How can I create the same reference, but not to a nested dictionary, but to a value, such as int or str?
Like this:
ref = some_dict["second"]["second_1"]
some_dict["second"]["second_1"] = "not a second_1"
print(ref)
Estimated output:
not a second_1

The quick-and-dirty answer is to put it into a one-element list.
my_original_int = [0]
my_ref_to_int = my_original_int
Now modifications to (the integer value inside) either variable will reflect in the other.
But, that's horrible coding practice. At minimum, that should be encapsulated into a class.
class Cell:
def __init__(self, value):
self.value = value
my_original_int = Cell(0)
my_ref_to_int = my_original_int
Then either can be accessed or modified through the .value member.
Even better would be to make a specific class to your use case. Ask yourself why you're wanting to alias integers. "My integer represents data in a JSON file somewhere" is a great answer, and it suggests that there should be a MyJsonData class somewhere that has integer fields, and you should be passing around references to that instead of to the integers.
Basically, make your data usage as explicit as possible, and your code will be more readable and intuitive. I even go so far as to try to avoid aliasing lists and dictionaries. If I find myself needing such behavior, it's often about time for me to encapsulate that list in a class with a more specific name and detailed docstring.

Related

Python Linking Variable Values

I want to to store a reference to a class object inside a dictionary or another class that will maintain the same value as the actual class object. Currently, I'm using a string of the class object and the eval() function. I know this isn't a proper solution but haven't found another fix.
curWeapon is a object of WeaponInfo with pveFlag and pvpFlag variables
ListSetting stores the boolean expression of curWeapon along with a,b,c...
wishLists is passed to the main class of the program which reads a file and changed the pve/pvpFlags
When a empty line is read the boolean expression is evaluated with the new flags
wishLists.append(ListSetting("curWeapon.pveFlag or not curWeapon.pvpFlag", a, b, c, d))
My only idea is making a new function that has separates the boolean expression from a,b,c.. when making the ListSetting and adding that separately. Although I'm not sure if wishLists would update the firstFlag, secondFlag... variables.
firstFlag = ListSetting(a,b,c,d)
wishLists.append(firstFlag)
def wishListFlags():
firstFlag.flags = curWeapon.pveFlag or not curWeapon.pvpFlag
secondFlag.flags = ""
...
I'm pretty sure that updating the index of wishLists would work but would need a bunch of if statements or a dictionary.
firstFlag = ListSetting(a,b,c,d)
wishLists.append(firstFlag)
flagExpressions = {
1 : curWishListcurWeapon.pveFlag or not curWeapon.pvpFlag,
2 : "",
...}
def wishListFlags():
for index in len(wishLists):
wishLists[index].flags = flagExpressions.get(index)
If anyone knows a better way to go about this please let me know. Also, if my examples aren't specific enough or are confusing I'd be happy to share my entire program, I didn't know if it would be too much.
To store an expression you use a function, which you later call to get the value of the expression.
flagExpressions = {
1: lambda: curWishListcurWeapon.pveFlag or not curWeapon.pvpFlag
2: lambda: ""
}
def wishListFlags():
for index in len(wishLists):
wishLists[index].flags = flagExpressions.get(index, lambda: None)()

Is there a pythonic way to log a function with extra values without bloating its paramter list?

I am currently struggling to log a function in python in a clean way.
Assume I want to log a function which has an obscure list_of_numbers as argument.
def function_to_log(list_of_numbers):
# manipulate the values in list_of_numbers ...
return some_result_computed_from_list_of_numbers
When the values in list_of_numbers are manipulated in the above function, I would like to log that change, but not with the value in the obscure list_of_numbers, but the value at the same index in a second list list_with_names_for_log.
The thing that annoys me: now I also have to input list_with_names_for_log, which bloats the argument list of my function,
def function_to_log(list_of_numbers, list_with_names_for_log):
# do some stuff like, change value on 3rd index:
list_of_numbers[3] = 17.4
log.info('You have changed {} to 17.4'.format(list_with_names_for_log[3]))
# and so on ...
return some_result_computed_from_list_of_numbers
I use multiple of these lists exclusively for logging in this function.
Has anybody an idea how to get this a little bit cleaner?
Provided it makes sense for the data to be grouped, I'd group the name/data pairs in a structure. What you currently have is essentially "parallel lists", which are typically a smell unless you're in a language where they're your only option.
A simple dataclass can be introduced:
from dataclasses import dataclass
#dataclass
class NamedData:
name: str
data: int
Then:
def function_to_log(pairs):
pairs[3].data = 17.4
log.info('You have changed {} to 17.4'.format(pairs[3].name))
# and so on ...
return some_result_computed_from_list_of_numbers
As a sample of data:
pairs = [NamedData("Some Name", 1), NamedData("Some other name", 2)]
And if you have two separate lists, it's simple to adapt:
pairs = [NamedData(name, data) for name, data in zip(names, data_list)]
Only do this though if you typically need both bits in most places that each list is used. Grouping will only help clean up the code if the name and data are both needed in most places that either is used. Otherwise, you're just introducing overhead and bloat elsewhere to clean up a few calls.

Recasting Python Variable

In python I have a variable set as a string which is a username:
self.loggedInUser = "Hanna"
When I have retrieved the user details as a list, I would like to reuse the variable:
self.loggedInUser = (0, "Hanna", "hash", "UID")
Is it possible to do this, probably the more important, is it bad practice?
H
Python lets you change the value of a variable at any time, so it's certainly possible to change the value of self.loggedInUser from a string to a list or tuple, or any other kind of object.
This can be confusing though, and could cause errors in your code if it's expecting one kind of object and finds a different kind, so it's better to split them up, say as self.loggedInUserName and self.loggedInUser.
It is possible, python variables can change their type.
About bad practice... There are no rules for this. Personally I think that it can cause many problems of getting a user into a function and not knowing if it is a full user or only a name. Why not a dictionary? This way the user will always be a dictionary, but unretrieved properties just won't be there.
By the way, in the line you wrote it is not a list but a tuple (tuple uses round brackets - (), list uses square ones - [])
In Python variables are not forced as one type, variables are reference to some object and those references if changed are still references. So changing variables from one object to other is not changing this objects but references.
These operations are only on refernces:
variable1 = 'ala'
variable1 = []
variable1 = {}
These operations are on values of objects that variables are referencing too (inplace):
variable1 ='ala'
variable1 +='o' #variable1 -> 'alao'
So in Your case when changing :
self.loggedInUser = "Hanna"
to
self.loggedInUser = (0, "Hanna", "hash", "UID")
You are only working on references...
May as well do:
self.loggedInUser = "Hanna"
tp=type(self.loggedInUser)
if tp==str:
self.loggedInUser = (0, self.loggedInUser, "hash", "UID")
elif tp==tuple:
self.loggedInUser = (0, self.loggedInUser[1], "hash", "UID")
else:
print 'sorry, something went wrong...'
Unused object with overwriten reference will become garbage. In Cython you could define types of variables with cdef.
Dictionaries and lists are nice too...
Here is example of auto vivication dictionary:
class vividict(dict):
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
With this You could do:
self.loggedInUsers=vividict()
self.loggedInUsers['user0']['name']='Hanna'
self.loggedInUsers['user0']['key1']='val1'
self.loggedInUsers['user0']['key2']='val2'
self.loggedInUsers['user0']['key3']='val3'
...

python select a function based on the value in a dictionary [duplicate]

This question already has answers here:
Calling a function of a module by using its name (a string)
(18 answers)
Closed 6 years ago.
I want to select a function based on the value of a dictionary:
dict = {"func_selector":"func1", "param_value":"some_value"}
# defined a function
def func1(param):
# some function code
Now, I want to select the function based on the value of some key, so that it can achieve something like:
# calling a function based on some dict value
dict["func_selector"](dict["param_value"])
The syntax is probably wrong, but I am wondering if it is possible to do that in Python or something similar.
Try storing the value of the function in the dictionary, instead of its name:
def func1(param):
print "func1, param=%r" % (param,)
d = {"func_selector":func1, "param_value": "some value"}
Then you can say:
>>> d['func_selector'](d['param_value'])
func1, param='some value'
The best approach IMO is do it like this
def func1(param):
#code
some_value = ... #The value you need
my_dict = {"func_selector": func1, "param_value": some_value }
And then
my_dict["func_selector"](my_dict["param_value"])
Now, if you only have the name of the function you need to call getattr
And call it
getattr(my_class, my_dict["func_selector"])(my_dict["param_value"])
my_class is the class which contains the method. If it's not in a class I think you can pass self

How to access the name of a string converted into a variable? [duplicate]

This question already has answers here:
How do I create variable variables?
(17 answers)
Closed 8 years ago.
I'm working with Python in FreeCAD and to add properties (adjustable variables) to an object we use:
obj.addProperty("App::PropertyFloat","variable","variable label","variable explanation").variable = 2
Using a for loop I want to add multiple properties with multiple variable names. I read how to convert a string into a variable but I didn't find how to access that variable name. In my case: how to use it at the .variable position in my code above.
Say I have the code:
varName = []
for i in range(3):
varName.append('variable%s' %i)
exec("%s = %d" % (varName[i],2))
I know for example that I can use print variable0 to get the value 2. How can I access the name of variable0 and put it on the .variable position? I read about creating a list or dictionary of variables but you can only access the value of the variable, not its name right?
A list can be the input for this object and its contents should be assigned to the variable names. The output should look like this (but than suitable for any length of the input list):
obj.addProperty("App::PropertyFloat","variable0,"variable label","variable explanation").variable0 = input[0]
obj.addProperty("App::PropertyFloat","variable1,"variable label","variable explanation").variable1 = input[1]
etc.
I'm no expert on FreeCAD so this is something of a guess, but..., it looks like addProperty takes a name, type and description for a variable and creates one with a default value for you. The examples show creation / update in one step, like in your example. The problem is what to do when you don't know the name of the variable in advance. In python, you handle "variable attribute names" with getattr and setattr. So, if you have a name and a value you can:
name = 'some_name'
value = 1
setattr(obj.addProperty("App::PropertyFloat", name, "variable label",
"variable explanation"), name, value)
There are various ways to generate variable names. In your example,
for i in range(3):
setattr(obj.addProperty("App::PropertyFloat", "variable%d" % i, "variable label",
"variable explanation"), name, i)
Or maybe you already have them in a dict somewhere
for name, value in mydict.items():
setattr(obj.addProperty("App::PropertyFloat", name, "variable label",
"variable explanation"), name, value)
I am really puzzled by the FreeCAD property implementation. The property object should have a single well-known name to access it's value, not some tricky name-of-the-property thing.
In FreeCAD dynamic properties added from python are exposed as standart python instance attributes. Therefore you can use the normal hasattr, getattr and setattr etc. Hence your loop could look like this:
varName = []
for i in range(3):
varName = 'variable' + str(i)
obj.addProperty("App::PropertyFloat", varName, "awesome label","best property ever")
setattr(obj, varName, 1.234)
value = getattr(obj, varname)
Thnx for you answer! However, for some reaseon the first example you showed doesn't assign a value to the generated variable0, variable1 and variable2. Using the dictionary worked! When I'm inserting for example a list of objects into the obj:
dict = {}
for i in range(len(objectList)):
dict['object%d'%i] = objectList[i]
for name, value in dict.items():
setattr(obj.addProperty("App::PropertyPythonObject", name, "object label", "object explanation"), name, value)
I think this can be useful to make assemblies, for example a loft that consist of a user specified amount of (2D) profiles.

Categories

Resources