I have two methods here that don't seem to want to talk to eachother. I think the way I use my dictionary is slightly wrong and I've probably confused myself a bit:
def load(self, msg):
loadState = {'foo1' { 'FirstObject':1, 'SecondObject':2 }, 'foo2' { 'FirstObject':3, 'SecondObject':4 }}
foo_objects = loadState.keys()
for name in foo_objects:
if name == 'foo1':
ValueTuple = loadState[foo_objects[0]]
elif name == 'foo2':
ValueTuple = loadState[foo_objects[1]]
self.program.supervisor.setI2c( ValueTuple, foo = name ) #This is where I think it goes wrong
Then to store these values, I pass them over to this method which was working previously, put it doesn't like the new method above:
def setI2c( self, ValueTuple, foo=None ) :
for name in foo :
object = self.objects[name]
for name in ValueTuple :
register = reg(name)
register.value = regNameValueTuple[name]
EDIT: Part where I went wrong:
self.program.supervisor.setI2c( ValueTuple, foo = [name] )
Your load message can be simplified to:
def load(self, msg):
loadState = {'foo1' { 'FirstObject':1, 'SecondObject':2 }, 'foo2' { 'FirstObject':3, 'SecondObject':4 }}
for name, value in foo_objects.iteritems():
self.program.supervisor.setI2c(value, foo=name)
Here, foo is a string, one of the keys in loadState.
You then loop over foo in setI2c, where foo is still as string. That means you are iterating over the individual characters:
>>> foo = 'foo1'
>>> for name in foo:
... print name
...
f
o
o
1
You don't need that loop at all, you most likely wanted this:
def setI2c(self, value_dict, foo=None):
object = self.objects[foo]
for name in value_dict:
register = reg(name)
register.value = regNameValueTuple[name]
but you don't actually use object anywhere in your code, so that line could be removed altogether.
If setI2c() always expects foo to be a list of names, then you should pass in a list in load:
self.program.supervisor.setI2c(value, foo=[name])
Related
I am working with an API that is returning a response that contains fields like this:
{
"0e933a3c-0daa-4a33-92b5-89d38180a142": someValue
}
Where the field name is a UUID that changes depending on the request (but is not included in the actual request parameters). How do I declare that in a dataclass in Python? It would essentially be str: str, but that would interpret the key as literally "str" instead of a type.
I personally feel the simplest approach would be to create a custom Container dataclass. This would then split the dictionary data up, by first the keys and then individually by the values.
The one benefit of this is that you could then access the list by index value instead of searching by the random uuid itself, which from what I understand is something you won't be doing at all. So for example, you could access the first string value like values[0] if you wanted to.
Here is a sample implementation of this:
from dataclasses import dataclass
#dataclass(init=False, slots=True)
class MyContainer:
ids: list[str]
# can be annotated as `str: str` or however you desire
values: list[str]
def __init__(self, input_data: dict):
self.ids = list(input_data)
self.values = list(input_data.values())
def orig_dict(self):
return dict(zip(self.ids, self.values))
input_dict = {
"0e933a3c-0daa-4a33-92b5-89d38180a142": "test",
"25a82f15-abe9-49e2-b039-1fb608c729e0": "hello",
"f9b7e20d-3d11-4620-9780-4f500fee9d65": "world !!",
}
c = MyContainer(input_dict)
print(c)
assert c.values[0] == 'test'
assert c.values[1] == 'hello'
assert c.values[2] == 'world !!'
assert c.orig_dict() == input_dict
Output:
MyClass(values=['test', 'hello', 'world !!'], ids=['0e933a3c-0daa-4a33-92b5-89d38180a142', '25a82f15-abe9-49e2-b039-1fb608c729e0', 'f9b7e20d-3d11-4620-9780-4f500fee9d65'])
So i have code like this
varName = 'test'
varContents = 'something'
My question is:
How do i create a variable with the name of the contents of varName, having contents containing the contents of varContents?
Generally you'll want to avoid doing this - especially if you are dealing with user inputs, but you can use the exec() function. It runs a string passed into it as code, so you can do something like:
varName = 'test'
varContents = 'something'
exec(f"{varName} = '{varContents}'")
print(test)
A better way of storing data with a dynamic key is with a dict like this:
myDict = {}
varName = 'test'
varContents = 'something'
myDict[varName] = varContents
print(myDict[varName])
you can create variables like this :
locals()['newVar'] = "local variable"
print (newVar)
globals()['newGlobalVar'] = "global variable"
print (newGlobalVar)
so you could do this :
locals()[varName] = varContents
# Or
globals()[varName] = varContents
In Swift, you can define #dynamicMemberLookup (see documentation) to get direct access to properties that are nested inside another type. Is there a Python equivalent?
Example of what I want to achieve with Python
Let's say I have a class with members, e.g.:
c = OuterClass()
c.inner_class = ClassWithManyMembers()
c.inner_class.member1 = "1"
c.inner_class.member2 = "2"
c.inner_class.member3 = "3"
I would like to be able to get/set those members without having to type the inner_class every time:
print(c.member1) # prints "1"
c.member1 = 3
print(c.member1) # prints "3"
Example in Swift (Source):
Dynamic member lookup by member name
#dynamicMemberLookup
struct DynamicStruct {
let dictionary = ["someDynamicMember": 325,
"someOtherMember": 787]
subscript(dynamicMember member: String) -> Int {
return dictionary[member] ?? 1054
}
}
let s = DynamicStruct()
// Use dynamic member lookup.
let dynamic = s.someDynamicMember
print(dynamic)
// Prints "325"
Dynamic member lookup by key path
struct Point { var x, y: Int }
#dynamicMemberLookup
struct PassthroughWrapper<Value> {
var value: Value
subscript<T>(dynamicMember member: KeyPath<Value, T>) -> T {
get { return value[keyPath: member] }
}
}
let point = Point(x: 381, y: 431)
let wrapper = PassthroughWrapper(value: point)
print(wrapper.x)
My only idea in Python would be to monkey-patch all nested properties directly to the outer class.
I would advise against nesting classes in one another, but if you must do it, try this:
class MetaOuter(type):
def __getattr__(cls, attr):
for member in cls.__dict__.values():
if hasattr(member, attr):
return getattr(member, attr)
raise AttributeError(attr)
def __setattr__(cls, attr, value):
for member in cls.__dict__.values():
if hasattr(member, attr):
setattr(member, attr, value)
return
super().__setattr__(attr, value)
class Outer(metaclass=MetaOuter):
a = 0
class Inner:
x = 1
y = 2
Now any attributes of a nested class inside Outer are available (and can be written to) as an attribute of Outer:
>>> Outer.x, Outer.y
(1, 2)
>>> Outer.a # Accessing regular attributes still works as usual
0
>>> Outer.x = True
>>> Outer.Inner.x
True
If you need to nest more than one level, use the same meta class for any inner encapsulating classes:
class Outer(metaclass=MetaOuter):
a = 0
class Inner(metaclass=MetaOuter):
x = 1
y = 2
class Innerer:
z = 42
>>> Outer.a, Outer.x, Outer.y, Outer.z
(0, 1, 2, 42)
>>> Outer.z = -1
>>> Outer.z
-1
Note: Be aware that if you're trying to access an attribute that is found in multiple nested classes, you can't be sure of which class the attribute will come from. A more predictable implementation in this case would be to handle some kind of key path that will be looked up, but that's essentially the same as what Python provides by default (e.g., Outer.Inner.Innerer.z).
Generally, you can just save a reference to the inner object when you want to make repeated accesses to it.
c = OuterClass()
c.inner_class = ClassWithManyMembers()
ic = c.inner_class
print(ic.member1)
print(ic.member2)
print(ic.member3)
ic.member1 = "5"
When a user enters a name (e.g. "Jim") as an argument for an instance of my "Test" class, the def find function is called and for-loops through all the names in the dict matching "Jim". If def find finds the key word "Jim" in the dict, then it should print out the corresponding value. But when I run the code it just says "None". What do I need to change so that invoking def find results in the print statement 'worked'??
class Test(object):
def __init__(self, x=0): # error in (def find)?
self.x = x
c = None # error while looping in the for loop?
users = {
'John': 1,
'Jim': 2,
'Bob': 3
}
def find(self, x): # The user is supposed to type in the name "x"
for self.c in self.users: # it goes through the dictionary
if x == self.users[self.c]: # If x is equal to key it prints worked
print('worked')
else:
pass
beta = Test()
print(beta.find('Jim'))
#nk001,
I think this is a little more like what you are trying for:
class Test(object):
def __init__(self, x=0):
self.x = x # <-- indent the __init__ statements
users = { # <-- users = {
'John': 1, # KEY: VALUE,
'Jim': 2, # KEY: VALUE,
'Bob': 3 # KEY: VALUE,
} # }
def find(self, x): # <-- The user passes the "x" argument
for i in self.users: # <-- Now it goes through the dictionary
if x == i: # <-- If ARGV('x') == KEY
return 'worked' # <-- Then RETURN 'worked'
else:
pass
beta = Test()
print(beta.find("Jim"), beta.users["Jim"])
There's a couple different ways to get the 'worked' msg and the corresponding Value printed, this is just an example to demonstrate accessing the dict[KEY] to get the VALUE.
Also, I'm just assuming you meant an if/else block, and not a for/else? Indentation is critical w/Python. Also, your original script was returning None because there was no explicit return in your for loop - hence, when the function is called in the printing statement print(beta.find('Jim')) when the function finishes it returns nothing ("None"). Hope that helps!
I write a worked code:
class Test(object):
users = {
'John': 1,
'Jim': 2,
'Bob': 3
}
def __init__(self, x=0): # So I don't get an error in (def find)
self.x = x
def find(self, x): # The user is suppose to type in the name "x"
for name in Test.users.keys(): # it goes through the dictionary
if x == name: # If x is equal to key it prints worked
print('worked', self.users[name])
else:
pass
beta = Test()
beta.find('Jim')
You don not need the self.c.
The users is a class variable, you need to visit it by Test.users.
Your names is stored as the keys of the dict. So you need to get them by Test.users.keys()
The statement print(beta.find('Jim')) will print the return value of the find. But you don't return a value manually, you will get a None in your output.
Let's assume that we the following ndb model:
class MyModel(ndb.Model):
x = ndb.StringProperty()
y = ndb.StringProperty()
z = ndb.StringProperty(repeated=True)
We have a method that creates a query for the above model, executes it and fetch the results. However, we want this query to be modified my other functions. Specifically, we have the following:
def method_a():
qry = MyModel.query()
values = {'query':qry}
method_b(**values)
entities = qry.fetch()
def method_b(**kwargs):
k = ['a', 'b', 'c']
qry = kwargs['query']
qry.filter(MyModel.z.IN(k))
The problem is that the Query object is immutable, and thus it cannot be modified by method_b. Also, based on the specific architecture of the code, we cannot have method_b to return the new Query to method_a.
Any ideas on how to achieve the aforementioned functionality in another way??
Update: Please check the architecture of my code as presented below:
First, in a configuration file we specify a list of modules and if they are enabled or not. These modules affect the filters of the query we want to execute.
testparams = {
'Test1': True,
'Test2': True,
'Test3': False,
'Test4': True
}
Then, we have a method somewhere in the code that makes a query after the appropriate modules have been executed. Thus, it seems like this:
def my_func():
qry = MyEntity.query()
# modules
query_wrapper = [qry]
values = {'param':'x', 'query_wrapper':query_wrapper} #other values also
execute_modules(**values)
# get query and add some more things, like ordering
entities = query_wrapper[0].fetch()
The execute_modules function is the following:
def execute_modules(**kwargs):
for k in config.testparams:
if config.testparams[k]:
if kwargs['param'] == 'x':
(globals()[k]).x(**kwargs)
elif kwargs['param'] == 'y':
(globals()[k]).y(**kwargs)
Finally, an indicative module is similar to the following:
class Test1():
#classmethod
def x(cls, *args, **kwargs):
qry = kwargs['query_wrapper'][0]
# do some stuff like adding filters
kwargs['query_wrapper'][0] = qry
Any proposals to modify this architecture to a better approach?
I'm not aware of a way to do this without having method_b either return or change a referenced parameter. You should use a technique to pass a variable by reference, like passing a class with parameters.
You can pass in the args in a refrence object such as a dict/list:
def modify_query(kwargs):
kwargs['qry'] = kwargs['qry'].filter(MyModel.z.IN(k))
qry = MyModel.query()
kwargs = {'qry': qry}
modify_query(kwargs)
result = kwargs['qry'].fetch()
It should be noted that this is an extremly dirty way to accomplish what you want to accomplish. Similarly, if you pass in a list with say one object, then you can modify the contents of said list (through assignment) to modify the object:
def modify_query(list_object):
list_object[0] = list_object[0].filter(...)
You can do some hack for replace it object by other. For example:
def f(args):
qry = args[0]
qry_new = qry.filter(Model.a == 2)
args[0] = qry_new
qry = Model.query()
args = [qry]
f(args)
qry = args[0]