0

I am trying to use elements in a list for class instances. I'm not sure if I'm using the right terminology since I'm new to python, but here's an example in code:

list = ['one', 'two', 'three']

class numbers:
    def __init__(self, letter):
        self.letter = letter

one = numbers('a')

I am trying to use the list to do something like this:

print(list[0].letter)

This doesn't seem to work because list[0] is a string. Is there a work around? Thanks in advance.

immeeh
  • 59
  • 5
  • its getattr , also answered on below link: https://stackoverflow.com/questions/4821104/python-dynamic-instantiation-from-string-name-of-a-class-in-dynamically-imported – Rehan Azher Apr 05 '18 at 07:04
  • you didn't set `list[0]` to the created object `one` so it's still a str, which doesn't have any attributes. – Ami Hollander Apr 05 '18 at 07:06
  • What do you expect to see in `list[0].letter`? – SomethingSomething Apr 05 '18 at 07:09
  • @RehanAzher This might be what I'm looking for, I'll have to study it thanks – immeeh Apr 05 '18 at 07:12
  • @AmiHollander How would I go about using the list to create the object? – immeeh Apr 05 '18 at 07:13
  • @SomethingSomething I was expecting print(list[0].letter) to generate: a – immeeh Apr 05 '18 at 07:13
  • 1
    @RehanAzher How is `getattr` going to help here? There's no object with an attribute named `'one'` anywhere in his code. – abarnert Apr 05 '18 at 07:14
  • 1
    `[numbers("a"), numbers("b")]' – Ami Hollander Apr 05 '18 at 07:14
  • @abarnert I think the code above is a sample code, but reading the problem explanation can give us more details. He has a list of Class names that he want to instantiate. – Rehan Azher Apr 05 '18 at 07:15
  • You really, really don't want to do this, but just for fun, try `lst2 = [globals()[name] for name in lst]` and see what you get… – abarnert Apr 05 '18 at 07:16
  • 1
    I have a gut feeling that what you really want is an [enum](https://docs.python.org/3/library/enum.html). Ignore me if I'm wrong. – MariusSiuram Apr 05 '18 at 07:17
  • @RehanAzher And that list of class names isn't going to be attributes of any object to `getattr` either. (Well, I suppose you could always do something ridiculous like `getattr(sys.modules[__name__], name)` to get at `globals()` without calling it `globals()`, but why?) – abarnert Apr 05 '18 at 07:17
  • And I have linked the similar question, which answers this in quite detailed explanations. – Rehan Azher Apr 05 '18 at 07:19
  • @RehanAzher If you don't understand the difference between a module that you imported—which is an object, with attributes—and the global namespace—which is not an object with attributes, but a dict—I don't know how to explain it to you. – abarnert Apr 05 '18 at 07:28
  • @abarnert yes, it may be that i understood requirement wrong cause i was not sure. That is why i did not answered the question just pointed to a possible solution. Cheers. – Rehan Azher Apr 05 '18 at 07:31

3 Answers3

2

In your list you create 3 strings:

list = ['one', 'two', 'three']

Then you create an object of the type numbers:

one = numbers('a')

This new object is not part of your list. So when you access list[0] you get back the string 'one'

You need to create the objects before adding them to the list, then create the list.

one = numbers('a')
two = numbers('b')
list_ = [one, two]
list_[0].letter
'a'

The part you were confusing is the string 'one' and the object reference for one variable are not the same thing. The latter is an object, and it has the letter attribute. The former is just a string.

Chen A.
  • 10,140
  • 3
  • 42
  • 61
  • No, rebinding `one` will _not_ change `list_[0]`. – abarnert Apr 05 '18 at 07:13
  • @abarnert you are right. I got confused myself. removed it from my answer and thank you for your comment. – Chen A. Apr 05 '18 at 07:14
  • You want to assign them values after they are in the list? – Chen A. Apr 05 '18 at 07:16
  • I'm trying to avoid assigning anything to the elements in my list as they are being used elsewhere in my actual code. – immeeh Apr 05 '18 at 07:16
  • Well you can't create a list with non-existent objects, so you have to create them first. If you need to keep to change them and don't want to do it on the actual elements create a copy of these objects. – Chen A. Apr 05 '18 at 07:19
  • Yes it may be i understood the requirement wrong , that is why i did not posted it asnaswer. Cheers – Rehan Azher Apr 05 '18 at 07:30
1

You really don't want to do what you're asking for. Ned Batchelder has a great explanation of why. I have a not as great one.

But if you've read those, and you're convinced you actually do have one of those rare cases where it really is the right thing to do, the way to look up a global variable by name is to use the globals function to get the global namespace as a dictionary, then look it up there:

>>> lst = ['one', 'two', 'three']
>>> one = numbers('a')
>>> globals()[lst[0]].letter
'a'

Notice that you could do this a lot more easily, and a lot more readably, if you just created and used a dictionary in the first place:

>>> dct = {'one': numbers('a'), 'two': numbers('b'), 'three': numbers('c')}
>>> dct['one'].letter
'a'
>>> dct[lst[0]].letter
'a'

And, besides being more readable and more explicit, it's also more powerful, because you can build any dict you want, not just whatever globals has. You can even build the dict and the numbers together in one fell swoop:

>>> numbers = (numbers(letter) for letter in string.ascii_lowercase)
>>> dct = dict(zip(lst, numbers))
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • Nice workaround using globals, but it really looks wrong to access an object this way. – Chen A. Apr 05 '18 at 07:38
  • @Vinny Yes, and I'm pretty sure it's _deliberate_ that this looks wrong. Python is forcing you to highlight to the reader that you're doing something weird by making it look weird. (Compare to Tcl, where this would be perfectly idiomatic, and… that's part of the reason we're using Python right now instead of Tcl.) – abarnert Apr 05 '18 at 07:43
  • Dictionaries will definitely work, thanks @abarnert! – immeeh Apr 05 '18 at 07:47
-1

Like this?

one = numbers('a')
two = numbers('b')
three = numbers('c')
list = [one, two, three]

And the result is below.

>>> print(list[0].letter)
a
yura
  • 26
  • 4