I have a method in my Python code that returns a tuple - a row from a SQL query. Let's say it has three fields: (jobId, label, username)
For ease of passing it around between functions, I've been passing the entire tuple as a variable called 'job'. Eventually, however, I want to get at the bits, so I've been using code like this:
(jobId, label, username) = job
I've realised, however, that this is a maintenance nightmare, because now I can never add new fields to the result set without breaking all of my existing code. How should I have written this?
Here are my two best guesses:
(jobId, label, username) = (job[0], job[1], job[2])
...but that doesn't scale nicely when you have 15...20 fields
or to convert the results from the SQL query to a dictionary straight away and pass that around (I don't have control over the fact that it starts life as a tuple, that's fixed for me)
#Staale
There is a better way:
job = dict(zip(keys, values))
I'd say that a dictionary is definitely the best way to do it. It's easily extensible, allows you to give each value a sensible name, and Python has a lot of built-in language features for using and manipulating dictionaries. If you need to add more fields later, all you need to change is the code that converts the tuple to a dictionary and the code that actually makes use of the new values.
For example:
job={}
job['jobid'], job['label'], job['username']=<querycode>
This is an old question, but...
I'd suggest using a named tuple in this situation: collections.namedtuple
This is the part, in particular, that you'd find useful:
Subclassing is not useful for adding new, stored fields. Instead, simply create a new named tuple type from the _fields attribute.
Perhaps this is overkill for your case, but I would be tempted to create a "Job" class that takes the tuple as its constructor argument and has respective properties on it. I'd then pass instances of this class around instead.
I would use a dictionary. You can convert the tuple to a dictionary this way:
values = <querycode>
keys = ["jobid", "label", "username"]
job = dict([[keys[i], values [i]] for i in xrange(len(values ))])
This will first create an array [["jobid", val1], ["label", val2], ["username", val3]] and then convert that to a dictionary. If the result order or count changes, you just need to change the list of keys to match the new result.
PS still fresh on Python myself, so there might be better ways off doing this.
An old question, but since no one mentioned it I'll add this from the Python Cookbook:
Recipe 81252: Using dtuple for Flexible Query Result Access
This recipe is specifically designed for dealing with database results, and the dtuple solution allows you to access the results by name OR index number. This avoids having to access everything by subscript which is very difficult to maintain, as noted in your question.
With a tuple it will always be a hassle to add or change fields. You're right that a dictionary will be much better.
If you want something with slightly friendlier syntax you might want to take a look at the answers this question about a simple 'struct-like' object. That way you can pass around an object, say job, and access its fields even more easily than a tuple or dict:
job.jobId, job.username = jobId, username
If you're using the MySQLdb package, you can set up your cursor objects to return dicts instead of tuples.
import MySQLdb, MySQLdb.cursors
conn = MySQLdb.connect(..., cursorclass=MySQLdb.cursors.DictCursor)
cur = conn.cursor() # a DictCursor
cur2 = conn.cursor(cursorclass=MySQLdb.cursors.Cursor) # a "normal" tuple cursor
How about this:
class TypedTuple:
def __init__(self, fieldlist, items):
self.fieldlist = fieldlist
self.items = items
def __getattr__(self, field):
return self.items[self.fieldlist.index(field)]
You could then do:
j = TypedTuple(["jobid", "label", "username"], job)
print j.jobid
It should be easy to swap self.fieldlist.index(field) with a dictionary lookup later on... just edit your __init__ method! Something like Staale does.
Related
EDIT BELOW
I read a lot of discussion about dynamic variable in python and a lot have shown that they are things that you generally shouldn't do. However I have a case that I really can't think of a way to avoid it (and hence why I am here)
I am currently reading in a text file that stores the information of different members of a store. Each members have their own information: like their phone number, email, points in their accounts, etc. I want to create a class and objects that stores this information. Don't worry this is just a part of an assignment and they are not real people. Here is the sample code:
class Member:
def __init__(self, name, phoneNumber, email, points):
self.name = name
self.phoneNumber = phoneNumber
self.email = email
self.points = points
self.totalPointsSpent = 0
#There are methods below that will return some calculated results, like total points
spent or their ranking. I will not show those here as they are irrelevant
And for each member in the file will be read in and create an object out of it. For example if there are 5 members in that file, five objects will be created, and I want to name them member1, member2, etc. However, this will be confusing when accessing it, as it will be hard to tell them apart, so I add them to a dictionary, with their memberID as the key:
dictonary[memberID] = member1
dictonary[memberID] = member2 #and so on
which would result in a dictionary that look like something like this:
dictionary = {'jk1234':member1,'gh5678':member2,...#etc}
This is the interesting thing about python is the fact that the dictionary value does not need to be a value, it can be an object. This is something new to me, coming from Java.
However, here is the first problem. I do not know how many member are there in a file, or I should say, the number of members in the file varies from file to file. If the number is 5, I need 5 variables; if there are 8, I need 8, and so on. I have thought of using a while loop, and the code will look something like this:
a = len(#the number of members in the file.)
i = 0
while i <= a:
member + i = Member(#pass in the information)
but the '+' operator only works for strings when combining names, not for identifiers. And thus cannot work (I think).
Many solution I read has indicated that I should use a dictionary or a list in such case. However, since my variables are pointing towards a class object, I cannot think of a way to use list/dictionary as the implementation.
My current solution is to use a tuple to store the members information, and do the calculations elsewhere (i.e. I took out the class methods and defined them as functions) so that the dictionary looks like this:
dictionary = {'jk1234': (name, phoneNumber, email, points),'gh5678':(name, phoneNumber, email, points),...#etc}
However given the amount of information I need to pass in it is less than ideal. My current algorithm works, but I want to optimized it and make it more encapsulated.
If you made this far I appreciate it, and would like to know if there is a better algorithm for this problem. Please excuse me for my less than a year of Python experience.
EDIT: Okay I just discovered something really interesting that might seems basic to experienced Python programmers. That is you can pass the values inside the dictionary, instead of naming a variable exclusively. For my problem, that would be:
dictionary = {'jk1234': Member(name, phoneNumber, email, points),'gh5678':Member(name, phoneNumber, email, points),...#etc}
#and they are retrievable:
dictionary['jk1234'].get_score() #get_score is a getter function inside the class
And it would return the proper value. This seems to be a good solution. However, I would still love to hear other ways to think about this problem
It looks like you are on the right track with your update.
It's not clear to me if your MemberID is a value for each member, but if it is, you can use it in a loop creating the objects
d = {}
for member in members_file:
d[MemberID] = Member(name, phoneNumber, email, points)
This, of course, assumes that MemberID is unique, otherwise you will overwrite existing entries.
You could also use a list instead
l = []
for member in members_file:
l.append(Member(name, phoneNumber, email, points))
Further, you could also do the above with list/dict comprehensions which are basically just condensed for-loops.
Dict-comprehension
d = {MemberID: Member(name, phoneNumber, email, points) for member in members_file}
List-comprehension
l = [Member(name, phoneNumber, email, points) for member in members_file]
Whether a list or a dictionary makes more sense, is up to your use-case.
Also note that the above assumes various things, e.g. on what form you get the data from the file, since you did not provide that information.
You can create a dictionary with a for loop:
d = {}
for i in the_members_in_the_file:
d[i] = Member(your, parameters, that\'s, inputted)
You will get your expected dictionary.
Im currently uniquifying a list of objects based on their name attribute by creating a dict of objects with the name value as the dict key like this:
obj_dict = dict()
for obj in obj_list:
if not obj.name in obj_dict:
obj_dict[obj.name] = obj
new_obj_list = obj_dict.items()
And I was wondering if there was a quicker or more pythonic way to do this.
If two objects with the same name should always considered identical you could implement __eq__ and __hash__ accordingly. Then your solution would be as easy as storing all your objects in a set():
new_obj_list = list(set(obj_list))
Converting the list back to a set is probably not even necessary since the order is lost anyway so unless you need to do something with it that only works with a list but not with a set just keep using the set.
And if you need ordering:
oset = set()
new_obj_list = []
for o in obj_list:
if o not in oset:
oset.add(o)
new_obj_list.append(o)
I would also go the set approach but then figured you want to be able to look-up by name I guess..., but here's another approach that doesn't require amending the class... (albeit a bit more expensive)... Note that you could add further sort parameters to sorted to order by other priorities (such as age, or gender etc...)
from operator import attrgetter
from itetools import groupby
unique_by_name = {key: next(item) for key, item in groupby(attrgetter('name'), sorted(obj_list, key=attrgetter('name')))}
There's a unique last seen recipe in itertools too.
Otherwise for different ordering requirements, put them as different class methods, (or one called 'sort' that takes a known order and calls an internal function)...
So I have a dict passed from a web page. I want to build the query dynamically based on the dict. I know I can do:
session.query(myClass).filter_by(**web_dict)
However, that only works when the values are an exact match. I need to do 'like' filtering. My best attempt using the __dict__ attribute:
for k,v in web_dict.items():
q = session.query(myClass).filter(myClass.__dict__[k].like('%%%s%%' % v))
Not sure how to build the query from there. Any help would be awesome.
You're on the right track!
First thing you want to do different is access attributes using getattr, not __dict__; getattr will always do the right thing, even when (as may be the case for more convoluted models) a mapped attribute isn't a column property.
The other missing piece is that you can specify filter() more than once, and just replace the old query object with the result of that method call. So basically:
q = session.query(myClass)
for attr, value in web_dict.items():
q = q.filter(getattr(myClass, attr).like("%%%s%%" % value))
I'm using the couchdb.mapping in one of my projects. I have a class called SupportCase derived from Document that contains all the fields I want.
My database (called admin) contains multiple document types. I have a type field in all the documents which I use to distinguish between them. I have many documents of type "case" which I want to get at using a view. I have design document called support with a view inside it called cases. If I request the results of this view using db.view("support/cases), I get back a list of Rows which have what I want.
However, I want to somehow have this wrapped by the SupportCase class so that I can call a single function and get back a list of all the SupportCases in the system. I created a ViewField property
#ViewField.define('cases')
def all(self, doc):
if doc.get("type","") == "case":
yield doc["_id"], doc
Now, if I call SupportCase.all(db), I get back all the cases.
What I don't understand is whether this view is precomputed and stored in the database or done on demand similar to db.query. If it's the latter, it's going to be slow and I want to use a precomputed view. How do I do that?
I think what you need is:
#classmethod
def all(cls):
result = cls.view(db, "support/all", include_docs=True)
return result.rows
Document class has a classmethod view which wraps the rows by class on which it is called. So the following returns you a ViewResult with rows of type SupportCase and taking .rows of that gives a list of support cases.
SupportCase.view(db, viewname, include_docs=True)
And I don't think you need to get into the ViewField magic. But let me explain how it works. Consider the following example from the CouchDB-python documentation.
class Person(Document):
#ViewField.define('people')
def by_name(doc):
yield doc['name'], doc
I think this is equivalent to:
class Person(Document):
#classmethod
def by_name(cls, db, **kw):
return cls.view(db, **kw)
With the original function attached to People.by_name.map_fun.
The map function is in some ways analogous to an index in a relational database. It is not done again every time, and when new documents are added the way it is updated does not require everything to be redone (it's a kind of tree structure).
This has a pretty good summary
ViewField uses a pre-defined view so, once built, will be fast. It definitely doesn't use a temporary view.
So, I'm writing something and I've come into a roadblock on how to do it (and what is the proper way of doing things). SO, explaining the situation will help be better understand the problem, and hopefully someone will know the answer :) Here it goes:
Basically, I'm writing up some dynamic forms in Python (more specifically Django) - I am using a form factory to generate the form that I want to make. This is all fine and dandy, so far I have been defining the properties of the form in a hardcoded way, basically matching a property to a certain form (a ChoiceField, a Boolean, etc). BUT, what I would like instead of hardcoding these values is essentially create a properties dictionary dynamically, based on what information I pass it...
I basically have an array of "options", and so here are the two methods I am considering:
Have a function for my "options" model/object that will have a series of if/elses. Like:
def get_property():
if value = "artifact": #artifact being one option
return form.BooleanField(label="blah")
else if value = "environment": #environment being another type of option
return form.ChoicesField(label="blah")
etc...
Use a very polymorphic approach. In this way, I mean creating an object based on my "option" object, and will create a new object based on the option. Say maybe something like:
class Base_Property():
value = ""
def __init__(self, option):
value = form.BooleanField()
class Artifact_Property(Base_Property):
def __init__(self, option):
Base_Property.__init__(self, option)
value = form.ChoiceField(choices=some_choices_array())
If option two is the way to go, could someone explain how I can create an object dynamically based on a variable? Like, matching the name of the value (say, Artifact, to match Artifact_Property).
Thanks so much for the help! I am really interested to see what happens to be a proper way - maybe it will spark a debate :)
-Shawn
Have you considered using a dictionary? They're excellent for this sort of conditional.
def get_option(field_type):
options = {
'artifact': forms.BooleanField,
'environment': forms.Choice Field,
}
return options[field_type](label='blah')