5

Right now I am learning Python and struggling with a few concepts of OOP, one of that being how difficult it is (to me) to dynamically initialize class instances and assign them to a dynamically generated variable name and why I am reading that I shouldn't do that in the first place.

In most threads with a similar direction, the answer seems to be that it is un-Pythonic to do that.

For example generating variable names on fly in python

Could someone please elaborate?

Take the typical OOP learning case:

LOE = ["graham", "eric", "terry_G", "terry_J", "john", "carol"]
class Employee():
    def __init__(self, name, job="comedian"):
        self.name = name
        self.job = job

Why is it better to do this:

employees = []
for name in LOE:
    emp = Employee(name)
    employees.append(emp)

and then

for emp in employees:
    if emp.name == "eric":
        print(emp.job)

instead of this

for name in LOE:
    globals()[name] = Employee(name)

and

print(eric.job)

Thanks!

Robowraith
  • 53
  • 4
  • 2
    p.s.: In writing this question, I think that I may have understood the reasoning behind this: My problem is that at the start of the program, I don't know all the names of the employees and thus cannot statically assign variable names for all the employees, but I seem to have assumed, that, when working with the employee-objects later in the program I somehow will know all the assigned variable names, when in reality i cannot. Am I on the right track? – Robowraith May 29 '18 at 12:05
  • A fair comparison would be your approach vs. using a dictionary, not vs. using a list, and also using comprehensions. Your second code sample would become `employees = {name: Employee(name) for name in loe}`, and the third sample would become `print(employees['eric'].job)`. – mkrieger1 May 29 '18 at 12:08
  • 2
    This question, in my opinion, deserves a cross-language canonical answer. It's not specific to Python, but is asked multiple times a week, or day. – jpp May 29 '18 at 12:09
  • Though many times, the question is phrased as "I need 10 variables, `student_0` through `student_9`, how do I do that?". Even though these names *are* predictable, the answer is still the same - your program will be much more versatile and maintainable if you use a list variable `student`, and iterate over it to get all students, or index into it by integer list index. – PaulMcG May 29 '18 at 12:17
  • @Paul The answer to that is often very simple and pragmatic: what's the difference between `student_0` and `students[0]`, except for the fact that the latter is easy with a variable for the index, and the former is somewhere between unnecessarily hard and impossible, depending on the language. – deceze May 29 '18 at 12:19

2 Answers2

6

If you dynamically generate variable names, you don't know what names exist, and you can't use them in code.

globals()[some_unknown_name] = Foo()

Well, now what? You can't safely do this:

eric.bar()

Because you don't know whether eric exists. You'll end up having to test for eric's existence using dictionaries/lists anyway:

if 'eric' in globals(): ...

So just store your objects in a dictionary or list to begin with:

people = {}
people['eric'] = Foo()

This way you can also safely iterate one data structure to access all your grouped objects without needing to sort them from other global variables.

deceze
  • 510,633
  • 85
  • 743
  • 889
1

globals() gives you a dict which you can put names into. But you can equally make your own dict and put the names there.

So it comes down to the idea of "namespaces," that is the concept of isolating similar things into separate data structures.

You should do this:

employees = {}
employees['alice'] = ...
employees['bob'] = ...
employees['chuck'] = ...

Now if you have another part of your program where you describe parts of a drill, you can do this:

drill['chuck'] = ...

And you won't have a name collision with Chuck the person. If everything were global, you would have a problem. Chuck could even lose his job.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436