How to select values of tuples inside tuples? - python

I have a tuple called params which included two other tuples. The tutorial I got this code from accesses the tuples inside the tuple with self.params.printlog. However, that is not working for me. Is there anything I'm missing?
class TestStrategy():
params = (
('maperiod', 15),
('printlog', False),
)
def log(self, txt, dt=None, doprint=False):
if self.params.printlog or doprint:
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
Description in the tutorial of what is done:
I thought the same, but it's not a dict and not a named tuple. It
would a bit unpractical to hardcode some of the values in the strategy
and have no chance to change them easily. Parameters come in handy to
help.
Definition of parameters is easy and looks like:
params = (('myparam', 27), ('exitbars', 5),)
Being this a standard Python tuple with some tuples inside it, the
following may look more appealling to some:
params = (
('myparam', 27),
('exitbars', 5),)

Use params[0][0] for accessing maperiod
And similarly, params[1][0] for printlog
Alternatively, you can also use a named tuple

Ok so there are three main data structures at play here.
There is a tuple, which is what you have in the code you've shown us:
params = (('maperiod', 15), ('printlog', False))
You have to use ints to access, like to get the 'printlog' value use params[1][1] and the maperiod value use params[0][1].
params[0][1] == 15
params[1][1] == False
There are dicts which is what is sounds like how you accessed the data
params = {'maperiod': 15, 'printlog': False}
Now we can access the data by key
params['maperiod'] == 15
params['printlog'] == False
Sometimes we want the best of both worlds, both a tuple and we can access by key. This sounds like what the example was using since we access with . notation. For that we use namedtuples.
from collections import namedtuple
params = namedtuple('Params', 'maperiod, printlog')(15, False)
and access by index or by attribute
params[0] == params.maperiod == 15
params[1] == params.printlog == False
What is confusing is that you've mentioned all three in different ways. I'd go back and look at the example to see which one they are using and follow that.
Edit: If what you do have is a tuple then it is very easy to convert into a dict or namedtuple for easier accessing. Just do:
dict_params = dict(params)
nt_params = namedtuple('Params', [p[0] for p in params])(*[p[1] for p in params])

Related

How do you programmatically pass in keywords to function in python?

I have some code I'm trying to refactor, which looks a bit like this in python 3:
# some_obj.query(streetaddress="burdon")
# some_obj.query(area="bungo")
# some_obj.query(region="bingo")
# some_obj.query(some_other_key="bango")
How I can DRY this up so I have something like this?
# define a list of tuples like so:
set_o_tuples = [
("streetaddress", "burdon")
("area", "bungo"),
("region", "bingo"),
("some_other_key", "bango"),
])
And then call it in a function
for key, val in set_o_tuples:
some_obj.query(key=val)
When I try to run this code, I get an exception like the following - as Python doesn't like keywords being passed in like this:
SyntaxError: keyword can't be an expression
What is the idiomatic way to DRY this up, so I don't have to repeat loads of code like the example above?
Update: sorry folks, I think the example I put together above missed a few important details. I basically have some pytest code like so
def test_can_search_by_location(self, db, docs_from_csv):
"""
We want to be able to query the contents of all the below fields when we query by location:
[ streetaddress, locality, region, postcode]
"""
search = SomeDocument.search()
locality_query = search.query('match', locality="some val")
locality_res = locality_query.execute()
region_query = search.query('match', region="region val")
region_query_res = region_query.execute()
postcode_query = search.query('match', postcode="postcode_val")
postcode_query_res = postcode_query.execute()
streetaddress_query = search.query('match', concat_field="burdon")
field_query_res = field_query.execute()
location_query = search.query('match', location=concat_value)
location_query_res = location_query.execute()
assert len(locality_query_res) == len(location_query_res)
assert len(region_query_res) == len(location_query_res)
assert len(streetaddress_query_res) == len(location_query_res)
assert len(postcode_query_res) == len(location_query_res)
I was trying to DRY up some of this, as there are similar examples I have, but after reading the comments, I've rethought it - the savings in space don't really justify the changes. Thanks for the pointers.
You could define a list of dictionaries instead, and then unpack them when calling the method in a loop:
list_of_dicts = [
{"streetaddress": "burdon"}
{"area": "bungo"},
{"region": "bingo"},
{"some_other_key": "bango"},
]
for kwargs in list_of_dicts:
some_obj.query(**kwargs)
Use dictionary unpacking
some_obj.query(**{key: val})
I wouldn't recommend what you're doing though. The original method is clean and obvious. Your new one could be confusing. I would keep it as is. This looks to be a poorly designed python API, some_obj.query should just take multiple keyword arguments in one function. You could make your own like this:
def query(obj, **kwargs):
# python 3.6 or later to preserve kwargs order
for key, value in kwargs.items():
obj.query(**{key: value})
And then call like so
query(some_obj, streetaddress='burdon', area='bungo', region='bingo', some_other_key='bango')

Dynamically creating a class

I have a function which returns me two lists, symbols and data where the corresponding values are with the same index. For example symbols[i] gives the variable name and data[i] gives the actual value (int).
I would like to use these two lists to dynamically create a class with static values of the following format:
class a:
symbols[i] = data[i]
symbols[i+1] = data[i+1]
and so on so that I could later refer to the values like this:
a.symbols[i]
a.symbols[i+1]
where symbols[i] and symbols[i+1] should be replaced with the wanted variable name, like a.var1 or a.var2
How could this be achieved?
Edit: added detail below
So I have a main program lets say def main() which should read in a list.dat of this style:
dimension1;0.1
dimension2;0.03
dimension3;0.15
and separate the values to symbols and data lists.
So I don't know how many values there are exactly in these lists. I want to create a class dynamically to be able to refer to the values in the main program and to give the class to sub functions as an argument like def sub1(NewClass, argument1, argument2) etc. At the moment I am using a manually created simple python list (list.py) of the following format:
dimension1 = 0.1
dimension2 = 0.03
dimension3 = 0.15
and then using from list import * in the main program and also in the sub functions, which causes a SyntaxWarning telling me that import * only allowed at module level. So what I actually want is a smart and consistent way of handling the parameters list and transferring it to another functions
You can create a class dynamically with type. If I understand what you want to achieve here, your code will look like:
my_classes = []
for i in range(0, len(data), 2):
my_classes.append(
type('A%d' % i, (), {'var1': data[i], 'var2': data[i+1]})
)
I suspect what you actually want, re-reading the description, is to use type as follows:
NewClass = type('NewClass', (object,), dict(zip(symbols, data)))
Given a minimal example:
>>> symbols = 'foo bar baz'.split()
>>> data = range(3)
The outcome would be:
>>> NewClass.foo
0
>>> NewClass.bar
1
>>> NewClass.baz
2
Using zip allows you to easily create a dictionary from a list of keys and a list of associated values, which you can use as the __dict__ for your new class.
However, it's not clear why you want this to be a class, specifically.

question about managing values with multiple subvalues in python 3k

I have a question reguarding how I would perform the following task in python.
(I use python 3k)
what I have are several variables which can yield further variables on top of those
and each of those have even more variables
for example:
a generic name would be
item_version_type =
where each part (item, version, and type) refer to different variables(here there are 3 for each)
item = item_a, item_b, item_c
version = range(1,3)
itemtype = itemtype_a, itemtype_b, itemtype_c
simply listing each name and defining it is annoying:
itema_ver1_typea =
itemb_ver1_typea =
itemc_ver1_typea =
itema_ver2_typea =
etc.
etc.
etc.
especially when I have something where one variable is dependent on something else
for example:
if value == True:
version = ver + 1
and to top it off this whole example is rather simply compared to what I'm actually
working with.
one thing I am curious about is using multiple "." type of classes such as:
item.version.type
I know that this can be done
I just can't figure out how to get a class with more than one dot
either that or if anyone can point me to a better method
Thanks for help.
Grouping of data like this can be done in three ways in Python.
First way is tuples:
myvariable = ('Sammalamma', 1, 'Text')
The second way is a dictionary:
myvariable = {'value': 'Sammalamma', 'version': 1, 'type': 'Text'}
And the third way is a class:
class MyClass(object):
def __init__(self, value, version, type):
self.value = value
self.version = version
self.type = type
>>> myvariable = MyClass('Sammalamma', 1, 'Text')
>>> myvariable.value
'Sammalamma'
>>> myvariable.version
1
>>> myvariable.type
'Text'
Which one to use in each case is up to you, although in this case I would claim that the tuple doesn't seem to be the best choice, I would go for a dictionary or a class.
None of this is unique to Python 3, it works in any version of Python.
In addition to #Lennart Regebro's answer if items are immutable:
import collections
Item = collections.namedtuple('Item', 'value version type')
items = [Item(val, 'ver'+ver, t)
for val in 'abc' for ver in '12' for t in ['typea']]
print(items[0])
# -> Item(value='a', version='ver1', type='typea')
item = items[1]
print(item.value, item.type)
# -> b typea
sorry for posting this here instead of the comments but I have no clue how to work the site here.
for clarification
what I need is basically to have be able to get an output of said such as where
I could take a broad area (item) narrow it further (version) and even further (type as in type of item like lets say types are spoon, knife, fork)
or a better description is like arm.left.lower = lower left arm
where I could also have like leg.left.lower
so I could have arm.both.upper to get both left and right upper arms
where a value would be assigned to both.
what I need is to be able to do truth tests etc. and have it return the allowable values
such as
if leg == True
output is --> leg.both.lower, leg.both.upper, leg.left.upper leg.right.upper, etc., etc., etc.
if upper == True
output is --> leg.both.upper, leg.left.upper, etc., etc., etc.
hopefully that helps
Basically I get how to get something like item.version but how do I get something
like item.version.type
I need to have it to be more specific than just item.version
I need to be able to tell if item is this and version is that then type will be x
like
item.version.type
if version == 3:
item.version = spoon.3.bent
#which is different from
if version == 2:
item.version.type = spoon.2.bent

Multiple values for key in dictionary in Python

What I'm trying to do is get 3 values from a key into separate variables. Currently I'm doing it like this:
for key in names:
posX = names[key][0]
posY = names[key][1]
posZ = names[key][2]
This doesn't seem very intuitive to me even though it works. I've also tried doing this:
for key, value in names:
location = value
Unfortunately, this gives me a single object (which is what I expected), but I need the individual values assigned to the key. Thanks and apologize for my newness to Python.
Update
Apologies for not specifying where I was getting my values from. Here is how I'm doing it for the first example.
names = {}
for name in objectNames:
cmds.select(name)
location = cmds.xform(q=True, ws=True, t=True)
names[name] = location
It's not unintuitive at all.
The only way to store "multiple values" for a given key in a dictionary is to store some sort of container object as the value, such as a list or tuple. You can access a list or tuple by subscripting it, as you do in your first example.
The only problem with your example is that it's the ugly and inconvenient way to access such a container when it's being used in this way. Try it like this, and you'll probably be much happier:
>>> alist = [1, 2, 3]
>>> one, two, three = alist
>>> one
1
>>> two
2
>>> three
3
>>>
Thus your second example could instead be:
for key, value in names.items():
posX, posY, posZ = value
As FabienAndre points out in a comment below, there's also the more convenient syntax I'd entirely forgotten about, for key,(posX,posY,posZ) in names.items():.
You don't specify where you're getting these values from, but if they're coming from code you have control over, and you can depend on using Python 2.6 or later, you might also look into named tuples. Then you could provide a named tuple as the dict value, and use the syntax pos.x, pos.y, etc. to access the values:
for name, pos in names.items():
doSomethingWith(pos.x)
doSomethingElseWith(pos.x, pos.y, pos.z)
If you don't mind an external dependency, you could include Werkzeug's MultiDict:
A MultiDict is a dictionary subclass customized to deal with multiple values for the same key which is for example used by the parsing functions in the wrappers.
>>> d = MultiDict([('a', 'b'), ('a', 'c')])
>>> d
MultiDict([('a', 'b'), ('a', 'c')])
>>> d['a']
'b'
>>> d.getlist('a')
['b', 'c']
>>> 'a' in d
True
Another way to store multiple values for a key is to use a container type, like a list, a set or a tuple.
Looking at your code, working with position/location variables, you could also unify the X, Y and Z position into a common type, for example using named tuples:
from collections import namedtuple
Position = namedtuple("Position", "x, y, z")
locations = {
"chair": Position(1, 2, 5.3),
"table": Position(5, 3.732, 6),
"lamp": Position(4.4, 7.2, 2)
}
print "Chair X location: ", locations["chair"].x
Just a suggestion, though.

What is the preferred technique to convert an object's properties to a sorted list of tuples?

I'm working with an open-source library and they define a class like so:
class Provider(object):
""" Defines for each of the supported providers """
DUMMY = 0
EC2 = 1
EC2_EU = 2
RACKSPACE = 3
SLICEHOST = 4
GOGRID = 5
VPSNET = 6
LINODE = 7
VCLOUD = 8
RIMUHOSTING = 9
I need to take the properties (DUMMY, EC2, etc.) and convert them to a sorted list of tuples that would look something like this:
[(0, 'DUMMY'), (1, 'EC2'), ...]
I want to sort on the name of the property itself. I've come up with a few ways to tackle this, including the following which seems like an inefficient way to handle this:
import operator
from libcloud.types import Provider
PROVIDER_CHOICES = [(v,k) for k, v in vars(Provider).items()
if not k.startswith('__')]
PROVIDER_CHOICES = sorted(PROVIDER_CHOICES, key=operator.itemgetter(1))
It works but seems inelegant and like there may be a better way. I also see flaws in the way I'm constructing the list by doing the if not k.startswith('__') - mainly what if the open-source lib adds methods to the Provider class?
Just looking for some opinions and other techniques that may work better for this.
If you are looking for class variables that are of the type integer, you could do it like this:
import inspect
PROVIDER_CHOICES = inspect.getmembers(Foo, lambda x: isinstance(x, int))
Check out the inspect module for more information.
As an aside: you can use PROVIDER_CHOICES.sort(key=...) in your last line, which does an inplace sort.
Edit: getmembers returns a sorted list as stated in the documentation so sorted is unnecessary (thanks J.F. Sebastian)
If you worry about methods and other types of attributes just filter them out too.
PROVIDER_CHOICES = [(v,k) for k, v in vars(Provider).iteritems()
if not k.startswith('_') and isinstance(v,int)]
PROVIDER_CHOICES.sort( key=itemgetter(1) )
You just have to run this once for every class in question, so speed shouldn't be a issue to begin with. If you really care, you can just store the list on the class itself.

Categories