Random.choice pulling entire list, instead of single items - python

I am (continually) working on a project with gmail and imaplib. I am searching gmail for emails that contain a specific word, and receiving a list of unique ids (most of my code is based off of/is Doug Hellman's, from his excellent imaplib tutorial). Those unique ids are saved to a list. I am trying to pull a single id from the list, but random.choice keeps pulling the entire list. Here is the code:
import imaplib
import random
from imaplib_list_parse import parse_list_response
c = imaplib_connect.open_connection()
msg_ids = []
c.select('[Gmail]/Chats', readonly=True)
typ, msg_ids = c.search(None, '(BODY "friend")')
random_id = random.choice(msg_ids)
print random_id
I've messed around in the interpreter and msg_ids is definitely a list. I've also attempted to pull specific elements of the array (ex: msg_ids[1] etc) but it says "IndexError: list index out of range" which I understand to mean "the thing you are looking for isn't there", which is confusing because it is there.
Is there anytime a list is not a list? Or something? I'm confused.
As always, I appreciate any feedback the wonderful people of stackoverflow could give :)

I think random_id is a list of list, something like: [[1,2,3,5]]。So when you call msg_ids[1], it raise IndexError. And since there is only one element in the list, random.choice() always return the element. You can try:
print random.choice(msg_ids[0])
To debug this kind of things, you can try print the random_id, or use IPython to interactive your code to find out the problem.

Related

I have the index of an item in a list, how do i find the item of that index?

Currently, I am trying to program an encoder/decoder for a custom cipher and I have an idea of how to encrypt the data. What I am doing is I am putting the user's message in a list, then getting the element with the index of 0, then encrypting that and printing all the characters at once. However, every site I search has the reverse problem: finding the index of an element in a list. Any ideas?
message = input()
uselist = list(message)
numofchar = len(message)
for i in range(0, numofchar):
(code for searching the list)
(some people were confused, so here is a roadmap)
user inputs message -> message is changed to list and length is determined -> for loop is used to check the index of 0 -> character is pulled from the list and translated
What you're asking is a bit confusing but there are two cases.
You know the index and looking for the message, then you use the ordinary Python way to access list items:
uselist[index]
You know the message and you want to find its index in the list, in this case, you can use the index() method:
uselist.index(message)
However, this method needs the message to be unique, or it will return the lowest index of its where it first occurred. And, in case the message is not found, it will raise an error.

Python- Insert new values into 'nested' list?

What I'm trying to do isn't a huge problem in php, but I can't find much assistance for Python.
In simple terms, from a list which produces output as follows:
{"marketId":"1.130856098","totalAvailable":null,"isMarketDataDelayed":null,"lastMatchTime":null,"betDelay":0,"version":2576584033,"complete":true,"runnersVoidable":false,"totalMatched":null,"status":"OPEN","bspReconciled":false,"crossMatching":false,"inplay":false,"numberOfWinners":1,"numberOfRunners":10,"numberOfActiveRunners":8,"runners":[{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":2.8,"size":34.16},{"price":2.76,"size":200},{"price":2.5,"size":237.85}],"availableToLay":[{"price":2.94,"size":6.03},{"price":2.96,"size":10.82},{"price":3,"size":33.45}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832765}...
All I want to do is add in an extra field, containing the 'runner name' in the data set below, into each of the 'runners' sub lists from the initial data set, based on selection_id=selectionId.
So initially I iterate through the full dataset, and then create a separate list to get the runner name from the runner id (I should point out that runnerId===selectionId===selection_id, no idea why there are multiple names are used), this works fine and the code is shown below:
for market_book in market_books:
market_catalogues = trading.betting.list_market_catalogue(
market_projection=["RUNNER_DESCRIPTION", "RUNNER_METADATA", "COMPETITION", "EVENT", "EVENT_TYPE", "MARKET_DESCRIPTION", "MARKET_START_TIME"],
filter=betfairlightweight.filters.market_filter(
market_ids=[market_book.market_id],
),
max_results=100)
data = []
for market_catalogue in market_catalogues:
for runner in market_catalogue.runners:
data.append(
(runner.selection_id, runner.runner_name)
)
So as you can see I have the data in data[], but what I need to do is add it to the initial data set, based on the selection_id.
I'm more comfortable with Php or Javascript, so apologies if this seems a bit simplistic, but the code snippets I've found on-line only seem to assist with very simple Python lists and nothing 'nested' (to me the structure seems similar to a nested array).
As per the request below, here is the full list:
{"marketId":"1.130856098","totalAvailable":null,"isMarketDataDelayed":null,"lastMatchTime":null,"betDelay":0,"version":2576584033,"complete":true,"runnersVoidable":false,"totalMatched":null,"status":"OPEN","bspReconciled":false,"crossMatching":false,"inplay":false,"numberOfWinners":1,"numberOfRunners":10,"numberOfActiveRunners":8,"runners":[{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":2.8,"size":34.16},{"price":2.76,"size":200},{"price":2.5,"size":237.85}],"availableToLay":[{"price":2.94,"size":6.03},{"price":2.96,"size":10.82},{"price":3,"size":33.45}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832765},{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":20,"size":3},{"price":19.5,"size":26.36},{"price":19,"size":2}],"availableToLay":[{"price":21,"size":13},{"price":22,"size":2},{"price":23,"size":2}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832767},{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":11,"size":9.75},{"price":10.5,"size":3},{"price":10,"size":28.18}],"availableToLay":[{"price":11.5,"size":12},{"price":13.5,"size":2},{"price":14,"size":7.75}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832766},{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":48,"size":2},{"price":46,"size":5},{"price":42,"size":5}],"availableToLay":[{"price":60,"size":7},{"price":70,"size":5},{"price":75,"size":10}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832769},{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":18.5,"size":28.94},{"price":18,"size":5},{"price":17.5,"size":3}],"availableToLay":[{"price":21,"size":20},{"price":23,"size":2},{"price":24,"size":2}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832768},{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":4.3,"size":9},{"price":4.2,"size":257.98},{"price":4.1,"size":51.1}],"availableToLay":[{"price":4.4,"size":20.97},{"price":4.5,"size":30},{"price":4.6,"size":16}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832771},{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":24,"size":6.75},{"price":23,"size":2},{"price":22,"size":2}],"availableToLay":[{"price":26,"size":2},{"price":27,"size":2},{"price":28,"size":2}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":12832770},{"status":"ACTIVE","ex":{"tradedVolume":[],"availableToBack":[{"price":5.7,"size":149.33},{"price":5.5,"size":29.41},{"price":5.4,"size":5}],"availableToLay":[{"price":6,"size":85},{"price":6.6,"size":5},{"price":6.8,"size":5}]},"sp":{"nearPrice":null,"farPrice":null,"backStakeTaken":[],"layLiabilityTaken":[],"actualSP":null},"adjustmentFactor":null,"removalDate":null,"lastPriceTraded":null,"handicap":0,"totalMatched":null,"selectionId":10064909}],"publishTime":1551612312125,"priceLadderDefinition":{"type":"CLASSIC"},"keyLineDescription":null,"marketDefinition":{"bspMarket":false,"turnInPlayEnabled":false,"persistenceEnabled":false,"marketBaseRate":5,"eventId":"28180290","eventTypeId":"2378961","numberOfWinners":1,"bettingType":"ODDS","marketType":"NONSPORT","marketTime":"2019-03-29T00:00:00.000Z","suspendTime":"2019-03-29T00:00:00.000Z","bspReconciled":false,"complete":true,"inPlay":false,"crossMatching":false,"runnersVoidable":false,"numberOfActiveRunners":8,"betDelay":0,"status":"OPEN","runners":[{"status":"ACTIVE","sortPriority":1,"id":10064909},{"status":"ACTIVE","sortPriority":2,"id":12832765},{"status":"ACTIVE","sortPriority":3,"id":12832766},{"status":"ACTIVE","sortPriority":4,"id":12832767},{"status":"ACTIVE","sortPriority":5,"id":12832768},{"status":"ACTIVE","sortPriority":6,"id":12832770},{"status":"ACTIVE","sortPriority":7,"id":12832769},{"status":"ACTIVE","sortPriority":8,"id":12832771},{"status":"LOSER","sortPriority":9,"id":10317013},{"status":"LOSER","sortPriority":10,"id":10317010}],"regulators":["MR_INT"],"countryCode":"GB","discountAllowed":true,"timezone":"Europe\/London","openDate":"2019-03-29T00:00:00.000Z","version":2576584033,"priceLadderDefinition":{"type":"CLASSIC"}}}
i think i understand what you are trying to do now
first hold your data as a python object (you gave us a json object)
import json
my_data = json.loads(my_json_string)
for item in my_data['runners']:
item['selectionId'] = [item['selectionId'], my_name_here]
the thing is that my_data['runners'][i]['selectionId'] is a string, unless you want to concat the name and the id together, you should turn it into a list or even a dictionary
each item is a dicitonary so you can always also a new keys to it
item['new_key'] = my_value
So, essentially this works...with one exception...I can see from the print(...) in the loop that the attribute is updated, however what I can't seem to do is then see this update outside the loop.
mkt_runners = []
for market_catalogue in market_catalogues:
for r in market_catalogue.runners:
mkt_runners.append((r.selection_id, r.runner_name))
for market_book in market_books:
for runner in market_book.runners:
for x in mkt_runners:
if runner.selection_id in x:
setattr(runner, 'x', x[1])
print(market_book.market_id, runner.x, runner.selection_id)
print(market_book.json())
So the print(market_book.market_id.... displays as expected, but when I print the whole list it shows the un-updated version. I can't seem to find an obvious solution, which is odd, as it seems like a really simple thing (I tried messing around with indents, in case that was the problem, but it doesn't seem to be, its like its not refreshing the market_book list post update of the runners sub list)!

Python chatbot - TypeError: list indices must be integers, not str

I've created a simple chatbot using Python, but when I try to use it, it gives me an error: TypeError: list indices must be integers, not str.
I'll get into the error later, but first I'll explain what the bot should do. The bot has a database, represented by a dictionary, in which all of the user's and bot's responses are stored.
First of all, it outputs "Hi!"
The user is asked for input.
Every output has some responses associated with it. The output is stored in keys in the dictionary, and responses are in a list, which is the key's value.
The bot's output is chosen randomly from the list of responses associated with the user's input.
If the input isn't in the dictionary, then it will be added. Also, the input will be echoed by the bot.
Repeat this forever.
(Sorry if the explanation was bad, but maybe if you keep reading you might understand.)
So, here is an example of what the bot should do.
BOT> Hi!
YOU> Hello!
BOT> Hello!
YOU> How do you do?
BOT> How do you do?
YOU> I'm fine, thanks.
BOT> I'm fine, thanks.
YOU> Hello!
BOT> How do you do?
YOU> I'm fine thanks.
BOT> Hello!
Here's the code I'm using (I excluded the parts that aren't needed)
import pickle
import random
class Bot:
def __init__(self, current, database, saveFile):
self.current = "Hi!"
self.database = []
def say(self, text):
print("BOT> " + text)
self.current = text
def evaluate(self, text):
if text in self.database:
self.say(random.choice(self.database[text]))
else:
self.database[text] = []
self.say(text)
bot = Bot("", {})
bot.say("Hi!")
while 1:
bot.evaluate(input("YOU> "))
And now, onto the problem I'm having.
When I try to talk to the bot, I get the error TypeError: list indices must be integers, not str. It was pointing to the line of code self.database[text] = [].
Here's an example:
BOT> Hi!
YOU> Hello!
(error)
I don't know what's going on, so I don't know what I should do to try and fix it. I thought the code would work right, but it doesn't... Could anybody give me a little help?
self.database is a list. List items are accessed by specifying their position within the list, for example self.database[0] for the first item and self.database[4] for the fifth item.
You're trying to use text as a list position, which makes no sense.
If you want to store items based on a text key instead of an integer position, use a dictionary instead of a list.
You mention the database is a dictionary, yet you're creating a list
self.database = []
A dict is created by curly braces
self.database = {}

Parsing multiple occurrences of an item into a dictionary

Attempting to parse several separate image links from JSON data through python, but having some issues drilling down to the right level, due to what I believe is from having a list of strings.
For the majority of the items, I've had success with the below example, pulling back everything I need. Outside of this instance, everything is a 1:1 ratio of keys:values, but for this one, there are multiple values associated with one key.
resultsdict['item_name'] = item['attribute_key']
I've been adding it all to a resultsdict={}, but am only able to get to the below sample string when I print.
INPUT:
for item in data['Item']:
resultsdict['images'] = item['Variations']['Pictures']
OUTPUT (only relevant section):
'images': [{u'VariationSpecificPictureSet': [{u'PictureURL': [u'http//imagelink1'], u'VariationSpecificValue': u'color1'}, {u'PictureURL': [u'http//imagelink2'], u'VariationSpecificValue': u'color2'}, {u'PictureURL': [u'http//imagelink3'], u'VariationSpecificValue': u'color3'}, {u'PictureURL': [u'http//imagelink4'], u'VariationSpecificValue': u'color4'}]
I feel like I could add ['VariationPictureSet']['PictureURL'] at the end of my initial input, but that throws an error due to the indices not being integers, but strings.
Ideally, I would like to see the output as a simple comma-separated list of just the URLs, as follows:
OUTPUT:
'images': http//imagelink1, http//imagelink2, http//imagelink3, http//imagelink4
An answer to your comment that required a bit of code to it.
When using
for item in data['Item']:
resultsdict['images'] = item['Variations']['Pictures']
you get a list with one element, so I recommend using this
for item in data['Item']:
resultsdict['images'] = item['Variations']['Pictures'][0]
now you can use
for image in resultsdict['images']['VariationsSpecificPictureSet']:
print(image['PictureUR‌​L'])
Thanks for the help, #uzzee, it's appreciated. I kept tinkering with it and was able to pull the continuous string of all the image URLs with the following code.
resultsdict['images'] = sum([x['PictureURL'] for x in item['variations']['Pictures'][0]['VariationSpecificPictureSet']],[])
Without the sum it looks like this and pulls in the whole list of lists...
resultsdict['images'] = [x['PictureURL'] for x in item['variations']['Pictures'][0]['VariationSpecificPictureSet']]

Random (well, apparently) "IndexError: list index out of range"

I am trying to write a programme that parses all the xml files within a single directory. The code seems to work OK, but sometimes a file is parsed without any trouble (when it is alone or when it is the first one to be parsed), sometimes parsing the same file returns ""IndexError: list index out of range"
from xml.dom.minidom import parse, parseString
import os
liste=open('oup_list_hybrid.txt','a')
for r,d,f in os.walk('C:/Users/bober/Documents/Analyse_citation_crossref/'):
for files in f:
if files.endswith(".xml"):
print files
dom=parse(files)
for element in dom.getElementsByTagName('record'):
rights = element.getElementsByTagName('dc:rights')
doi = element.getElementsByTagName('dc:identifier')
date= element.getElementsByTagName('dc:date')
try:
valeurrights=rights[0].firstChild.nodeValue
valeurdoi=doi[1].firstChild.nodeValue
valeurdate=date[0].firstChild.nodeValue
resultat=valeurrights+';'+valeurdoi+';'+valeurdate+'\n'
liste.write(resultat)
except IndexError:
print 'pb avec'+files
continue
break
liste.close()
What am I doing wrong here ?
Thanks in advance for any help !
Are you sure that rights, doi or date actually contain anything? If the getElementsByTagName doesn't find anything, these lists will be empty.
doi may also only contain one element, and you're trying to access the second doi[1].
Long story short, check your lists actually contain data before accessing it, or use a try-catch

Categories