parameters inside f-string in a dictionary cannot update later - python-3.x

I encounter this issue, when I instantiate an Test object and call the main() method, it prints out
https://bunch/of/urls/with/None/
Although I am expecting to have
https://bunch/of/urls/with/1234/
It cannot update the self.id inside the dictionary f-string later inside the main function.
My expectation is when the dictionary value retrieves given the key, it will update the self.id as well while returning the value.
The reason I am expecting that because I call self.b_func(1234) before I call self.a_func() so it updates the self.id.
However, it is not updating the self.id inside the f-string. This behavior I don't understand why. What concept am I missing? How can I fix this?
class Test():
def __init__(self,id=None):
self.id = id
# Problem happens here
self.a_dict = {'a' : f'https://bunch/of/urls/with/{self.id}/'}
def a_func(self):
val = self.a_dict['a']
print(val)
# It prints
# https://bunch/of/urls/with/None/
# Not
# https://bunch/of/urls/with/1234/
def b_func(self, id):
self.id = id
return True
def main(self):
self.b_func(1234)
self.a_func()
if __name__ == '__main__':
a = Test()
a.main()

f-strings are evaluated right where they are defined. They do not magically update themselves when the values of the expressions inside change afterwards.
You can consider making a_dict a property instead so that its value would be dynamically generated:
class Test():
def __init__(self,id=None):
self.id = id
#property
def a_dict(self):
return {'a' : f'https://bunch/of/urls/with/{self.id}/'}
Demo: https://replit.com/#blhsing/UnnaturalElasticClasslibrary

Related

How can I invoke a functional_call using dictionary parameters passed to it?

My class Test below can have multiple functions, in this example, one of the functions is put_in_db(). My class expects a JSON object that will get converted to the dictionary and will be passed to invoke_func. Now, within invoke_func, how can I invoke different functions using the key functional_call and the parameters list using params. I have chosen params as a list so that the order is maintained. I cannot change put_in_db() function since this can be from an external class too.
class Test:
def __init__(self):
print("Test initialized")
def put_in_db(name, age):
print("put in db")
def invoke_func(self, my_dict):
print(my_dict)
function_call = my_dict.get('functional_call')
params = my_dict.get('params')
print(params)
'''
how to invoke put_in_db from here using function_call
and params
'''
if __name__ == "__main__":
T = Test()
data = {'functional_call': 'put_in_db', 'params': [{'name': 'Saf', 'age': '81'}]}
T.invoke_func(data)
You can use exec():
def invoke_func(self, my_dict):
print(my_dict)
function_call = my_dict.get('functional_call')
params = my_dict.get('params')
print(params)
exec(f'{function_call}({params})')
Note: exec() does have security vulnerabilities, as it will execute any code that is passed to it, so exercise caution when the user inputs something that is then executed.
class Test:
def __init__(self):
print("Test initialized")
def put_in_db(self,name, age):
print(f"put {name}, age {age}, in db")
def invoke_func(self, my_dict):
print(my_dict)
function_call = my_dict.get('functional_call')
params = my_dict.get('params')
print(params)
getattr(self,function_call)(**params)
if __name__ == "__main__":
T = Test()
data = {'functional_call': 'put_in_db', 'params': {'name': 'Saf', 'age': '81'}}
T.invoke_func(data)
getattr(some_object, att_name) will grab the attribute att_name from some_object, assuming it exists (if not it will give an AttributeError unless you also include a default). Then you can just invoke it by unpacking params as an argument.
I made two other changes to your code:
You left out self from your put_in_db method.
You made param a list containing a dict, but it's easier to work with if you just make it a dict.
You will probably want to add some error checks error checks for if/when someone passes in an string that doesn't correspond to an attribute.

Is it possible to make a class which lets you stack enum Flags?

I'd like to use named constants whereever possible instead of providing literal values or longish function signatures with a lot of boolean args.
Therefore i like pythons enum.Flag or enum.Enum.
More precisely, I would like to pass an argument to a function which holds a bit combination of enum.Flags. And i would like to avoid writing module.TheFlags.flagX for every set flag I would like to pass to the function. The flags should replace the boolean args.
I came up with following code:
import enum
class AvailableFlags(enum.Flag):
flag1 = enum.auto()
flag2 = enum.auto()
class FuncFlags:
def __init__(self):
self._flags = AvailableFlags(0)
#property
def flag1(self):
self._flags |= AvailableFlags.flag1
return self
#property
def flag2(self):
self._flags |= AvailableFlags.flag2
return self
def __str__(self):
return str(self._flags.value)
def func(setup_flags: FuncFlags):
print(setup_flags)
if __name__ == "__main__":
func(FuncFlags().flag1)
func(FuncFlags().flag2)
func(FuncFlags().flag1.flag2)
func(FuncFlags())
It creates instances of FuncFlags and then mis-uses the properties to set single flags returning the changed object itself.
However, one would expect that the property does NOT change object state.
Therefore, this is obviously not a clean solution despite that it works, though.
So, my question is, how this can be implemented in a clean, reusable way?
I'm not really clear on what you are trying to accomplish, but perhaps this helps?
import enum
class AvailableFlags(enum.Flag):
flag1 = enum.auto()
flag2 = enum.auto()
flag1, flag2 = AvailableFlag
def func(setup_flags: AvailableFlags):
print(setup_flags)
if __name__ == "__main__":
func(flag1)
func(flag2)
func(flag1|flag2)
func()
Meanwhile, I found an answer by adding another level of indirection.
I want to share it here if it is of interest for someone else.
Object state is maintained as every invokation of a flag creates a new instance from the current instance by setting an additional flag.
If we attempt to access an undefined flag an exception is raised (not shown).
import enum
class AvailableFlags(enum.Flag):
flag1 = enum.auto()
flag2 = enum.auto()
class FlagHelper:
def __init__(self, cls, value = 0):
self._cls = cls
self._flags = self._cls(value)
def __getattr__(self, item):
if item in self._cls.__members__:
return self.__class__(self._flags | getattr(self._cls, item))
getattr(self._cls, item) # Let attribute error pass through
def __str__(self):
return str(self._flags.value)
class FuncFlags(FlagHelper):
def __init__(self, value = 0):
super().__init__(AvailableFlags, value)
def func(setup_flags: FuncFlags):
print(setup_flags)
if __name__ == "__main__":
ff = FuncFlags()
func(ff.flag1)
func(ff.flag2)
func(ff.flag1.flag2)
func(ff)
Output:
1
2
3
0

PYTHON: How to access class object's members if the object is a key in a dictionary

I have the following class:
class car_class(object):
def __init__(self, mileage=11, tyre_size=11):
self.mileage = mileage
self.tyre_size = tyre_size
self.default_val = ''
def __hash__(self):
return hash((self.mileage, self.tyre_size))
def __getitem__(self, default_val):
return self.default_val
def __setitem__(self, default_val, mileage, tyre_size):
self[default_val] = str(mileage) + '_' + str(tyre_size)
def __eq__(self, other):
return (self.mileage, self.tyre_size) == (other.mileage, other.tyre_size)
def __str__(self):
return ('dict_cl: (tyre_size=\'%d\', mileage=\'%d\'' % (int(self.tyre_size), int(self.mileage)))
def __repr__(self):
return ('dict_cl: (tyre_size=\'%d\', mileage=\'%d\'' % (int(self.tyre_size), int(self.mileage)))
def __ne__(self, other):
# Not strictly necessary, but to avoid having both x==y and x!=y
# True at the same time
return not(self == other)
I also have a dictionary which takes the object of this class as the Key against a value as follows-
my_dict = dict()
dict_value_list = list()
mm_dict_cl = car_class()
mm_dict_cl.mileage = 29
mm_dict_cl.tyre_size = 265
dict_value_list.extend(['car_color'])
my_dict.update(mm_dict_cl = dict_value_list)
So the dictionary(my_dict)key has the class object(mm_dict_cl) as key and car_color as a value for this key. The Key is the class object itself having two attributes mileage and tyre_size.
Now when I print the following value of the dictionary I get the value as car_color as expected -
`>>>` print(my_dict[next(iter(my_dict))])
['car_color']
However I'm struggling to find a way to retrieve the properties of the class object.
>>>` print(next(iter(my_dict)))
mm_dict_cl
It prints the class name and the key type if printed as string.
`>>>` print(type(next(iter(my_dict))))
<type 'str'>
Query: How can then I access the object attributes of the key?
I want to check what is the value of tyre_size and mileage for a particular 'car_color' using the key of the dictionary my_dict
Please help, a novice here, trying to learn this language.
-edit: Fixed the extend call to the list for adding 'car_colour' as a list instead of a string as pointed by SorousH Bakhtiary.
The problem here is that you stored the name of the instance of your class as key in your dictionary("it is string"), so you have no control over the instance but because your object is defined in global namespace, you can retrieve it like this : (I simplified your class for the moment)
class car_class:
def __init__(self, mileage=11, tyre_size=11):
self.mileage = mileage
self.tyre_size = tyre_size
self.default_val = ''
my_dict = {}
dict_value_list = []
mm_dict_cl = car_class()
dict_value_list.append('car_color')
print(dict_value_list)
mm_dict_cl.mileage = 'temp1'
mm_dict_cl.tyre_size = 'temp2'
my_dict.update(mm_dict_cl=dict_value_list)
# here :
obj = globals().get(next(iter(my_dict)))
print(obj.mileage) # prints temp1
print(obj.tyre_size) # prints temp2
btw, you should have used append instead of extend method to add 'car_color'

Can we skip explicit object creation in Python

When I do not crate object for CP class, the operations are not captured. I am referring to the code below, Can somebody help me understand why we need obj creation in this case
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working")
CP().set_pre("test string added")
CP().execute()
It produces,
++++++++working
0
{0: 'test string added'}
[0]
+++++++not working
0
{}
[]
When you call the class the second time with CP.execute(), you have created a completely new instance of the CP class. It is not going to have the text string you specified.
If you actually wanted it to print the values like the working one you can make the functions return self after each call in the P class. If you did that you could do something like this.
from abc import ABC, abstractmethod
class P(ABC):
def __init__(self):
super().__init__()
self._pre_map = {}
self._pre_order = []
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
##need to return self here
return self
def execute(self):
pass
class CP(P):
def __init__(self):
super().__init__()
def execute(self):
self.prnt()
def prnt(self):
print (self._pre_map)
print (self._pre_order)
#Working
print("\n++++++++ working")
obj = CP()
obj.set_pre("test string added")
obj.execute()
#Not Working
print("\n+++++++ not working: but now working after returning self in the P class")
CP().set_pre("test string added").execute()
++++++++ working
0
{0: 'test string added'}
[0]
+++++++ not working: but now working after returning self in the P class
0
{0: 'test string added'}
[0]
This would print the result you want.
The reason for the difference is the fact that in the first one, you are creating an instance, and using that instance the whole way through, whereas in the second one, you are using two different instances of your class.
The two different instances cannot share their attributes, so you are unable to recall what happened. If you really don't want to use a dedicated variable, change your P class to look like this:
class P(ABC):
...
def set_pre(self, tag_value):
index = len(self._pre_map)
print(index)
self._pre_map[index] = tag_value
self._pre_order.append(index)
return self
...
And use CP().set_pre("test string added").execute()

Python - How to return a the details of each Room instance in a list, from inside of a Map instance

I am working on a small text adventure in python, and am attempting to use classes. I'm not very well versed in OOP and although I feel like I'm slowly gaining a greater understanding...I know that I still have a ways to go.
This is my room class
#!usr/bin/env python3
"""
A room class and a method to load room data from json files
"""
import json
class Room():
def __init__(
self,
id = "0",
name = "An empty room",
desc = "There is nothing here",
items = {},
exits = {},
):
self.id = id
self.name = name
self.desc = desc
self.items = items
self.exits = exits
def __str__(self):
return "{}\n{}\n{}\n{}".format(self.name, self.desc, self.items, self.exits)
# Create method to verify exits
def _exits(self, dir):
if dir in self.exits:
return self.exits[dir]
else:
return None
def north(self):
return self._exits('n')
def south(self):
return self._exits('s')
def east(self):
return self._exits('e')
def west(self):
return self._exits('w')
# Create method to get room info from json file
def get_room(id):
ret = None
with open("data/{}.json".format(str(id)) , "r") as f:
jsontext = f.read()
d = json.loads(jsontext, strict = False)
d['id'] = id
ret = Room(**d)
return ret
This is my map class
#!/usr/bin/env python3
from rooms import *
"""
Map class used to build a map from all of the rooms
"""
class Map():
def __init__(self, rooms = {}):
self.rooms = rooms
def __str__(self):
return map(str, rooms)
def build_map(id, num_of_rooms):
rooms = {}
room_count = 0
while room_count < num_of_rooms:
rooms[id] = get_room(id)
id += 1
room_count += 1
return rooms
a_map = Map(build_map(1, 3))
def test_map(map):
return map.rooms
print(test_map(a_map))
I'm not understanding why test_map only returns a list of objects, and was wondering how I might be able to receive the actual list of rooms so that I can confirm that they were created properly. I'm sure that I'm just going about this the COMPLETE wrong way...which is why I've come here with the issue.
For general information about __str__ and __repr__, Check out this answer.
In this case, here's what's happening:
Your __str__ function on Map doesn't return a string, it returns a map object. __str__ must return a string.
That's not causing an error because the function isn't getting called, here: test_map returns the given Map's rooms attribute, which is a dictionary. If you tried to do print(a_map) you'd get an exception.
Dictionaries have their own __str__ method, which is getting called here but the dictionary's __str__ method calls __repr__ on its members, which you haven't defined. (See the linked answer for details on why this is so.)
When you haven't defined a __repr__ for your class, you get the __repr__ from object, which is why your print(test_map(a_map)) gives you output like {1: <__main__.Room instance at 0x7fca06a5b6c8>, 2: <__main__.Room instance at 0x7fca06ac3098>, 3: <__main__.Room instance at 0x7fca06ac36c8>}.
What I'd suggest:
Write __repr__ functions for both Map and Room.
Have Map.__repr__ return a string that indirectly relies on Room.__repr__, something like the following:
def __repr__(self):
return '<Map: {}>'.format(self.rooms)
This isn't maybe the best long-term approach - what if you have a map with a hundred rooms? - but it should get you going and you can experiment with what works best for you.

Resources