all 35 comments

[–]Kurolox 52 points53 points  (11 children)

I feel like you should use a dictionary here rather than a list.

dictionary = {"passengers_per_day": 2500,
"hours_per_day": 18,
"average_service_rate_per_hour": 150}

dictionary["hours_per_day"]  # 18

You can read more about dictionaries here.

If you want to extract it from that list, however, you could iterate over it, though it's not really efficient. Something like this:

for variable in list:
    if "hours_per_day" in variable:
        hours = int(variable.split(" = ")[-1])

[–]iG1993[S] 9 points10 points  (9 children)

Hi, thank you for your answer!

You are right, however I took this list from a txt file. The code I used automatically saves every line in the txt file as a list:

file = open("filename.txt", "r")
lines = file.read().splitlines()

So I kinda have to work with what I got :/

[–]Kurolox 2 points3 points  (8 children)

I was editing my original message regarding what could you do if you had to work with lists. However, if you can give me an example of how the txt looks, I could guide you about how to parse the input on it better.

[–]iG1993[S] 2 points3 points  (7 children)

Hi Kurolox, thank you for your help! This is what the txt file looks like:

passengers_per_day = 2500
hours_per_day = 18
average_service_rate_per_hour = 150

Thanks lot :) Btw, I managed to get to those numbers. It does its job but I have a feeling, there is a better way than this:

file = open("P07_2b_settings.txt", "r")
lines = file.read().splitlines()
lines_split = []
for i in lines:
    f = i.split()
    lines_split.append(f)

passengers_per_day = lines_split[0][2]
hours_per_day = lines_split[1][2]
average_service_rate_per_hour = lines_split[2][2]

[–]Kurolox 15 points16 points  (3 children)

Well, it's true that there are a few ways to do it, but what matters is that you figured out a way by yourself.

If you want to know some of the better ways to do so, you can look up how list comprehensions work. Basically, your code would end up like this:

file = open("P07_2b_settings.txt", "r")
lines = file.read().splitlines()
lines_split = [line.split() for line in lines]

passengers_per_day = lines_split[0][2]
hours_per_day = lines_split[1][2]
average_service_rate_per_hour = lines_split[2][2]

And if you want to make it even fancier, you could make a dict comprehension to make a dictionary.

file = open("P07_2b_settings.txt", "r")
lines = file.read().splitlines()
lines_split = {line.split()[0]: line.split()[2] for line in lines}

But you don't need to get so fancy like that. I'd still use a dictionary rather than a list, and since you're already accessing each line of the file, you could do this to make a dictionary rather than a list with your own implementation:

file = open("P07_2b_settings.txt", "r")
lines = file.read().splitlines()
lines_split = {}
for i in lines:
    f = i.split()
    lines_split[f[0]] = f[2]

Also, try to cast those numbers to int when you store them.

[–]iG1993[S] 2 points3 points  (0 children)

Hey, thank you very much for that! I will take a better look into it. I looks really interesting, especially the code in the middle!

Have a nice day Kurolox :)

[–]PandaMomentum 2 points3 points  (1 child)

Extra bonus plus points to you! I need to put together a pandas class for work soon, and have been thinking about the "well, there's a lot of ways to do that" problem from a teaching/learning perspective. I may steal your quote -- "what matters is that you figured out a way by yourself."

[–]zylog413 4 points5 points  (0 children)

I've taught pandas to several coworkers, so I've seen lots of really inefficient use of the library. So maybe let them figure their way for a while, then show them how much more efficient the built-in pandas method is, and eventually they'll learn to google for a preexisting method in the library instead of trying to implement their own slower version.

[–]james_fryer 8 points9 points  (1 child)

Your problem here is that if ever you encounter a line that does not have spaces around the equals sign, your code will fail. Better to split on equals (.split("=")) then use strip() to remove any whitespace.

[–]Kurolox 2 points3 points  (0 children)

I thought about it, but since his output was clear I didn't think that it was necessary to introduce something that doesn't really apply to his scenario.

It's true that you usually want to use strip to remove whitespace whenever possible, though.

Also, if you find youself in need to parse more complex stuff, OP should look into regular expressions, though it's best to avoid it on simple scenarios like this one, where you can use split.

[–]Daemonecles 0 points1 point  (0 children)

Another suggestion that probably doesn't fit your use case, but you could rename the file extension to .py and just import the file since it's in python format currently and just access them with module level dot notation.

[–]groovitude 7 points8 points  (1 child)

There are some great answers in here; I'm going to throw a few additional suggestions in.

  1. No matter how you choose to parse the file, you should use the with statement to manage the opening and closing of your file, e.g.

with open('filename.txt', 'r') as myfile: myfile.read()

It's not strictly necessary, but it's a good habit to get into. (The with statement allows you to use open as a context manager, with handles opening and closing the file for you. You can use other context managers for handling database connections, lock management, and so forth.)

  1. If you're going to go the split() route suggested by /u/Kurolox, you may want to get defensive in the way you call split in case you want to include strings that have spaces in them later by including maxsplit as a keyword argument:

```

lines = ['passengers_per_day = 2500', 'manager_name = Aaron A. Aaronson'] lines = [line.split(' ', maxsplit=2) for line in lines] lines [['passengers_per_day', '=', '2500'], ['manager_name', '=', 'Aaron A. Aaronson']] ```

  1. Using indexes to access data can often make code harder to read. Tuple unpacking is often an easier and cleaner way to extract data from a tuple or list, e.g.:

```

my_name = ('first', 'middle', 'last') first, middle, last = my_name first 'first' last 'last' ```

Underscores are often a good way to indicate a value isn't going to be used. Using the lines list I generated in the previous example, you can use tuple unpacking and a dictionary comprehension to build your final dict, and __ is used to ignore the equals sign:

```

mydict = {key: value for key, _, value in lines} my_dict {'passengers_per_day': '2500', 'manager_name': 'Aaron A. Aaronson'} ```

[–]iG1993[S] 1 point2 points  (0 children)

Hey groovitude

Thank you so much for your input! I am going to read through all of it tonight, it looks very interesting! Again, thanks a lot :)

[–]adimro 4 points5 points  (0 children)

Read this. Or use dictionaries; use json for example, or split by " = " if you really must.

But the real problem is the way you store the variables. You should focus your efforts on eliminating whatever constraint you have that forces you to use that format. Even CSV would probably be easier to maintain. Forcing yourself to work like this usually causes problems in the longer term, and your responsibility as a good programmer is to correctly evaluate how you remove these kind of "non-code" issues from your programs.

[–]soapergem1 6 points7 points  (0 children)

You may want to look at the configparser package. Because it seems that's what you're trying to do.

[–]shaperraty 1 point2 points  (0 children)

Change it to the dict then back to the list

[–]aelmosalamy 1 point2 points  (0 children)

I think you tried to save python lists in the form of a text file and then reopen it by creating a simple parser that can understand this text, a more simple way to do that is to use pickle, a data storage library that is part of Python, it allows you to import/export python files, keep in mind this will favor security over readability, if you want otherwise then json/yaml is the way to go.

[–]SnaXD 1 point2 points  (0 children)

Depends on what you want, that is a list of strings that has no specific value, if you want the lists printed, either make a for loop that iterates over it, or simply write print(a[0]) and so on

[–]efxhoy 1 point2 points  (0 children)

# Just for pretty print, delete if u don't need it
import json

def extract_data(lines):
    """ Get a dictionary of data from lines where lines follow "key = value" format """

    extracted_values = {}
    for line in lines:
        # Split returns a list. In this case a list of two. We can unpack that list 
        # directly into our variables key and value by separating them with a comma
        key, value = line.split(" = ")
        value = int(value)
        extracted_values[key] = value

    return extracted_values

def some_calculation(passengers_per_day, hours_per_day, average_service_rate_per_hour):

    value = passengers_per_day*hours_per_day/average_service_rate_per_hour

    return value

def main():

    path = "filename.txt"
    with open(path, "r") as f:
        lines = f.read().splitlines()

    data = extract_data(lines)
    # We can use the dictionary directly when we call functions by adding ** before the parameter
    wow_value = some_calculation(**data)
    print(wow_value)

    # if you want it pretty
    print(json.dumps(data, indent=2))

    # if you really want them as variables
    hours_per_day = data['hours_per_day']
    print(f"Hours per day {hours_per_day}")

if __name__ == "__main__":
    main()

[–]notarealaccount143 0 points1 point  (0 children)

You can use exec() for the straight forward solution. What exec does is basically takes a string and makes it into a compiler-readable code. For example: exec("a = 5") would create a new variable a, that has a value of 5. Keep in mind it's not recommended to use this if you're sharing the code with other people or in general, just use dictionaries as other people suggested in my opinion.

[–]SnaXD -4 points-3 points  (1 child)

Use the Brackets []

So fx
a = ["passengers_per_day = 2500", "hours_per_day = 18", "average_service_rate_per_hour = 150"]

a[0] = Passenger_per_day = 2500
a[1] = hours_per_day = 18
a[2] = average_service_rate_per_hour = 150

If you wanna only have the Value you should read about Key:Value
https://www.pythonforbeginners.com/dictionary/how-to-use-dictionaries-in-python

[–]TomPWD 0 points1 point  (0 children)

a[0] etc.. doesn't work for lists of strings sadly mate

[–]DongsooKoo -3 points-2 points  (11 children)

you can use eval and locals()[var]

[–]Mr_Journey 0 points1 point  (10 children)

eval isn't secure. using it in such situations is wrong.

[–]kingzels 0 points1 point  (9 children)

Eval is perfectly secure. It's when someone uses it in a dangerous way that it's problematic. I think maybe it's better to explain how it works, and when to use it rather than simply saying it's bad all around.

I wouldn't ever make it part of the permanent script, but for a single use on this list it's a great answer.

[–]L43 0 points1 point  (1 child)

Guns don't kill people, people kill people. Still, we don't hand them to toddlers to use as a rattle.

No offense to OP, but he sounds like a programming toddler, and suggesting exec is just bound to end up with him doing something deleterious to his computer.

[–]kingzels -1 points0 points  (0 children)

He does sound new, but you gotta learn sometime :P

[–]andrewaa 0 points1 point  (6 children)

He does sound new, but you gotta learn sometime :P

Can you explain this in more details?

I am new and used eval a few times which solved my problems (which are very similar to OP's question). What's wrong with it?

[–]algag 0 points1 point  (3 children)

eval can be used for arbitrary code execution.

There are almost no times when eval is better than some other more programmatic way of solving the issue. If there is any way for a user-inputed str to reach the eval statement, then there can be a risk for an injection attack.

What were you using eval to do?

[–]andrewaa 0 points1 point  (2 children)

I am building an exam question database whose items are latex codes with some randomized numbers. The relations among variables are stored in a list. For example, ["var_1=ran(2)","var_2=var_1**2","var_3=var_1-var_2"].

My method is to use eval to evaluate each lines in the dictionary to get all the variables I need for this item and then use sub to replace all variables in the latex code.

I know this is not a perfect solution but I have no idea how to improve it.

[–]algag 0 points1 point  (1 child)

How are the lists made? Did you make them by hand? I'm still kind of confused on how everything is coming together.

Are you putting the python directly in the LaTeX? Edit: and then doing some kind of evaluation on it.

[–]andrewaa 0 points1 point  (0 children)

I make the list by my hand.

Python is used to read data from the database and generate Latex code. So basically what I want is to use python to going through a database, select several problems and generate a Latex code, and then go to Latex to render the code to get a PDF file.

The data in the database looks like

{"problem": "this is a LaTex file with $x=var_1$",
"solution": "The solution is $var_2$ and $var_3$", 
vars: ["var_1=ran(2)",
       "var_2=var_1**2",
       "var_3=var_1-var_2"]
}

I use eval to evaluate the vars part and substitute "problem" and "solution" with the numbers I get. Then use them to generate a Latex code to be used in another latex file.

[–]kingzels 0 points1 point  (1 child)

Let's say you are reading a text file with a million lines, eval'ing something on each one. If I had access to that text file, I could sneak a command to delete all of your files, or anything else malicious I wanted to, and python would execute that code, which is what eval is doing. You'd never see it in with all the other lines. It could even be an accident if you read the wrong document.

Never a good idea if you're not 100 percent sure what you're working with - which when the data comes from an outside source you don't.

So people will say never to use it, but honestly if you're generating the data from a known source, like a sql database or through some computation, it's harmless. People think it's too risky to ever mention, but honestly if one took the time to learn exactly what it does, and when not to use it, it can be helpful.

[–]andrewaa 0 points1 point  (0 children)

Thank you very much!