0

Preface: I've spent many HOURS trying to read similar posts like this one and try lots of different things, but for the life of me, I can't understand what I'm doing wrong, or why this doesn't work. Can someone please give me an ELI5 answer as to either what I'm doing wrong, or why this doesn't work the way I'm expecting?

class User:

    def __init__(self, first, last, age):
        self.firstname = first
        self.lastname  = last
        self.age       = age


dict = {
    'User1': {'id': 'id001', 'firstname': 'User', 'lastname': 'One', 'age': 11},
    'User2': {'id': 'id002', 'firstname': 'User', 'lastname': 'Two', 'age': 22},
    'User3': {'id': 'id003', 'firstname': 'User', 'lastname': 'Three', 'age': 33}
}


for user in dict:
    u    = dict[user]
    user = User(u['firstname'], u['lastname'], u['age'])


try:
    print(User1.lastname)
except Exception as e:
    print(e)
finally:
    print(user.lastname)

Which returns:

name 'User1' is not defined
Three

Clearly user is getting instantiated three times with the data from dict, with User3's data being the last of it.

If I do a:

for user in dict:
    print(user)

it prints out:

User1
User2
User3

So my expectation would be that the for loop would run, and then I'd be able to call User1.firstname, User2.lastname, or User3.age, and it'd give me the same data that's in dict (User, Two, 33, respectively), but that's clearly not the case.

Help?

  • 3
    Don't use dict as an object name. That is a built in module. Change it to something like dict1. –  Jun 01 '18 at 19:54
  • 2
    You do not have a variable named `User1`. You have a key in a dictionary with that name, and can access that user's variables with `print(dict['User1']['lastname']`. There is a not a good way in python to convert a dictionary key directly to a variable/class name without `eval` (don't use `eval`), meaning there is not a good way to get a variable called `User1` unless you manually say `User1 = User(...)`. If you want your users in classes, you can make a dictionary or list of users as g.d.d.c. has done. – jeremysprofile Jun 01 '18 at 19:58
  • @Rthomas529 yeah, poor use in my example. thanks. – thisAaronMdev Jun 01 '18 at 20:14
  • 1
    @jeremysprofile gotcha, and that's what I was trying to achieve - having the key be the object's name, which clearly doesn't work and/or isn't easy to do. Still learning... – thisAaronMdev Jun 01 '18 at 20:15
  • @thisAaronMdev, it's not necessarily that it doesn't work, it's that it's the wrong way to think about it. You want your program to be able to handle all users, not just `User3`. If you want to do something special with a certain user, you can add a `self.userid` field to your `User` class, and then when you're looping through your list with of `for user in users` you can check `if user.userid == User3`. – jeremysprofile Jun 01 '18 at 20:24
  • @jeremysprofile I guess that's something I'll need to learn to appreciate then. I'm still new at this, so in my mind, I was trying to create all the objects in bulk, so that I could work directly with them, with a naming convention that made sense to me. Having to add a little logic to my program via the if statement against a list seems like an unnecessary (but apparently necessary) step...to me. But again, still learning so I'm sure there's tried and true reasoning behind this approach. – thisAaronMdev Jun 01 '18 at 20:33

2 Answers2

2

You're not storing the instances you create.

for user in dict:
    u    = dict[user]
    user = User(u['firstname'], u['lastname'], u['age'])

^^ Create a bunch of User's, but assign each one to the same name, user. That means that users 1 and 2 are lost as as you continue iterating through your loop.

If you want to have access to each of these users, you need to store the reference to the created instance:

users = []
for user in dict:
    u    = dict[user]
    user = User(u['firstname'], u['lastname'], u['age'])
    users.append(user)

Then, to print results:

for user in users:
    print(user)

*PS: When you write for user in dict you are iterating over the keys of dict, which is why you get User1, User2, User3.

**PPS: You should absolutely not use dict as a name, that shadows the builtin dict() function, and will cause you problems later.

g.d.d.c
  • 46,865
  • 9
  • 101
  • 111
  • First, thank you! That makes sense, though I guess I was expecting the instantiation within the for loop to be stored in the variable's (user) _value_ (User1, User2, etc), rather than to the variable name itself. What I was trying to do was instantiate the user data to an object/variable named with the key name, so that I could then work with User#.attribute, but it's looking like once it's in the list, I can only reference them now as `users[index]`? – thisAaronMdev Jun 01 '18 at 20:11
  • Also, once the objects are stored in the list, is the only way to see all the objects' info with `[print(vars(i)) for i in users]` or is there a better way? – thisAaronMdev Jun 01 '18 at 20:20
  • @thisAaronMdev, you can add a specific method to your class: `def __str__():` and have it return whatever you want it to print, such as print all the fields in your class, or just say "this is a user. goodluck" if you want. Then when you call print(users[0]) it will print exactly what you want it to print. – jeremysprofile Jun 01 '18 at 20:36
1

For completeness - it is possible to create global variables in python thanks to globals()[key] = value - but everyone with a bit of experience would never advise you to do that - if not for very specific and well-thought-out reasons there is no justification for doing so. Nonetheless here you can see how it is achieved given your example:

https://repl.it/repls/InternationalShamelessBug

wiesion
  • 2,349
  • 12
  • 21
  • So I just got around to playing with your answer, and your answer gave me EXACTLY what I was looking for. BUUUUUUUUT you said that this is not the way to go about it. Why is that? – thisAaronMdev Jun 01 '18 at 21:14
  • Shortsaid, that makes everything unpredictable and potentially unstable - For more read https://stackoverflow.com/a/19158418/3820185 and the linked resources, so use this only if you are aware of the implications. – wiesion Jun 01 '18 at 21:38
  • So I read through a lot of those, and it sounds like, in my example, if I instantiated `User1` from my User class, and it's available globally, further down the line, another function/method/whatever, could bork it up, and because it's available globally, it could potentially be difficult to track down where that happened? How is that different than storing objects within a list or dictionary that's available globally? Or should the list and/or dict not be accessible globally either? – thisAaronMdev Jun 01 '18 at 23:06
  • 1
    It has so many downsides it's hard to start somewhere, but for instance: an IDE will not know where the global was declared so before compiling a lookup/introspection for those globals is not possible. In general you want to work with imports whenever you want to re-use something, for instance: https://repl.it/repls/LovelyPersonalOpengroup – wiesion Jun 01 '18 at 23:50