Referencing a variable after separating it from a list - python

input = "12345_usa_wool_10x10_100_80.jpg"
def CleanData(input):
data = input.split('_')
sku = data[0]
country = data[1].capitalize()
material = data[2].capitalize()
size = data[3]
retail_price = data[4]
sale_price = data[5]
CleanData(input)
print (sku)
print (country)
I'm getting
NameError: name 'sku' is not defined
I tried to store all the values in the list I created to easily reference back to them later.
Such as if they wanted total savings I could then later make something like
def Saving()
total_saving = retail_price-sale_price
return total_saving
So I can later have a final output of something like:
print (sku+" "+country+" "+sale_price+" "+Saving())
I'm a beginner and self-learner so I figured this isn't too hard of a problem but I don't know how to have sku,country,material, etc. be able to be referenced publicly.

sku is defined in the CleanData function only, it does not have scope outside of that function.
I'd recommend using a dict object instead. E.g.,
def parseData(input):
data = input.split('_')
d = {}
d['sku'] = data[0]
d['country'] = data[1].capitalize()
d['material'] = data[2].capitalize()
d['size'] = data[3]
d['retail_price'] = data[4]
d['sale_price'] = data[5]
return d
myData = parseData(input)
print(myData['sku'])
print(myData['country'])
You can also directly construct the dict:
def parseData(input):
data = input.split('_')
d = {'sku': data[0],
'country': data[1].capitalize(),
'material': data[2].capitalize(),
'size': data[3],
'retail_price': data[4],
'sale_price': data[5]}
return d

What you have is python scope, sku, and all the other variables assigned in the function, are local to the function. If you want them to be global, then mark them as such:
def CleanData(input):
data = input.split('_')
global sku
sku = data[0]
.... and so on
However, it is generally not a good idea to use global variables in this way. We want our functions to be encapsulated so that they can be used in many programs. The problem with using globals is that you have to know that the name is not going to be used for something else in the program. If you need to reuse the code across programs then that will break.
The solution is to return some sort of container. For example it could be a dictionary, a list, or a tuple. Here is an example using a tuple:
def CleanData(input):
data = input.split('_')
# sku, country, material, size, retail_price, sale_price
return (data[0],
data[1].capitalize(),
data[2].capitalize(),
data[3],
data[4],
data[5])
sku, country, material, size, retail_price, sale_price = CleanData(input)
print (sku)
print (country)
You could probably simplify this further by just returning data. In addition you might wish to test len(data) to ensure you have the correct number of fields.

Related

Having a problem making a redundant dictionary into a function

I am having a trouble converting a really redundant dictionary into a function (def)
The original code that works just fine is:
Pen = (9,'always','monday')
Paper = (1,'always','tues')
PriceDic = {'Pen': Pen[0],
'Paper': Paper[0]}
while True:
name = input("name of the product?")
print(PriceDic.get(name),'dollar')
break
which prints as...
>>>name of the product?Pen
>>>9 dollar
but the problem is
I have not only Pen, and Paper but probably another 100-200 more tuples to write
and each tuple needs to contain multiple information... so the final goal of this program is to be able to fetch various info from the tuple indexes and print them.
so
I thought maybe I could function and wrote this code...
def FindPriceFunction(x):
Pen = (9,'always','monday')
Paper = (1,'always','tuesday')
FindPriceDic = { x : x[0]}
print(FindPriceDic.get(x),'dollar')
while True:
name = input("name of the product?")
FindPriceFunction(name)
break
which gave me...
>>>name of the product?Pen
>>>P dollar
PLEASE HELP ME
You are trying to use the string x to access the variable name which won't work the way you're expecting, as x holds the value 'Pen', for example. This is not necessarily a recommended approach, but you can use the locals function to dynamically get the value of the variable like this:
def FindPriceFunction(x):
Pen = (9,'always','monday')
Paper = (1,'always','tuesday')
print(locals()[x][0],'dollar')
while True:
name = input("name of the product?")
FindPriceFunction(name)
break
Here, locals returns a dictionary of locally-defined variables, and you can use the string x as a key to access the value of the variable. So locals()['Pen'] will give you the value (9,'always','monday')
However, it would be better (and safer) to just store your tuples directly into a dictionary somewhere, or maybe in a file that you read from if you don't want a long block in your code, and then access the data through there like you were originally trying, except you can store the whole tuple rather than just the price and then access the first element of the tuple for the price. locals returns a dictionary of the variable name as the key and the variable value as the value, so it's essentially accomplishing what you could just do in the first place with storing the value in a dict
If you wanted to store this all in a JSON file for example, since you will have hundreds of dicts, you could do this:
JSON file:
{
"Pen": [9, "always", "monday"],
"Paper": [1, "always", "tuesday"]
}
Code:
import json
with open('prices.json', 'r') as f:
prices = json.load(f)
def FindPriceFunction(x):
print(prices[x][0], 'dollar')
As you gave:
FindPriceDic = { x : x[0]}
print(FindPriceDic.get(x),'dollar')
and called the function with x as Pen, it prints x[0] = 'Pen'[0] = 'P'. This caused you the problem. So try:
def FindPriceFunction(x):
FindPriceDic = {}
FindPriceDic['Pen'] = (9,'always','monday')
FindPriceDic['Paper'] = (1,'always','tuesday')
print(FindPriceDic.get(x)[0],'dollar')
while True:
name = input("name of the product?")
FindPriceFunction(name)
break
You could write your data as a dictionary from the beginning (the values in a dictionary can be any type, a tuple is fine):
my_data = {
'Pen': (9, 'always', 'monday'),
'Paper': (1, 'always', 'tuesday')
}
def FindPriceFunction(x):
print(my_data.get(x)[0],'dollar')
while True:
name = input("name of the product?")
FindPriceFunction(name)
break
In case you would need to be more flexible with your data structure, you can also rely on semantic keys, rather than operate with indexes of tuples.
products.yaml
Pen:
price: 9
frequency: Always
day: Monday
Paper:
price: 1
frequency: Rarely
day: Tuesday
Scissors:
frequency: Often
main.py
import yaml # pyyaml package
with open("products.yaml", 'r') as stream:
try:
products = yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)
print(products)
print(products['Pen']['price'])
def find_price(product_name):
try:
return products[product_name]['price']
except KeyError:
return "N/A"
print(find_price('Pen'))
print(find_price('Scissors'))

How to get id(address) of each value in list in python like pointer C?

First of all, I'm not good at English, sorry.
I'm trying to get id of list in python, to make each variable get filtered value by locale function.
Please check out the code below.
dayLow ="123124"
dayHigh = "200000"
volume = "21512542"
marketCap = "235136346137"
toLocale = [dayLow, dayHigh, volume, marketCap]
afterLocale = list()
def locale(inform_data):
inform_data = f"{inform_data:,}"
return inform_data
for item in toLocale:
item = locale(int(item))
afterLocale.append(item)
i = 0
while(i < afterLocale):
id(toLocale[i]) = afterLocale[i]
i += 1
and it's not working, and spit error out like that.
File "c:\******\test.py", line 19
id(toLocale[i]) = afterLocale[i]
^
SyntaxError: cannot assign to function call
So the question is
How can each variable in toLocale(dayLow, dayHigh, ...) gets the value filtered by function locale()?
You can't assign to a variable by using it's id() - that's not what id() is for.
If you want to dynamically assign variables to values, you can use exec to do so.
But I WOULD HIGHLY RECOMMEND NOT DOING SO.
Just type out the extra code required. It'll save you a lot of headache in the long run
dayLow ="123124"
dayHigh = "200000"
volume = "21512542"
marketCap = "235136346137"
toLocale = {'dayLow': dayLow, 'dayHigh': dayHigh, 'volume': volume, 'marketCap': marketCap}
def locale(inform_data):
inform_data = f"{inform_data:,}"
return inform_data
for var_name, var_value in toLocale.items():
exec(f'{var_name} = "{locale(int(var_value))}"')
print(dayLow, dayHigh, volume, marketCap)
Output:
123,124 200,000 21,512,542 235,136,346,137
I'm not sure if this is what exactly you want, but if you are trying to translate the values, using a dictionary would be maybe better for your usecase:
items = {
"dayLow": 123124,
"dayHigh" 200000,
"volume": 21512542,
"marketCap": 235136346137,
}
# now you can use `locale` for the keys:
for name, value in items.values():
# `name` will have a value of "dayLow", "dayHigh" etc...
print(locale(name), value)

how to fix 'int' object is not subscriptable error?

So I'm getting the error in the title when trying to print out a value from a sublist
for record in data:
(product_id, name, price) = record
accumulator = 0
order_items = ''
if 'order_1' in form:
print('<tr><td>%s</td><td>%s</td>' % (name[0], price[0]))
accumulator += int(price[0])
order_items += 'item 1 (check menu), '
I just want the output to be a print of those values, while adding on the the accumulator variables, any help would be much appreciated.
The reason is this part:
for record in data:
(product_id, name, price) = record
You're probably expecting product_id, name and price to be the "columns" corresponding to sub-collections in data, but that is not how it works. Instead, they just take the value of the last sub-collectiong in data.
You need something like this, which turns data into a nested collection where each sub-collection can be treated as a single column:
product_id, name, price = zip(*data)
The iterative equivalent:
product_id = []
name = []
price = []
for record in data:
product_id.append(record[0])
name.append(record[1])
price.append(record[2])
The rest of your code should work fine, except for one correction I would suggest; instead of using C-style formatting, prefer f-strings (Python >= 3.6) or format method formatting:
output = f'<tr><td>{name[0]}</td><td>{price[0]}</td>' # Python >= 3.6
output = '<tr><td>{}</td><td>{}</td>'.format(name[0], price[0]) # others

Faster or better way than looping to find data?

I have an array of object of class Person like the below, with thisRate first set to None:
class Person(object):
def __init__(self, id, name):
self.id = id
self.name = name
self.thisRate= None
I loaded around 21K Person objects into an array, name not sorted.
Then I loaded another array from data in a file which has data for thisRate, about 13K of them, name is not sorted as well:
person_data = []
# read from file
row['name'] = 'Peter'
row['thisRate'] = '0.12334'
person_data.append(row)
Now with these 2 sets of arrays, when the name is matched between them, I will assign thisRate from person_data into Person.thisRate.
What I am doing is a loop is like this:
for person in persons:
data = None
try:
data = next(personData for personData in person_data
if personData['name'] == person.name)
except StopIteration:
print("No rate for this person: {}".format(person.name))
if data:
person.thisRate = float( data['thisRate'] )
This loop
data = next(personData for personData in person_data
if personData['name'] == person.name)
is running fine and uses 21 seconds on my machine with Python 2.7.13.
My question is, is there a faster or better way to achieve the same thing with the 2 arrays I have?
Yes. Make an dictionary from name to thisRate:
nd = {}
with open(<whatever>) as f:
reader = csv.DictReader(<whatever>):
for row in reader:
nd[row['name']] = row['thisRate']
Now, use this dictionary to do a single pass over your Person list:
for person in persons:
thisRate = nd.get(person.name, None)
person.thisRate = thisRate
if thisRate is None:
print("No rate for this person: {}".format(person.name))
Dictionaries have a .get method which allows you to provide a default value in case the key is not in the dict. I used None (which is actually what is the default default value) but you can use whatever you want.
This is a linear-time solution. Your solution was quadratic time, because you are essentially doing:
for person in persons:
for data in person_data:
if data['name'] == person.name:
person.thisRate = data['thisRate']
break
else:
print("No rate for this person: {}".format(person.name))
Just in a fashion that obscures this fundamentally nested for-loop inside of a generator expression (not really a good use-case for a generator expression, you should have just used a for-loop to begin with, then you don't have to deal with try-catch a StopIteration

reading file into a dictionary

I was wondering if there is a way that i can read delimitered text into a dictionary. I have been able to get it into lists no problem here is the code:
def _demo_fileopenbox():
msg = "Pick A File!"
msg2 = "Select a country to learn more about!"
title = "Open files"
default="*.py"
f = fileopenbox(msg,title,default=default)
writeln("You chose to open file: %s" % f)
c = []
a = []
p = []
with open(f,'r') as handle:
reader = csv.reader(handle, delimiter = '\t')
for row in reader:
c = c + [row[0]]
a = a + [row[1]]
p = p + [row[2]]
while 1:
reply = choicebox(msg=msg2, choices= c )
writeln( reply + ";\tArea: " + a[(c.index(reply))] + " square miles \tPopulation: " + p[(c.index(reply))] )
that code makes it 3 lists because each line of text is a country name, their area, and their population. I had it that way so if i choose a country it will give me the corrosponding information on pop and area. Some people say a dictionary is a better approach, but first of all i dont think that i can put three things into one spot int the dictionary. I need the Country name to be the key and then the the population and area the info for that key. 2 dictionaries could probably work? but i just dont know how to get from file to dictionary, any help plz?
You could use two dictionaries, but you could also use a 2-tuple like this:
countries = {}
# ... other code as before
for row in reader:
countries[row[0]] = (row[1], row[2])
Then you can iterate through it all like this:
for country, (area, population) in countries.iteritems():
# ... Do stuff with country, area and population
... or you can access data on a specific country like this:
area, population = countries["USA"]
Finally, if you're planning to add more information in the future you might instead want to use a class as a more elegant way to hold the information - this makes it easier to write code which doesn't break when you add new stuff. You'd have a class something like this:
class Country(object):
def __init__(self, name, area, population):
self.name = name
self.area = area
self.population = population
And then your reading code would look something like this:
for row in reader:
countries[row[0]] = Country(row[0], row[1], row[2])
Or if you have the constructor take the entire row rather than individual items you might find it easier to extend the format later, but you're also coupling the class more closely to the representation in the file. It just depends on how you think you might extend things later.
Then you can look things up like this:
country = countries["USA"]
print "Area is: %s" % (country.area,)
This has the advantage that you can add new methods to do more clever stuff in the future. For example, a method which returns the population density:
class Country(object):
# ...
def get_density(self):
return self.population / self.area
In general I would recommend classes over something like nested dictionaries once you get beyond something where you're storing more than a couple of items. They make your code easier to read and easier to extend later.
As with most programming issues, however, other approaches will work - it's a case of choosing the method that works best for you.
Something like this should work:
from collections import defaultdict
myDict = {}
for row in reader:
country, area, population = row
myDict[country] = {'area': area, 'population': population}
Note that you'll have to add some error checking so that your code doesn't break if there are greater or less than three delimited items in each row.
You can access the values as follows:
>>> myDict['Mordor']['area']
175000
>>> myDict['Mordor']['population']
3000000
data = []
with open(f,'r') as handle:
reader = csv.reader(handle, delimiter = '\t')
for row in reader:
(country, area, population) = row
data.append({'country': country, 'area': area, 'population': population})
Data would then be a list of dictionaries.
But I'm not sure that's really a better approach, because it would use more memory. Another option is just a list of lists:
data = list(csv.reader(open(f), delimiter='\t'))
print data
# [['USA', 'big', '300 million'], ...]
the value of the dictionary can be a tuple of the population and area info. So when you read in the file you can do something such as
countries_dict = {}
for row in reader:
countries_dict[row[0]] = (row[1],row[2])

Categories

Resources